diff --git a/packages/stargate/src/stargateclient.spec.ts b/packages/stargate/src/stargateclient.spec.ts index 484a1eb8..f8ffe874 100644 --- a/packages/stargate/src/stargateclient.spec.ts +++ b/packages/stargate/src/stargateclient.spec.ts @@ -1,3 +1,5 @@ +import { assert } from "@cosmjs/utils"; + import { StargateClient } from "./stargateclient"; import { nonExistentAddress, pendingWithoutSimapp, simapp, unused } from "./testutils.spec"; @@ -11,14 +13,55 @@ describe("StargateClient", () => { }); }); + describe("getAccount", () => { + it("works for unused account", async () => { + pendingWithoutSimapp(); + const client = await StargateClient.connect(simapp.tendermintUrl); + + const account = await client.getAccount(unused.address); + assert(account); + expect(account).toEqual({ + address: unused.address, + pubkey: null, + accountNumber: unused.accountNumber, + sequence: unused.sequence, + }); + + client.disconnect(); + }); + + it("returns null for non-existent address", async () => { + pendingWithoutSimapp(); + const client = await StargateClient.connect(simapp.tendermintUrl); + + const account = await client.getAccount(nonExistentAddress); + expect(account).toBeNull(); + + client.disconnect(); + }); + }); + describe("getSequence", () => { it("works for unused account", async () => { pendingWithoutSimapp(); const client = await StargateClient.connect(simapp.tendermintUrl); - const { accountNumber, sequence } = await client.getSequence(unused.address); - expect(accountNumber).toEqual(unused.accountNumber); - expect(sequence).toEqual(unused.sequence); + const account = await client.getSequence(unused.address); + assert(account); + expect(account).toEqual({ + accountNumber: unused.accountNumber, + sequence: unused.sequence, + }); + + client.disconnect(); + }); + + it("returns null for non-existent address", async () => { + pendingWithoutSimapp(); + const client = await StargateClient.connect(simapp.tendermintUrl); + + const account = await client.getSequence(nonExistentAddress); + expect(account).toBeNull(); client.disconnect(); }); diff --git a/packages/stargate/src/stargateclient.ts b/packages/stargate/src/stargateclient.ts index 70257be2..751e3e82 100644 --- a/packages/stargate/src/stargateclient.ts +++ b/packages/stargate/src/stargateclient.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { Bech32, toAscii, toHex } from "@cosmjs/encoding"; -import { Coin } from "@cosmjs/launchpad"; +import { Coin, encodeSecp256k1Pubkey, PubKey } from "@cosmjs/launchpad"; import { Uint64 } from "@cosmjs/math"; import { decodeAny } from "@cosmjs/proto-signing"; import { Client as TendermintClient } from "@cosmjs/tendermint-rpc"; @@ -9,7 +9,15 @@ import Long from "long"; import { cosmos } from "./generated/codecimpl"; -export interface GetSequenceResult { +export interface Account { + /** Bech32 account address */ + readonly address: string; + readonly pubkey: PubKey | null; + readonly accountNumber: number; + readonly sequence: number; +} + +export interface SequenceResponse { readonly accountNumber: number; readonly sequence: number; } @@ -41,23 +49,39 @@ export class StargateClient { this.tmClient = tmClient; } - public async getSequence(address: string): Promise { - const binAddress = Bech32.decode(address).data; + public async getAccount(searchAddress: string): Promise { + const { prefix, data: binAddress } = Bech32.decode(searchAddress); // https://github.com/cosmos/cosmos-sdk/blob/8cab43c8120fec5200c3459cbf4a92017bb6f287/x/auth/types/keys.go#L29-L32 const accountKey = Uint8Array.from([0x01, ...binAddress]); const responseData = await this.queryVerified("acc", accountKey); + if (responseData.length === 0) return null; + const { typeUrl, value } = decodeAny(responseData); switch (typeUrl) { case "/cosmos.auth.BaseAccount": { - const { accountNumber, sequence } = cosmos.auth.BaseAccount.decode(value); + const { address, pubKey, accountNumber, sequence } = cosmos.auth.BaseAccount.decode(value); return { + address: Bech32.encode(prefix, address), + pubkey: pubKey.length ? encodeSecp256k1Pubkey(pubKey) : null, accountNumber: uint64FromProto(accountNumber).toNumber(), sequence: uint64FromProto(sequence).toNumber(), }; } default: - throw new Error(`Unsupported type: ${typeUrl}`); + throw new Error(`Unsupported type: '${typeUrl}'`); + } + } + + public async getSequence(address: string): Promise { + const account = await this.getAccount(address); + if (account) { + return { + accountNumber: uint64FromProto(account.accountNumber).toNumber(), + sequence: uint64FromProto(account.sequence).toNumber(), + }; + } else { + return null; } } diff --git a/packages/stargate/types/stargateclient.d.ts b/packages/stargate/types/stargateclient.d.ts index 0503db45..d0ea02ca 100644 --- a/packages/stargate/types/stargateclient.d.ts +++ b/packages/stargate/types/stargateclient.d.ts @@ -1,5 +1,12 @@ -import { Coin } from "@cosmjs/launchpad"; -export interface GetSequenceResult { +import { Coin, PubKey } from "@cosmjs/launchpad"; +export interface Account { + /** Bech32 account address */ + readonly address: string; + readonly pubkey: PubKey | null; + readonly accountNumber: number; + readonly sequence: number; +} +export interface SequenceResponse { readonly accountNumber: number; readonly sequence: number; } @@ -7,7 +14,8 @@ export declare class StargateClient { private readonly tmClient; static connect(endpoint: string): Promise; private constructor(); - getSequence(address: string): Promise; + getAccount(searchAddress: string): Promise; + getSequence(address: string): Promise; getBalance(address: string, searchDenom: string): Promise; /** * Queries all balances for all denoms that belong to this address.