diff --git a/CHANGELOG.md b/CHANGELOG.md index 4674a624..4aea3304 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,20 @@ and this project adheres to `Bech32.encode(prefix, rawSecp256k1PubkeyToRawAddress(pubkeyRaw))` with `rawSecp256k1PubkeyToRawAddress` from @cosmjs/amino. - @cosmjs/stargate: `parseRawLog` is now nested under the `logs` export. +- @cosmjs/stargate: `auth` extension now has unverified queries at the root and + verified queries nested under `.verified`. +- @cosmjs/stargate: `StargateClient.getAccount` now uses an unverified query and + `StargateClient.getAccountUnverified` has been removed. + `StargateClient.getAccountVerified` has been added, which performs a verified + query. +- @cosmjs/cosmwasm-stargate: `CosmWasmClient.getAccount` now uses an unverified + query and `CosmWasmClient.getAccountUnverified` has been removed. + `CosmWasmClient.getAccountVerified` has been added, which performs a verified + query. +- @cosmjs/stargate: `StargateClient.getSequence` now rejects if the account is + not found, instead of returning null. +- @cosmjs/cosmwasm-stargate: `CosmWasmClient.getSequence` now rejects if the + account is not found, instead of returning null. ### Deprecated diff --git a/packages/cosmwasm-stargate/src/cosmwasmclient.spec.ts b/packages/cosmwasm-stargate/src/cosmwasmclient.spec.ts index ce064371..8e4c77bd 100644 --- a/packages/cosmwasm-stargate/src/cosmwasmclient.spec.ts +++ b/packages/cosmwasm-stargate/src/cosmwasmclient.spec.ts @@ -82,24 +82,6 @@ describe("CosmWasmClient", () => { }); }); - describe("getSequence", () => { - it("works", async () => { - pendingWithoutWasmd(); - const client = await CosmWasmClient.connect(wasmd.endpoint); - expect(await client.getSequence(unused.address)).toEqual({ - accountNumber: unused.accountNumber, - sequence: unused.sequence, - }); - }); - - it("returns null for missing accounts", async () => { - pendingWithoutWasmd(); - const client = await CosmWasmClient.connect(wasmd.endpoint); - const missing = makeRandomAddress(); - expect(await client.getSequence(missing)).toBeNull(); - }); - }); - describe("getAccount", () => { it("works", async () => { pendingWithoutWasmd(); @@ -120,6 +102,26 @@ describe("CosmWasmClient", () => { }); }); + describe("getSequence", () => { + it("works", async () => { + pendingWithoutWasmd(); + const client = await CosmWasmClient.connect(wasmd.endpoint); + expect(await client.getSequence(unused.address)).toEqual({ + accountNumber: unused.accountNumber, + sequence: unused.sequence, + }); + }); + + it("rejects for missing accounts", async () => { + pendingWithoutWasmd(); + const client = await CosmWasmClient.connect(wasmd.endpoint); + const missing = makeRandomAddress(); + await expectAsync(client.getSequence(missing)).toBeRejectedWithError( + /account does not exist on chain/i, + ); + }); + }); + describe("getBlock", () => { it("works for latest block", async () => { pendingWithoutWasmd(); diff --git a/packages/cosmwasm-stargate/src/cosmwasmclient.ts b/packages/cosmwasm-stargate/src/cosmwasmclient.ts index 80ee8fd5..b6d57e64 100644 --- a/packages/cosmwasm-stargate/src/cosmwasmclient.ts +++ b/packages/cosmwasm-stargate/src/cosmwasmclient.ts @@ -109,20 +109,28 @@ export class CosmWasmClient { } public async getAccount(searchAddress: string): Promise { - const account = await this.forceGetQueryClient().auth.account(searchAddress); - return account ? accountFromAny(account) : null; + try { + const account = await this.forceGetQueryClient().auth.account(searchAddress); + return account ? accountFromAny(account) : null; + } catch (error) { + if (/rpc error: code = NotFound/i.test(error)) { + return null; + } + throw error; + } } - public async getSequence(address: string): Promise { + public async getSequence(address: string): Promise { const account = await this.getAccount(address); - if (account) { - return { - accountNumber: account.accountNumber, - sequence: account.sequence, - }; - } else { - return null; + if (!account) { + throw new Error( + "Account does not exist on chain. Send some tokens there before trying to query sequence.", + ); } + return { + accountNumber: account.accountNumber, + sequence: account.sequence, + }; } public async getBlock(height?: number): Promise { diff --git a/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts b/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts index 9184905a..8da3742c 100644 --- a/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm-stargate/src/signingcosmwasmclient.ts @@ -409,11 +409,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { throw new Error("Failed to retrieve account from signer"); } const pubkey = encodePubkey(encodeSecp256k1Pubkey(accountFromSigner.pubkey)); - const accountFromChain = await this.getAccount(signerAddress); - if (!accountFromChain) { - throw new Error("Account not found"); - } - const { accountNumber, sequence } = accountFromChain; + const { accountNumber, sequence } = await this.getSequence(signerAddress); const chainId = await this.getChainId(); const txBody = { messages: messages, diff --git a/packages/stargate/src/queries/auth.spec.ts b/packages/stargate/src/queries/auth.spec.ts index c95d3fca..c09584db 100644 --- a/packages/stargate/src/queries/auth.spec.ts +++ b/packages/stargate/src/queries/auth.spec.ts @@ -53,23 +53,24 @@ describe("AuthExtension", () => { tmClient.disconnect(); }); - it("returns null for non-existent address", async () => { + it("rejects for non-existent address", async () => { pendingWithoutSimapp(); const [client, tmClient] = await makeClientWithAuth(simapp.tendermintUrl); - const account = await client.auth.account(nonExistentAddress); - expect(account).toBeNull(); + await expectAsync(client.auth.account(nonExistentAddress)).toBeRejectedWithError( + /account cosmos1p79apjaufyphcmsn4g07cynqf0wyjuezqu84hd not found/i, + ); tmClient.disconnect(); }); }); - describe("unverified", () => { + describe("verified", () => { describe("account", () => { it("works for unused account", async () => { pendingWithoutSimapp(); const [client, tmClient] = await makeClientWithAuth(simapp.tendermintUrl); - const account = await client.auth.unverified.account(unused.address); + const account = await client.auth.verified.account(unused.address); assert(account); expect(account.typeUrl).toEqual("/cosmos.auth.v1beta1.BaseAccount"); @@ -86,7 +87,7 @@ describe("AuthExtension", () => { it("works for account with pubkey and non-zero sequence", async () => { pendingWithoutSimapp(); const [client, tmClient] = await makeClientWithAuth(simapp.tendermintUrl); - const account = await client.auth.unverified.account(validator.delegatorAddress); + const account = await client.auth.verified.account(validator.delegatorAddress); assert(account); expect(account.typeUrl).toEqual("/cosmos.auth.v1beta1.BaseAccount"); @@ -103,10 +104,9 @@ describe("AuthExtension", () => { it("returns null for non-existent address", async () => { pendingWithoutSimapp(); const [client, tmClient] = await makeClientWithAuth(simapp.tendermintUrl); + const account = await client.auth.verified.account(nonExistentAddress); - await expectAsync(client.auth.unverified.account(nonExistentAddress)).toBeRejectedWithError( - /account cosmos1p79apjaufyphcmsn4g07cynqf0wyjuezqu84hd not found/i, - ); + expect(account).toBeNull(); tmClient.disconnect(); }); diff --git a/packages/stargate/src/queries/auth.ts b/packages/stargate/src/queries/auth.ts index 07a65906..fe7259f3 100644 --- a/packages/stargate/src/queries/auth.ts +++ b/packages/stargate/src/queries/auth.ts @@ -13,7 +13,7 @@ export interface AuthExtension { * `typeUrl` and decode the `value` using its own type decoder. */ readonly account: (address: string) => Promise; - readonly unverified: { + readonly verified: { /** * Returns an account if it exists and `null` otherwise. * @@ -35,16 +35,16 @@ export function setupAuthExtension(base: QueryClient): AuthExtension { return { auth: { account: async (address: string) => { - // https://github.com/cosmos/cosmos-sdk/blob/8cab43c8120fec5200c3459cbf4a92017bb6f287/x/auth/types/keys.go#L29-L32 - const key = Uint8Array.from([0x01, ...toAccAddress(address)]); - const responseData = await base.queryVerified("acc", key); - if (responseData.length === 0) return null; - return Any.decode(responseData); + const { account } = await queryService.Account({ address: address }); + return account ?? null; }, - unverified: { + verified: { account: async (address: string) => { - const { account } = await queryService.Account({ address: address }); - return account ?? null; + // https://github.com/cosmos/cosmos-sdk/blob/8cab43c8120fec5200c3459cbf4a92017bb6f287/x/auth/types/keys.go#L29-L32 + const key = Uint8Array.from([0x01, ...toAccAddress(address)]); + const responseData = await base.queryVerified("acc", key); + if (responseData.length === 0) return null; + return Any.decode(responseData); }, }, }, diff --git a/packages/stargate/src/signingstargateclient.ts b/packages/stargate/src/signingstargateclient.ts index 0732a8b6..488e34ca 100644 --- a/packages/stargate/src/signingstargateclient.ts +++ b/packages/stargate/src/signingstargateclient.ts @@ -276,11 +276,7 @@ export class SigningStargateClient extends StargateClient { if (explicitSignerData) { signerData = explicitSignerData; } else { - const accountFromChain = await this.getAccountUnverified(signerAddress); - if (!accountFromChain) { - throw new Error("Account not found"); - } - const { accountNumber, sequence } = accountFromChain; + const { accountNumber, sequence } = await this.getSequence(signerAddress); const chainId = await this.getChainId(); signerData = { accountNumber, sequence, chainId }; } diff --git a/packages/stargate/src/stargateclient.spec.ts b/packages/stargate/src/stargateclient.spec.ts index c28e67b0..e8791532 100644 --- a/packages/stargate/src/stargateclient.spec.ts +++ b/packages/stargate/src/stargateclient.spec.ts @@ -131,12 +131,13 @@ describe("StargateClient", () => { client.disconnect(); }); - it("returns null for non-existent address", async () => { + it("rejects for non-existent address", async () => { pendingWithoutSimapp(); const client = await StargateClient.connect(simapp.tendermintUrl); - const account = await client.getSequence(nonExistentAddress); - expect(account).toBeNull(); + await expectAsync(client.getSequence(nonExistentAddress)).toBeRejectedWithError( + /account does not exist on chain/i, + ); client.disconnect(); }); diff --git a/packages/stargate/src/stargateclient.ts b/packages/stargate/src/stargateclient.ts index a97c7e46..7cf1e2b8 100644 --- a/packages/stargate/src/stargateclient.ts +++ b/packages/stargate/src/stargateclient.ts @@ -160,31 +160,36 @@ export class StargateClient { return status.syncInfo.latestBlockHeight; } - // this is nice to display data to the user, but is slower public async getAccount(searchAddress: string): Promise { - const account = await this.forceGetQueryClient().auth.account(searchAddress); - return account ? accountFromAny(account) : null; - } - - // if we just need to get the sequence for signing a transaction, let's make this faster - // (no need to wait a block before submitting) - public async getAccountUnverified(searchAddress: string): Promise { - const account = await this.forceGetQueryClient().auth.unverified.account(searchAddress); - return account ? accountFromAny(account) : null; - } - - public async getSequence(address: string): Promise { - const account = await this.getAccount(address); - if (account) { - return { - accountNumber: account.accountNumber, - sequence: account.sequence, - }; - } else { - return null; + try { + const account = await this.forceGetQueryClient().auth.account(searchAddress); + return account ? accountFromAny(account) : null; + } catch (error) { + if (/rpc error: code = NotFound/i.test(error)) { + return null; + } + throw error; } } + public async getAccountVerified(searchAddress: string): Promise { + const account = await this.forceGetQueryClient().auth.verified.account(searchAddress); + return account ? accountFromAny(account) : null; + } + + public async getSequence(address: string): Promise { + const account = await this.getAccount(address); + if (!account) { + throw new Error( + "Account does not exist on chain. Send some tokens there before trying to query sequence.", + ); + } + return { + accountNumber: account.accountNumber, + sequence: account.sequence, + }; + } + public async getBlock(height?: number): Promise { const response = await this.forceGetTmClient().block(height); return {