Skip to content

Commit 4c8b278

Browse files
Create toBech32 and fromBech32 in @cosmjs/encoding (#1058)
* Create toBech32 and fromBech32 * Add changes to CHANGELOG.md * Fix linting * Update packages/encoding/src/bech32.ts Co-authored-by: Simon Warta <[email protected]> * Update CHANGELOG.md Co-authored-by: Simon Warta <[email protected]> * Update packages/encoding/src/bech32.ts Co-authored-by: Simon Warta <[email protected]> * Update bech32.ts Co-authored-by: Simon Warta <[email protected]>
1 parent 9b80dff commit 4c8b278

20 files changed

+84
-61
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,16 @@ and this project adheres to
3737
identifier which should not be the case anyways.
3838
- @cosmjs/stargate: Added support for slashing queries ([#927])
3939
- @cosmjs/ledger-amino: Renamed `LaunchpadLedger` to `LedgerConnector` ([#955])
40+
- @cosmjs/encoding: Created `toBech32()` and `fromBech32()`. Class Bech32 is now
41+
deprecated and should not longer be used. ([#1053])
4042

4143
[#927]: https://github.com/cosmos/cosmjs/issues/927
4244
[#955]: https://github.com/cosmos/cosmjs/issues/955
4345
[#989]: https://github.com/cosmos/cosmjs/issues/989
4446
[#994]: https://github.com/cosmos/cosmjs/issues/994
4547
[#1011]: https://github.com/cosmos/cosmjs/issues/1011
4648
[#1026]: https://github.com/cosmos/cosmjs/issues/1026
49+
[#1053]: https://github.com/cosmos/cosmjs/issues/1053
4750

4851
### Removed
4952

packages/amino/src/addresses.spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Bech32, fromHex, toBase64 } from "@cosmjs/encoding";
1+
import { fromBech32, fromHex, toBase64 } from "@cosmjs/encoding";
22

33
import { pubkeyToAddress, pubkeyToRawAddress } from "./addresses";
44
import { decodeBech32Pubkey } from "./encoding";
@@ -12,7 +12,7 @@ describe("addresses", () => {
1212
value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP",
1313
};
1414
expect(pubkeyToRawAddress(pubkey)).toEqual(
15-
Bech32.decode("cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r").data,
15+
fromBech32("cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r").data,
1616
);
1717
});
1818

@@ -22,7 +22,7 @@ describe("addresses", () => {
2222
value: toBase64(fromHex("12ee6f581fe55673a1e9e1382a0829e32075a0aa4763c968bc526e1852e78c95")),
2323
};
2424
expect(pubkeyToRawAddress(pubkey)).toEqual(
25-
Bech32.decode("cosmos1pfq05em6sfkls66ut4m2257p7qwlk448h8mysz").data,
25+
fromBech32("cosmos1pfq05em6sfkls66ut4m2257p7qwlk448h8mysz").data,
2626
);
2727
});
2828

packages/amino/src/addresses.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// See https://github.com/tendermint/tendermint/blob/f2ada0a604b4c0763bda2f64fac53d506d3beca7/docs/spec/blockchain/encoding.md#public-key-cryptography
22

33
import { ripemd160, sha256 } from "@cosmjs/crypto";
4-
import { Bech32, fromBase64 } from "@cosmjs/encoding";
4+
import { fromBase64, toBech32 } from "@cosmjs/encoding";
55

66
import { encodeAminoPubkey } from "./encoding";
77
import { isEd25519Pubkey, isMultisigThresholdPubkey, isSecp256k1Pubkey, Pubkey } from "./pubkeys";
@@ -38,5 +38,5 @@ export function pubkeyToRawAddress(pubkey: Pubkey): Uint8Array {
3838
}
3939

4040
export function pubkeyToAddress(pubkey: Pubkey, prefix: string): string {
41-
return Bech32.encode(prefix, pubkeyToRawAddress(pubkey));
41+
return toBech32(prefix, pubkeyToRawAddress(pubkey));
4242
}

packages/amino/src/encoding.spec.ts

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Bech32, fromBase64, fromHex } from "@cosmjs/encoding";
1+
import { fromBase64, fromBech32, fromHex } from "@cosmjs/encoding";
22

33
import {
44
decodeAminoPubkey,
@@ -39,7 +39,7 @@ describe("encoding", () => {
3939

4040
describe("decodeAminoPubkey", () => {
4141
it("works for secp256k1", () => {
42-
const amino = Bech32.decode(
42+
const amino = fromBech32(
4343
"cosmospub1addwnpepqd8sgxq7aw348ydctp3n5ajufgxp395hksxjzc6565yfp56scupfqhlgyg5",
4444
).data;
4545
expect(decodeAminoPubkey(amino)).toEqual({
@@ -51,7 +51,7 @@ describe("encoding", () => {
5151
it("works for ed25519", () => {
5252
// Encoded from `corald tendermint show-validator`
5353
// Decoded from http://localhost:26657/validators
54-
const amino = Bech32.decode(
54+
const amino = fromBech32(
5555
"coralvalconspub1zcjduepqvxg72ccnl9r65fv0wn3amlk4sfzqfe2k36l073kjx2qyaf6sk23qw7j8wq",
5656
).data;
5757
expect(decodeAminoPubkey(amino)).toEqual({
@@ -65,7 +65,7 @@ describe("encoding", () => {
6565
});
6666

6767
it("works for multisig", () => {
68-
const pubkeyData = Bech32.decode(
68+
const pubkeyData = fromBech32(
6969
"cosmospub1addwnpepqd8sgxq7aw348ydctp3n5ajufgxp395hksxjzc6565yfp56scupfqhlgyg5",
7070
).data;
7171
const pubkey = {
@@ -156,7 +156,7 @@ describe("encoding", () => {
156156
type: "tendermint/PubKeySecp256k1",
157157
value: "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ",
158158
};
159-
const expected = Bech32.decode(
159+
const expected = fromBech32(
160160
"cosmospub1addwnpepqd8sgxq7aw348ydctp3n5ajufgxp395hksxjzc6565yfp56scupfqhlgyg5",
161161
).data;
162162
expect(encodeAminoPubkey(pubkey)).toEqual(expected);
@@ -169,7 +169,7 @@ describe("encoding", () => {
169169
type: "tendermint/PubKeyEd25519",
170170
value: "YZHlYxP5R6olj3Tj3f7VgkQE5VaOvv9G0jKATqdQsqI=",
171171
};
172-
const expected = Bech32.decode(
172+
const expected = fromBech32(
173173
"coralvalconspub1zcjduepqvxg72ccnl9r65fv0wn3amlk4sfzqfe2k36l073kjx2qyaf6sk23qw7j8wq",
174174
).data;
175175
expect(encodeAminoPubkey(pubkey)).toEqual(expected);
@@ -200,16 +200,16 @@ describe("encoding", () => {
200200
});
201201

202202
it("works for multisig", () => {
203-
const expected1 = Bech32.decode(testgroup1PubkeyBech32).data;
203+
const expected1 = fromBech32(testgroup1PubkeyBech32).data;
204204
expect(encodeAminoPubkey(testgroup1)).toEqual(expected1);
205205

206-
const expected2 = Bech32.decode(testgroup2PubkeyBech32).data;
206+
const expected2 = fromBech32(testgroup2PubkeyBech32).data;
207207
expect(encodeAminoPubkey(testgroup2)).toEqual(expected2);
208208

209-
const expected3 = Bech32.decode(testgroup3PubkeyBech32).data;
209+
const expected3 = fromBech32(testgroup3PubkeyBech32).data;
210210
expect(encodeAminoPubkey(testgroup3)).toEqual(expected3);
211211

212-
const expected4 = Bech32.decode(testgroup4PubkeyBech32).data;
212+
const expected4 = fromBech32(testgroup4PubkeyBech32).data;
213213
expect(encodeAminoPubkey(testgroup4)).toEqual(expected4);
214214
});
215215
});

packages/amino/src/encoding.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Bech32, fromBase64, fromHex, toBase64, toHex } from "@cosmjs/encoding";
1+
import { fromBase64, fromBech32, fromHex, toBase64, toBech32, toHex } from "@cosmjs/encoding";
22
import { Uint53 } from "@cosmjs/math";
33
import { arrayContentStartsWith } from "@cosmjs/utils";
44

@@ -77,7 +77,7 @@ export function decodeAminoPubkey(data: Uint8Array): Pubkey {
7777
* @param bechEncoded the bech32 encoded pubkey
7878
*/
7979
export function decodeBech32Pubkey(bechEncoded: string): Pubkey {
80-
const { data } = Bech32.decode(bechEncoded);
80+
const { data } = fromBech32(bechEncoded);
8181
return decodeAminoPubkey(data);
8282
}
8383

@@ -200,5 +200,5 @@ export function encodeAminoPubkey(pubkey: Pubkey): Uint8Array {
200200
* @param prefix the bech32 prefix (human readable part)
201201
*/
202202
export function encodeBech32Pubkey(pubkey: Pubkey, prefix: string): string {
203-
return Bech32.encode(prefix, encodeAminoPubkey(pubkey));
203+
return toBech32(prefix, encodeAminoPubkey(pubkey));
204204
}

packages/amino/src/secp256k1hdwallet.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
Slip10Curve,
1212
stringToPath,
1313
} from "@cosmjs/crypto";
14-
import { Bech32, fromBase64, fromUtf8, toBase64, toUtf8 } from "@cosmjs/encoding";
14+
import { fromBase64, fromUtf8, toBase64, toBech32, toUtf8 } from "@cosmjs/encoding";
1515
import { assert, isNonNullObject } from "@cosmjs/utils";
1616

1717
import { rawSecp256k1PubkeyToRawAddress } from "./addresses";
@@ -346,7 +346,7 @@ export class Secp256k1HdWallet implements OfflineAminoSigner {
346346
return Promise.all(
347347
this.accounts.map(async ({ hdPath, prefix }) => {
348348
const { privkey, pubkey } = await this.getKeyPair(hdPath);
349-
const address = Bech32.encode(prefix, rawSecp256k1PubkeyToRawAddress(pubkey));
349+
const address = toBech32(prefix, rawSecp256k1PubkeyToRawAddress(pubkey));
350350
return {
351351
algo: "secp256k1" as const,
352352
privkey: privkey,

packages/amino/src/secp256k1wallet.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Secp256k1, Sha256 } from "@cosmjs/crypto";
2-
import { Bech32 } from "@cosmjs/encoding";
2+
import { toBech32 } from "@cosmjs/encoding";
33

44
import { rawSecp256k1PubkeyToRawAddress } from "./addresses";
55
import { encodeSecp256k1Signature } from "./signature";
@@ -34,7 +34,7 @@ export class Secp256k1Wallet implements OfflineAminoSigner {
3434
}
3535

3636
private get address(): string {
37-
return Bech32.encode(this.prefix, rawSecp256k1PubkeyToRawAddress(this.pubkey));
37+
return toBech32(this.prefix, rawSecp256k1PubkeyToRawAddress(this.pubkey));
3838
}
3939

4040
public async getAccounts(): Promise<readonly AccountData[]> {

packages/amino/src/signdoc.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/* eslint-disable @typescript-eslint/naming-convention */
22
import { Random } from "@cosmjs/crypto";
3-
import { Bech32 } from "@cosmjs/encoding";
3+
import { toBech32 } from "@cosmjs/encoding";
44

55
import { AminoMsg, makeSignDoc, sortedJsonStringify } from "./signdoc";
66

77
function makeRandomAddress(): string {
8-
return Bech32.encode("cosmos", Random.getBytes(20));
8+
return toBech32("cosmos", Random.getBytes(20));
99
}
1010
const testAddress = "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6";
1111
const testValidatorAddress = "cosmosvaloper1yfkkk04ve8a0sugj4fe6q6zxuvmvza8r3arurr";

packages/cosmwasm-stargate/src/testutils.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable @typescript-eslint/naming-convention */
22
import { AminoSignResponse, Secp256k1HdWallet, Secp256k1HdWalletOptions, StdSignDoc } from "@cosmjs/amino";
33
import { Bip39, EnglishMnemonic, Random } from "@cosmjs/crypto";
4-
import { Bech32, fromBase64 } from "@cosmjs/encoding";
4+
import { fromBase64, toBech32 } from "@cosmjs/encoding";
55
import {
66
DirectSecp256k1HdWallet,
77
DirectSecp256k1HdWalletOptions,
@@ -64,7 +64,7 @@ export function getHackatom(): ContractUploadInstructions {
6464
}
6565

6666
export function makeRandomAddress(): string {
67-
return Bech32.encode("wasm", Random.getBytes(20));
67+
return toBech32("wasm", Random.getBytes(20));
6868
}
6969

7070
export const tendermintIdMatcher = /^[0-9A-F]{64}$/;

packages/encoding/README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ on invalid input.
1010
## Convert between bech32 and hex addresses
1111

1212
```
13-
>> Bech32.encode("tiov", fromHex("1234ABCD0000AA0000FFFF0000AA00001234ABCD"))
13+
>> toBech32("tiov", fromHex("1234ABCD0000AA0000FFFF0000AA00001234ABCD"))
1414
'tiov1zg62hngqqz4qqq8lluqqp2sqqqfrf27dzrrmea'
15-
>> toHex(Bech32.decode("tiov1zg62hngqqz4qqq8lluqqp2sqqqfrf27dzrrmea").data)
15+
>> toHex(fromBech32("tiov1zg62hngqqz4qqq8lluqqp2sqqqfrf27dzrrmea").data)
1616
'1234abcd0000aa0000ffff0000aa00001234abcd'
1717
```
1818

packages/encoding/src/bech32.spec.ts

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Bech32 } from "./bech32";
1+
import { fromBech32, toBech32 } from "./bech32";
22
import { fromHex } from "./hex";
33

44
describe("Bech32", () => {
@@ -8,30 +8,30 @@ describe("Bech32", () => {
88

99
describe("encode", () => {
1010
it("works", () => {
11-
expect(Bech32.encode("eth", ethAddressRaw)).toEqual("eth1n48g2mjh9ezz7zjtya37wtgg5r5emr0drkwlgw");
11+
expect(toBech32("eth", ethAddressRaw)).toEqual("eth1n48g2mjh9ezz7zjtya37wtgg5r5emr0drkwlgw");
1212
});
1313

1414
it("works for very short data", () => {
15-
expect(() => Bech32.encode("eth", new Uint8Array(1))).not.toThrow();
15+
expect(() => toBech32("eth", new Uint8Array(1))).not.toThrow();
1616
});
1717

1818
it("works for very long prefixes", () => {
19-
expect(() => Bech32.encode("p".repeat(70), new Uint8Array(20))).toThrowError(/exceeds length limit/i);
19+
expect(() => toBech32("p".repeat(70), new Uint8Array(20))).toThrowError(/exceeds length limit/i);
2020
});
2121

2222
// See https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#Bech32
2323
it("works if result is 90 characters", () => {
24-
const result = Bech32.encode("eth", new Uint8Array(50));
24+
const result = toBech32("eth", new Uint8Array(50));
2525
expect(result.length).toEqual(90);
2626
});
2727

2828
it("throws if result exceeds 90 characters", () => {
29-
expect(() => Bech32.encode("eth", new Uint8Array(51))).toThrowError(/exceeds length limit/i);
29+
expect(() => toBech32("eth", new Uint8Array(51))).toThrowError(/exceeds length limit/i);
3030
});
3131

3232
it("works if a limit parameter is provided", () => {
3333
const limit = 1024;
34-
const result = Bech32.encode("eth", new Uint8Array(51), limit);
34+
const result = toBech32("eth", new Uint8Array(51), limit);
3535
expect(result).toEqual(
3636
"eth1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqug55er",
3737
);
@@ -40,13 +40,13 @@ describe("Bech32", () => {
4040

4141
it("throws if result exceeds the provided limit parameter", () => {
4242
const limit = 10;
43-
expect(() => Bech32.encode("eth", ethAddressRaw, limit)).toThrowError(/exceeds length limit/i);
43+
expect(() => toBech32("eth", ethAddressRaw, limit)).toThrowError(/exceeds length limit/i);
4444
});
4545
});
4646

4747
describe("decode", () => {
4848
it("works", () => {
49-
expect(Bech32.decode("eth1n48g2mjh9ezz7zjtya37wtgg5r5emr0drkwlgw")).toEqual({
49+
expect(fromBech32("eth1n48g2mjh9ezz7zjtya37wtgg5r5emr0drkwlgw")).toEqual({
5050
prefix: "eth",
5151
data: ethAddressRaw,
5252
});
@@ -55,7 +55,7 @@ describe("Bech32", () => {
5555
it("works for addresses which exceed the specification limit of 90 characters", () => {
5656
// Example from https://github.com/cosmos/cosmos-sdk/pull/6237#issuecomment-658116534
5757
expect(() =>
58-
Bech32.decode(
58+
fromBech32(
5959
"cosmospub1ytql0csgqvfzd666axrjzqmn5q2ucztcyxw8hvlzen94ay05tegaerkug5pn3xn8wqdymt598ufzd666axrjzqsxllmwacap3f6xyc4x30jl8ecrcs2tze3zzgxkmthcsqxnqxhwwgfzd666axrjzqs2rlu3wz5gnslgpprszjr8r65n0d6y39q657th77eyvengtk3z0y6h2pnk",
6060
),
6161
).not.toThrow();
@@ -64,7 +64,7 @@ describe("Bech32", () => {
6464
it("throws for addresses which exceed the specification limit of 90 characters if a limit is specified", () => {
6565
// Example from https://github.com/cosmos/cosmos-sdk/pull/6237#issuecomment-658116534
6666
expect(() =>
67-
Bech32.decode(
67+
fromBech32(
6868
"cosmospub1ytql0csgqvfzd666axrjzqmn5q2ucztcyxw8hvlzen94ay05tegaerkug5pn3xn8wqdymt598ufzd666axrjzqsxllmwacap3f6xyc4x30jl8ecrcs2tze3zzgxkmthcsqxnqxhwwgfzd666axrjzqs2rlu3wz5gnslgpprszjr8r65n0d6y39q657th77eyvengtk3z0y6h2pnk",
6969
90,
7070
),

packages/encoding/src/bech32.ts

+27-7
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,39 @@
11
import * as bech32 from "bech32";
22

3+
export function toBech32(prefix: string, data: Uint8Array, limit?: number): string {
4+
const address = bech32.encode(prefix, bech32.toWords(data), limit);
5+
return address;
6+
}
7+
8+
export function fromBech32(
9+
address: string,
10+
limit = Infinity,
11+
): { readonly prefix: string; readonly data: Uint8Array } {
12+
const decodedAddress = bech32.decode(address, limit);
13+
return {
14+
prefix: decodedAddress.prefix,
15+
data: new Uint8Array(bech32.fromWords(decodedAddress.words)),
16+
};
17+
}
18+
19+
/**
20+
* @deprecated This class is deprecated and will be removed soon. Please use fromBech32() and toBech32() instead. For more details please refer to https://github.com/cosmos/cosmjs/issues/1053.
21+
*/
322
export class Bech32 {
23+
/**
24+
* @deprecated This class is deprecated and will be removed soon. Please use fromBech32() and toBech32() instead. For more details please refer to https://github.com/cosmos/cosmjs/issues/1053.
25+
*/
426
public static encode(prefix: string, data: Uint8Array, limit?: number): string {
5-
const address = bech32.encode(prefix, bech32.toWords(data), limit);
6-
return address;
27+
return toBech32(prefix, data, limit);
728
}
829

30+
/**
31+
* @deprecated This class is deprecated and will be removed soon. Please use fromBech32() and toBech32() instead. For more details please refer to https://github.com/cosmos/cosmjs/issues/1053.
32+
*/
933
public static decode(
1034
address: string,
1135
limit = Infinity,
1236
): { readonly prefix: string; readonly data: Uint8Array } {
13-
const decodedAddress = bech32.decode(address, limit);
14-
return {
15-
prefix: decodedAddress.prefix,
16-
data: new Uint8Array(bech32.fromWords(decodedAddress.words)),
17-
};
37+
return fromBech32(address, limit);
1838
}
1939
}

packages/encoding/src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export { fromAscii, toAscii } from "./ascii";
22
export { fromBase64, toBase64 } from "./base64";
3-
export { Bech32 } from "./bech32";
3+
export { Bech32, fromBech32, toBech32 } from "./bech32";
44
export { fromHex, toHex } from "./hex";
55
export { fromRfc3339, toRfc3339 } from "./rfc3339";
66
export { fromUtf8, toUtf8 } from "./utf8";

packages/faucet/src/faucet.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Random } from "@cosmjs/crypto";
2-
import { Bech32 } from "@cosmjs/encoding";
2+
import { toBech32 } from "@cosmjs/encoding";
33
import { makeCosmoshubPath, StargateClient } from "@cosmjs/stargate";
44
import { assert } from "@cosmjs/utils";
55

@@ -18,7 +18,7 @@ const defaultTokenConfig: TokenConfiguration = {
1818
const defaultAddressPrefix = "cosmos";
1919

2020
function makeRandomAddress(): string {
21-
return Bech32.encode(defaultAddressPrefix, Random.getBytes(20));
21+
return toBech32(defaultAddressPrefix, Random.getBytes(20));
2222
}
2323

2424
const faucetMnemonic =

packages/proto-signing/src/directsecp256k1hdwallet.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
Slip10Curve,
1313
stringToPath,
1414
} from "@cosmjs/crypto";
15-
import { Bech32, fromBase64, fromUtf8, toBase64, toUtf8 } from "@cosmjs/encoding";
15+
import { fromBase64, fromUtf8, toBase64, toBech32, toUtf8 } from "@cosmjs/encoding";
1616
import { assert, isNonNullObject } from "@cosmjs/utils";
1717
import { SignDoc } from "cosmjs-types/cosmos/tx/v1beta1/tx";
1818

@@ -346,7 +346,7 @@ export class DirectSecp256k1HdWallet implements OfflineDirectSigner {
346346
return Promise.all(
347347
this.accounts.map(async ({ hdPath, prefix }) => {
348348
const { privkey, pubkey } = await this.getKeyPair(hdPath);
349-
const address = Bech32.encode(prefix, rawSecp256k1PubkeyToRawAddress(pubkey));
349+
const address = toBech32(prefix, rawSecp256k1PubkeyToRawAddress(pubkey));
350350
return {
351351
algo: "secp256k1" as const,
352352
privkey: privkey,

0 commit comments

Comments
 (0)