From 4e72dc236cc33754952b8f42bb4665fc52e5add9 Mon Sep 17 00:00:00 2001 From: Zetazzz Date: Mon, 17 Jun 2024 06:37:14 +0800 Subject: [PATCH 1/5] add doc signers --- networks/cosmos/src/amino.ts | 10 ++++-- networks/cosmos/src/base/base-signer.ts | 20 +++++++++-- networks/cosmos/src/base/tx-builder.ts | 36 +++++++++++++++---- .../cosmos/src/builder/amino-tx-builder.ts | 8 ++++- .../cosmos/src/builder/direct-tx-builder.ts | 12 ++++++- networks/cosmos/src/direct.ts | 10 ++++-- packages/types/src/signer.ts | 10 ++++-- 7 files changed, 89 insertions(+), 17 deletions(-) diff --git a/networks/cosmos/src/amino.ts b/networks/cosmos/src/amino.ts index 70105566..0f478ea2 100644 --- a/networks/cosmos/src/amino.ts +++ b/networks/cosmos/src/amino.ts @@ -1,9 +1,9 @@ import { Auth, HttpEndpoint } from '@interchainjs/types'; import { constructAuthsFromWallet } from '@interchainjs/utils'; -import { BaseCosmosTxBuilder, CosmosBaseSigner } from './base'; +import { BaseCosmosTxBuilder, CosmosBaseSigner, CosmosDocSigner } from './base'; import { BaseCosmosTxBuilderContext } from './base/builder-context'; -import { AminoTxBuilder } from './builder/amino-tx-builder'; +import { AminoSigBuilder, AminoTxBuilder } from './builder/amino-tx-builder'; import { defaultSignerConfig } from './defaults'; import { AminoConverter, @@ -14,6 +14,12 @@ import { SignerOptions, } from './types'; +export class AminoDocSigner extends CosmosDocSigner { + getTxBuilder(): AminoSigBuilder { + return new AminoSigBuilder(new BaseCosmosTxBuilderContext(this)); + } +} + export abstract class AminoSignerBase< AminoDoc, > extends CosmosBaseSigner { diff --git a/networks/cosmos/src/base/base-signer.ts b/networks/cosmos/src/base/base-signer.ts index 0d2a2bd0..2130d681 100644 --- a/networks/cosmos/src/base/base-signer.ts +++ b/networks/cosmos/src/base/base-signer.ts @@ -10,6 +10,7 @@ import { BroadcastOptions, HttpEndpoint, IKey, + SignDocResponse, SignResponse, } from '@interchainjs/types'; import { assertEmpty, isEmpty } from '@interchainjs/utils'; @@ -28,10 +29,23 @@ import { UniCosmosBaseSigner, } from '../types'; import { calculateFee } from '../utils/fee'; -import { BaseCosmosTxBuilder } from './tx-builder'; +import { BaseCosmosSigBuilder, BaseCosmosTxBuilder } from './tx-builder'; + +export abstract class CosmosDocSigner extends BaseSigner { + txBuilder: BaseCosmosSigBuilder; + abstract getTxBuilder(): BaseCosmosSigBuilder; + async signDoc(doc: SignDoc): Promise> { + const sig = await this.txBuilder.buildSignature(doc); + + return { + signature: sig, + signDoc: doc, + }; + } +} export abstract class CosmosBaseSigner - extends BaseSigner + extends CosmosDocSigner implements UniCosmosBaseSigner { _queryClient?: QueryClient; @@ -39,7 +53,7 @@ export abstract class CosmosBaseSigner readonly _encodePublicKey: (key: IKey) => EncodedMessage; readonly parseAccount: (encodedAccount: EncodedMessage) => BaseAccount; prefix?: string; - txBuilder: BaseCosmosTxBuilder; + declare txBuilder: BaseCosmosTxBuilder; constructor( auth: Auth, diff --git a/networks/cosmos/src/base/tx-builder.ts b/networks/cosmos/src/base/tx-builder.ts index 439bf724..afffcb39 100644 --- a/networks/cosmos/src/base/tx-builder.ts +++ b/networks/cosmos/src/base/tx-builder.ts @@ -6,7 +6,13 @@ import { TxBody, TxRaw, } from '@interchainjs/cosmos-types/cosmos/tx/v1beta1/tx'; -import { ITxBuilder, StdFee } from '@interchainjs/types'; +import { + BaseSigner, + IKey, + ISigBuilder, + ITxBuilder, + StdFee, +} from '@interchainjs/types'; import { CosmosCreateDocResponse, @@ -18,16 +24,36 @@ import { calculateFee, toFee } from '../utils'; import { CosmosBaseSigner } from './base-signer'; import { BaseCosmosTxBuilderContext } from './builder-context'; +export abstract class BaseCosmosSigBuilder +implements ISigBuilder +{ + constructor(protected ctx: BaseCosmosTxBuilderContext) {} + + abstract buildDocBytes(doc: SignDoc): Promise; + async buildSignature(doc: SignDoc): Promise { + // get doc bytes + const docBytes = await this.buildDocBytes(doc); + + // sign signature to the doc bytes + return this.ctx.signer.signArbitrary(docBytes); + } +} + /** * BaseCosmosTxBuilder is a helper class to build the Tx and signDoc */ export abstract class BaseCosmosTxBuilder -implements ITxBuilder> + extends BaseCosmosSigBuilder + implements + ITxBuilder>, + ISigBuilder { constructor( public signMode: SignMode, protected ctx: BaseCosmosTxBuilderContext> - ) {} + ) { + super(ctx); + } abstract buildDoc( args: CosmosSignArgs, @@ -150,11 +176,9 @@ implements ITxBuilder> // buildDoc const doc = await this.buildDoc({ messages, fee, memo, options }, txRaw); - // get doc bytes - const docBytes = await this.buildDocBytes(doc); // sign signature to the doc bytes - const signature = this.ctx.signer.signArbitrary(docBytes); + const signature = await this.buildSignature(doc); // build TxRaw const signedTxRaw = TxRaw.fromPartial({ diff --git a/networks/cosmos/src/builder/amino-tx-builder.ts b/networks/cosmos/src/builder/amino-tx-builder.ts index d36c6c16..d88cc830 100644 --- a/networks/cosmos/src/builder/amino-tx-builder.ts +++ b/networks/cosmos/src/builder/amino-tx-builder.ts @@ -1,11 +1,17 @@ import { SignMode } from '@interchainjs/cosmos-types/cosmos/tx/signing/v1beta1/signing'; import { type AminoSignerBase } from '../amino'; -import { BaseCosmosTxBuilder } from '../base'; +import { BaseCosmosSigBuilder, BaseCosmosTxBuilder } from '../base'; import { BaseCosmosTxBuilderContext } from '../base/builder-context'; import { CosmosAminoDoc, CosmosSignArgs } from '../types'; import { encodeStdSignDoc, toAminoMsgs } from '../utils'; +export class AminoSigBuilder extends BaseCosmosSigBuilder { + async buildDocBytes(doc: CosmosAminoDoc): Promise { + return encodeStdSignDoc(doc); + } +} + export class AminoTxBuilder extends BaseCosmosTxBuilder { constructor( protected ctx: BaseCosmosTxBuilderContext> diff --git a/networks/cosmos/src/builder/direct-tx-builder.ts b/networks/cosmos/src/builder/direct-tx-builder.ts index 2efbb521..cf868729 100644 --- a/networks/cosmos/src/builder/direct-tx-builder.ts +++ b/networks/cosmos/src/builder/direct-tx-builder.ts @@ -4,10 +4,20 @@ import { TxRaw, } from '@interchainjs/cosmos-types/cosmos/tx/v1beta1/tx'; -import { BaseCosmosTxBuilder, CosmosBaseSigner } from '../base'; +import { + BaseCosmosSigBuilder, + BaseCosmosTxBuilder, + CosmosBaseSigner, +} from '../base'; import { BaseCosmosTxBuilderContext } from '../base/builder-context'; import { CosmosDirectDoc, CosmosSignArgs } from '../types'; +export class DirectSigBuilder extends BaseCosmosSigBuilder { + async buildDocBytes(doc: CosmosDirectDoc): Promise { + return SignDoc.encode(doc).finish(); + } +} + export class DirectTxBuilder extends BaseCosmosTxBuilder { constructor( protected ctx: BaseCosmosTxBuilderContext> diff --git a/networks/cosmos/src/direct.ts b/networks/cosmos/src/direct.ts index 8cb4ad46..64f61f4c 100644 --- a/networks/cosmos/src/direct.ts +++ b/networks/cosmos/src/direct.ts @@ -1,9 +1,9 @@ import { Auth, HttpEndpoint } from '@interchainjs/types'; import { constructAuthsFromWallet } from '@interchainjs/utils'; -import { BaseCosmosTxBuilder, CosmosBaseSigner } from './base'; +import { BaseCosmosTxBuilder, CosmosBaseSigner, CosmosDocSigner } from './base'; import { BaseCosmosTxBuilderContext } from './base/builder-context'; -import { DirectTxBuilder } from './builder/direct-tx-builder'; +import { DirectSigBuilder, DirectTxBuilder } from './builder/direct-tx-builder'; import { defaultSignerConfig } from './defaults'; import { CosmosBaseWallet, @@ -13,6 +13,12 @@ import { SignerOptions, } from './types'; +export class DirectDocSigner extends CosmosDocSigner { + getTxBuilder(): DirectSigBuilder { + return new DirectSigBuilder(new BaseCosmosTxBuilderContext(this)); + } +} + export class DirectSignerBase extends CosmosBaseSigner { constructor( auth: Auth, diff --git a/packages/types/src/signer.ts b/packages/types/src/signer.ts index 44ffee53..0f4d2da3 100644 --- a/packages/types/src/signer.ts +++ b/packages/types/src/signer.ts @@ -1,6 +1,7 @@ import Decimal from 'decimal.js'; import { Auth, IKey, Signature } from './auth'; +import { SignDocResponse } from './wallet'; export interface HttpEndpoint { url: string; @@ -46,6 +47,10 @@ export interface SignResponse broadcast: (options?: BroadcastOptions) => Promise; } +export interface ISigBuilder { + buildSignature(doc: Doc): Sig | Promise; +} + export interface ITxBuilder { buildSignedTxDoc(args: SignArgs): Promise; } @@ -81,8 +86,9 @@ export interface UniSigner< * to get printable address(es) */ getAddress(): AddressResponse; - signArbitrary(data: Uint8Array): IKey; - verifyArbitrary(data: Uint8Array, signature: IKey): boolean; + signArbitrary(data: Uint8Array): IKey | Promise; + signDoc(doc: Doc): SignDocResponse | Promise>; + verifyArbitrary(data: Uint8Array, signature: IKey): boolean | Promise; broadcastArbitrary( data: Uint8Array, options?: BroadcastOptions From 0b8f7a2d24219e46c9d29c74516c73da9a87a1d7 Mon Sep 17 00:00:00 2001 From: Zetazzz Date: Mon, 17 Jun 2024 15:38:27 +0800 Subject: [PATCH 2/5] fix build --- networks/cosmos/src/query/rpc.ts | 4 ++-- networks/cosmos/src/types/rpc.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/networks/cosmos/src/query/rpc.ts b/networks/cosmos/src/query/rpc.ts index f4b0b0ad..7b7d46b8 100644 --- a/networks/cosmos/src/query/rpc.ts +++ b/networks/cosmos/src/query/rpc.ts @@ -18,7 +18,7 @@ import { EncodedMessage, QueryClient, } from '../types'; -import { Status } from '../types/rpc'; +import { AsyncCometBroadcastResponse, CommitCometBroadcastResponse, Status, SyncCometBroadcastResponse } from '../types/rpc'; import { constructAuthInfo } from '../utils/direct'; import { broadcast, createTxRpc, getPrefix } from '../utils/rpc'; @@ -166,7 +166,7 @@ export class RpcClient implements QueryClient { deliver_tx, height, hash: hash3, - } = resp as CometBroadcastResponse.Commit; + } = resp as CommitCometBroadcastResponse; return { hash: hash3, check_tx, diff --git a/networks/cosmos/src/types/rpc.ts b/networks/cosmos/src/types/rpc.ts index ffd28401..60cf79ed 100644 --- a/networks/cosmos/src/types/rpc.ts +++ b/networks/cosmos/src/types/rpc.ts @@ -11,7 +11,7 @@ export type SyncCometBroadcastResponse = { hash: string; } & CheckTxResponse; -export interface Commit { +export interface CommitCometBroadcastResponse { hash: string; check_tx: CheckTxResponse; deliver_tx: DeliverTxResponse; From 6e9a068ce264a7bea5f8dfb9b4effda8bb286262 Mon Sep 17 00:00:00 2001 From: Zetazzz Date: Wed, 19 Jun 2024 06:26:39 +0800 Subject: [PATCH 3/5] fix build --- libs/interchainjs/src/cosmwasm-stargate.ts | 2 +- libs/interchainjs/src/signing-client.ts | 19 ++- libs/interchainjs/src/stargate.ts | 2 +- libs/interchainjs/src/types/wallet.ts | 30 ---- libs/interchainjs/src/wallets/secp256k1.ts | 153 --------------------- networks/cosmos/src/amino.ts | 21 +-- networks/cosmos/src/base/base-signer.ts | 2 +- networks/cosmos/src/direct.ts | 20 +-- networks/cosmos/src/types/signer.ts | 46 ++++++- networks/cosmos/src/types/wallet.ts | 51 +++++++ networks/cosmos/src/utils/wallet.ts | 26 +--- networks/cosmos/src/wallets/secp256k1hd.ts | 126 +++++++++++++++++ packages/types/src/wallet.ts | 5 + 13 files changed, 247 insertions(+), 256 deletions(-) delete mode 100644 libs/interchainjs/src/wallets/secp256k1.ts create mode 100644 networks/cosmos/src/types/wallet.ts create mode 100644 networks/cosmos/src/wallets/secp256k1hd.ts diff --git a/libs/interchainjs/src/cosmwasm-stargate.ts b/libs/interchainjs/src/cosmwasm-stargate.ts index edc84e1c..5eb61efa 100644 --- a/libs/interchainjs/src/cosmwasm-stargate.ts +++ b/libs/interchainjs/src/cosmwasm-stargate.ts @@ -1,4 +1,5 @@ import { AminoSigner } from '@interchainjs/cosmos/amino'; +import { OfflineSigner } from '@interchainjs/cosmos/types/wallet'; import { toConverter, toEncoder } from '@interchainjs/cosmos/utils'; import { CosmWasmMsgs } from '@interchainjs/cosmos-types/cosmwasm'; import { CosmWasmStargateImpl as TxImpl } from '@interchainjs/cosmos-types/service-ops'; @@ -7,7 +8,6 @@ import { HttpEndpoint } from '@interchainjs/types'; import { SigningClient } from './signing-client'; import { SignerOptions } from './types/signing-client'; -import { OfflineSigner } from './types/wallet'; import { defaultAuth } from './utils'; export class CosmWasmSigningClient extends SigningClient { diff --git a/libs/interchainjs/src/signing-client.ts b/libs/interchainjs/src/signing-client.ts index f22f1aa2..16628159 100644 --- a/libs/interchainjs/src/signing-client.ts +++ b/libs/interchainjs/src/signing-client.ts @@ -1,5 +1,10 @@ import { AminoSigner } from '@interchainjs/cosmos/amino'; -import { AccountData } from '@interchainjs/cosmos/types'; +import { AccountData, isICosmosAccount } from '@interchainjs/cosmos/types'; +import { + OfflineAminoSigner, + OfflineDirectSigner, + OfflineSigner, +} from '@interchainjs/cosmos/types/wallet'; import { constructAuthInfo, constructSignerInfo, @@ -44,11 +49,6 @@ import { SignerData, SignerOptions, } from './types/signing-client'; -import { - OfflineAminoSigner, - OfflineDirectSigner, - OfflineSigner, -} from './types/wallet'; import { BroadcastTxError, defaultAuth, sleep, TimeoutError } from './utils'; /** @@ -125,15 +125,14 @@ export class SigningClient { private async getAccountData(address: string): Promise { const accounts = await this.offlineSigner.getAccounts(); - const account = accounts.find( - (account) => account.getAddress() === address - ); + const account = accounts.find((account) => account.address === address); if (!account) { throw new Error( `No such account found in OfflineSigner for address ${address}` ); } - return account.toAccountData(); + + return isICosmosAccount(account) ? account.toAccountData() : account; } private async getPubkey(address: string): Promise { diff --git a/libs/interchainjs/src/stargate.ts b/libs/interchainjs/src/stargate.ts index dbcf3d0d..cfe62c65 100644 --- a/libs/interchainjs/src/stargate.ts +++ b/libs/interchainjs/src/stargate.ts @@ -1,4 +1,5 @@ import { AminoSigner } from '@interchainjs/cosmos/amino'; +import { OfflineSigner } from '@interchainjs/cosmos/types/wallet'; import { toConverter, toEncoder } from '@interchainjs/cosmos/utils'; import { StargateImpl as TxImpl } from '@interchainjs/cosmos-types/service-ops'; import { StargateMsgs } from '@interchainjs/cosmos-types/stargate'; @@ -6,7 +7,6 @@ import { HttpEndpoint } from '@interchainjs/types'; import { SigningClient } from './signing-client'; import { SignerOptions } from './types/signing-client'; -import { OfflineSigner } from './types/wallet'; import { defaultAuth } from './utils'; export class StargateSigningClient extends SigningClient { diff --git a/libs/interchainjs/src/types/wallet.ts b/libs/interchainjs/src/types/wallet.ts index 0e056947..818b60cd 100644 --- a/libs/interchainjs/src/types/wallet.ts +++ b/libs/interchainjs/src/types/wallet.ts @@ -1,5 +1,3 @@ -import { CosmosAccount } from '@interchainjs/cosmos/types'; -import { SignDoc, StdSignDoc } from '@interchainjs/types'; export type Algo = 'secp256k1' | 'ed25519' | 'sr25519'; @@ -15,34 +13,6 @@ export interface StdSignature { signature: string; } -export interface AminoSignResponse { - signed: StdSignDoc; - signature: StdSignature; -} - -export interface OfflineAminoSigner { - getAccounts: () => Promise; - signAmino: ( - signerAddress: string, - signDoc: StdSignDoc - ) => Promise; -} - -export interface DirectSignResponse { - signed: SignDoc; - signature: StdSignature; -} - -export interface OfflineDirectSigner { - getAccounts: () => Promise; - signDirect: ( - signerAddress: string, - signDoc: SignDoc - ) => Promise; -} - -export type OfflineSigner = OfflineAminoSigner | OfflineDirectSigner; - export interface WalletOptions { bip39Password?: string; hdPaths?: string[]; diff --git a/libs/interchainjs/src/wallets/secp256k1.ts b/libs/interchainjs/src/wallets/secp256k1.ts deleted file mode 100644 index a5eef843..00000000 --- a/libs/interchainjs/src/wallets/secp256k1.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { defaultHdPaths } from '@interchainjs/auth/defaults'; -import { Secp256k1Auth } from '@interchainjs/auth/secp256k1'; -import { defaultSignerConfig } from '@interchainjs/cosmos/defaults'; -import { CosmosAccount, CosmosBaseWallet } from '@interchainjs/cosmos/types'; -import { encodeStdSignDoc } from '@interchainjs/cosmos/utils'; -import { SignDoc } from '@interchainjs/cosmos-types/cosmos/tx/v1beta1/tx'; -import { Auth, StdSignDoc } from '@interchainjs/types'; - -import { - Algo, - AminoSignResponse, - Bech32Address, - DirectSignResponse, - OfflineAminoSigner, - OfflineDirectSigner, - WalletOptions, -} from '../types/wallet'; - -const isPublicKeyCompressed = defaultSignerConfig.publicKey.isCompressed; -const toAddress = defaultSignerConfig.publicKey.hash; -const messageHash = defaultSignerConfig.message.hash; -const defaultHdPath = defaultHdPaths.find( - ({ network, algo }) => network === 'cosmos' && algo === 'secp256k1' -)!.path; - -export class Secp256k1Wallet -implements CosmosBaseWallet, OfflineAminoSigner, OfflineDirectSigner -{ - readonly auths: Auth[] = []; - readonly addrs: Bech32Address[] = []; - - constructor(auths: Auth[], prefix: string = 'cosmos') { - this.auths = auths; - this.auths.forEach((auth) => { - this.addrs.push( - toAddress(auth.getPublicKey(isPublicKeyCompressed)).toBech32(prefix) - ); - }); - } - - static fromMnemonic(mnemonic: string, options?: WalletOptions) { - const hdPaths = options?.hdPaths || [defaultHdPath]; - const auths: Auth[] = Secp256k1Auth.fromMnemonic(mnemonic, hdPaths, { - bip39Password: options?.bip39Password, - }); - return new Secp256k1Wallet(auths, options?.prefix); - } - - async getAccountAuths(): Promise< - { - auth: Auth; - account: CosmosAccount; - }[] - > { - const accounts = await this.getAccounts(); - - return accounts.map((account, i) => { - const auth = this.auths[i]; - return { auth, account }; - }); - } - - async getAccounts(): Promise { - const accounts: CosmosAccount[] = []; - this.auths.forEach((auth, i) => { - const address = this.addrs[i]; - const account = { - toAccountData() { - return { - address: address, - algo: auth.algo as Algo, - pubkey: auth.getPublicKey(isPublicKeyCompressed).value, - }; - }, - getAddress() { - return address; - }, - algo: auth.algo as Algo, - publicKey: auth.getPublicKey(isPublicKeyCompressed), - }; - - accounts.push(account); - }); - return accounts; - } - - private getAuthFromBech32Addr(address: string) { - const id = this.addrs.findIndex((addr) => addr === address); - if (id === -1) { - throw new Error('No such signerAddress been authed.'); - } - return this.auths[id]; - } - - async signDirect( - signerAddress: string, - signDoc: SignDoc - ): Promise { - const auth = this.getAuthFromBech32Addr(signerAddress); - const doc = SignDoc.fromPartial(signDoc); - const signature = auth.sign(messageHash(SignDoc.encode(doc).finish())).toCompact(); - return { - signed: doc, - signature: { - pub_key: { - type: 'tendermint/PubKeySecp256k1', - value: { - key: auth.getPublicKey(isPublicKeyCompressed), - }, - }, - signature: signature.toBase64(), - }, - }; - } - - async signAmino( - signerAddress: string, - signDoc: StdSignDoc - ): Promise { - const auth = this.getAuthFromBech32Addr(signerAddress); - const signature = auth - .sign(messageHash(encodeStdSignDoc(signDoc))) - .toCompact(); - return { - signed: signDoc, - signature: { - pub_key: { - type: 'tendermint/PubKeySecp256k1', - value: { - key: auth.getPublicKey(isPublicKeyCompressed), - }, - }, - signature: signature.toBase64(), - }, - }; - } - - toOfflineDirectSigner(): OfflineDirectSigner { - return { - getAccounts: async () => this.getAccounts(), - signDirect: async (signerAddress: string, signDoc: SignDoc) => - this.signDirect(signerAddress, signDoc), - }; - } - - toOfflineAminoSigner(): OfflineAminoSigner { - return { - getAccounts: async () => this.getAccounts(), - signAmino: async (signerAddress: string, signDoc: StdSignDoc) => - this.signAmino(signerAddress, signDoc), - }; - } -} diff --git a/networks/cosmos/src/amino.ts b/networks/cosmos/src/amino.ts index 0f478ea2..7b4d3a5c 100644 --- a/networks/cosmos/src/amino.ts +++ b/networks/cosmos/src/amino.ts @@ -1,16 +1,14 @@ import { Auth, HttpEndpoint } from '@interchainjs/types'; -import { constructAuthsFromWallet } from '@interchainjs/utils'; import { BaseCosmosTxBuilder, CosmosBaseSigner, CosmosDocSigner } from './base'; import { BaseCosmosTxBuilderContext } from './base/builder-context'; import { AminoSigBuilder, AminoTxBuilder } from './builder/amino-tx-builder'; -import { defaultSignerConfig } from './defaults'; import { AminoConverter, CosmosAminoDoc, CosmosAminoSigner, - CosmosBaseWallet, Encoder, + ICosmosWallet, SignerOptions, } from './types'; @@ -84,32 +82,25 @@ export class AminoSigner } static async fromWallet( - wallet: CosmosBaseWallet, + wallet: ICosmosWallet, encoders: Encoder[], converters: AminoConverter[], endpoint?: string | HttpEndpoint, options?: SignerOptions ) { - const [auth] = await constructAuthsFromWallet( - wallet, - options?.publicKey?.isCompressed ?? - defaultSignerConfig.publicKey.isCompressed - ); + const [auth] = (await wallet.getAccounts()).map((acct) => acct.auth); + return new AminoSigner(auth, encoders, converters, endpoint, options); } static async fromWalletToSigners( - wallet: CosmosBaseWallet, + wallet: ICosmosWallet, encoders: Encoder[], converters: AminoConverter[], endpoint?: string | HttpEndpoint, options?: SignerOptions ) { - const auths = await constructAuthsFromWallet( - wallet, - options?.publicKey?.isCompressed ?? - defaultSignerConfig.publicKey.isCompressed - ); + const auths = (await wallet.getAccounts()).map((acct) => acct.auth); return auths.map((auth) => { return new AminoSigner(auth, encoders, converters, endpoint, options); diff --git a/networks/cosmos/src/base/base-signer.ts b/networks/cosmos/src/base/base-signer.ts index 2130d681..387c68df 100644 --- a/networks/cosmos/src/base/base-signer.ts +++ b/networks/cosmos/src/base/base-signer.ts @@ -61,7 +61,7 @@ export abstract class CosmosBaseSigner endpoint?: string | HttpEndpoint, options?: SignerOptions ) { - super(auth, { ...options, ...defaultSignerOptions }); + super(auth, { ...defaultSignerOptions, ...options }); this.encoders = encoders; this.parseAccount = options?.parseAccount ?? defaultSignerOptions.parseAccount; diff --git a/networks/cosmos/src/direct.ts b/networks/cosmos/src/direct.ts index 64f61f4c..f26badb7 100644 --- a/networks/cosmos/src/direct.ts +++ b/networks/cosmos/src/direct.ts @@ -1,15 +1,13 @@ import { Auth, HttpEndpoint } from '@interchainjs/types'; -import { constructAuthsFromWallet } from '@interchainjs/utils'; import { BaseCosmosTxBuilder, CosmosBaseSigner, CosmosDocSigner } from './base'; import { BaseCosmosTxBuilderContext } from './base/builder-context'; import { DirectSigBuilder, DirectTxBuilder } from './builder/direct-tx-builder'; -import { defaultSignerConfig } from './defaults'; import { - CosmosBaseWallet, CosmosDirectDoc, CosmosDirectSigner, Encoder, + ICosmosWallet, SignerOptions, } from './types'; @@ -48,30 +46,22 @@ export class DirectSigner } static async fromWallet( - wallet: CosmosBaseWallet, + wallet: ICosmosWallet, encoders: Encoder[], endpoint?: string | HttpEndpoint, options?: SignerOptions ) { - const [auth] = await constructAuthsFromWallet( - wallet, - options?.publicKey?.isCompressed ?? - defaultSignerConfig.publicKey.isCompressed - ); + const [auth] = (await wallet.getAccounts()).map((acct) => acct.auth); return new DirectSigner(auth, encoders, endpoint, options); } static async fromWalletToSigners( - wallet: CosmosBaseWallet, + wallet: ICosmosWallet, encoders: Encoder[], endpoint?: string | HttpEndpoint, options?: SignerOptions ) { - const auths = await constructAuthsFromWallet( - wallet, - options?.publicKey?.isCompressed ?? - defaultSignerConfig.publicKey.isCompressed - ); + const auths = (await wallet.getAccounts()).map((acct) => acct.auth); return auths.map((auth) => { return new DirectSigner(auth, encoders, endpoint, options); diff --git a/networks/cosmos/src/types/signer.ts b/networks/cosmos/src/types/signer.ts index 18b969ec..a5764eb1 100644 --- a/networks/cosmos/src/types/signer.ts +++ b/networks/cosmos/src/types/signer.ts @@ -9,7 +9,7 @@ import { } from '@interchainjs/cosmos-types/cosmos/tx/v1beta1/tx'; import { Any } from '@interchainjs/cosmos-types/google/protobuf/any'; import { - BaseWalletAccount, + Auth, BroadcastOptions, CreateDocResponse, HttpEndpoint, @@ -19,9 +19,11 @@ import { StdFee, StdSignDoc, UniSigner, - Wallet, } from '@interchainjs/types'; import { Event } from '@interchainjs/types'; +import { Key } from '@interchainjs/utils'; +import { ripemd160 } from '@noble/hashes/ripemd160'; +import { sha256 } from '@noble/hashes/sha256'; export type Algo = 'secp256k1' | 'ed25519' | 'sr25519'; @@ -225,9 +227,43 @@ export interface AccountData { pubkey: Uint8Array; } -export interface CosmosAccount extends BaseWalletAccount { - getAddress(prefix?: string): Bech32Address; +export interface ICosmosAccount { + publicKey: IKey; + address: Bech32Address; + auth: Auth; toAccountData(): AccountData; } -export type CosmosBaseWallet = Wallet; +export function isICosmosAccount(instance: AccountData | ICosmosAccount): instance is ICosmosAccount { + return (instance as ICosmosAccount).toAccountData !== undefined; +} + +export class CosmosAccount implements ICosmosAccount { + constructor( + public prefix: string, + public auth: Auth, + public isPublicKeyCompressed: boolean = true + ) {} + + get publicKey() { + return this.auth.getPublicKey(this.isPublicKeyCompressed); + } + + get address() { + return Key.from(ripemd160(sha256(this.publicKey.value))).toBech32( + this.prefix + ); + } + + toAccountData() { + return { + address: this.address, + algo: this.auth.algo as Algo, + pubkey: this.publicKey.value, + }; + } +} + +export interface ICosmosWallet { + getAccounts(): Promise; +} diff --git a/networks/cosmos/src/types/wallet.ts b/networks/cosmos/src/types/wallet.ts new file mode 100644 index 00000000..cb625ba6 --- /dev/null +++ b/networks/cosmos/src/types/wallet.ts @@ -0,0 +1,51 @@ +import { SignerConfig } from '@interchainjs/types'; + +import { + AccountData, + CosmosAminoDoc, + CosmosDirectDoc, + ICosmosAccount, +} from './signer'; + +export interface WalletOptions { + bip39Password?: string; + signerConfig: SignerConfig; +} + +export interface Pubkey { + type: string; + value: any; +} + +export interface StdSignature { + pub_key: Pubkey; + signature: string; +} + +export interface AminoSignResponse { + signed: CosmosAminoDoc; + signature: StdSignature; +} + +export interface OfflineAminoSigner { + getAccounts: () => Promise<(AccountData | ICosmosAccount)[]>; + signAmino: ( + signerAddress: string, + signDoc: CosmosAminoDoc + ) => Promise; +} + +export interface DirectSignResponse { + signed: CosmosDirectDoc; + signature: StdSignature; +} + +export interface OfflineDirectSigner { + getAccounts: () => Promise<(AccountData | ICosmosAccount)[]>; + signDirect: ( + signerAddress: string, + signDoc: CosmosDirectDoc + ) => Promise; +} + +export type OfflineSigner = OfflineAminoSigner | OfflineDirectSigner; diff --git a/networks/cosmos/src/utils/wallet.ts b/networks/cosmos/src/utils/wallet.ts index eba49355..762c1bdd 100644 --- a/networks/cosmos/src/utils/wallet.ts +++ b/networks/cosmos/src/utils/wallet.ts @@ -2,33 +2,9 @@ import { SignDoc } from '@interchainjs/cosmos-types/cosmos/tx/v1beta1/tx'; import { Auth, SignDocResponse, SignerConfig } from '@interchainjs/types'; import { defaultSignerConfig } from '../defaults'; -import { Algo, CosmosAccount, CosmosAminoDoc } from '../types'; +import { CosmosAminoDoc } from '../types'; import { encodeStdSignDoc } from './amino'; -export function getAccountFromAuth( - auth: Auth, - config: SignerConfig = defaultSignerConfig -): CosmosAccount { - const publicKey = auth.getPublicKey(config.publicKey.isCompressed); - const account = { - algo: auth.algo, - publicKey, - getAddress(prefix?: string) { - const addrKey = config.publicKey.hash(publicKey); - return addrKey.toBech32(prefix ?? ''); - }, - toAccountData() { - return { - address: account.getAddress(), - algo: auth.algo as Algo, - pubkey: publicKey.value, - }; - }, - }; - - return account; -} - export class SignResponseFromAuth { static signDirect( auth: Auth, diff --git a/networks/cosmos/src/wallets/secp256k1hd.ts b/networks/cosmos/src/wallets/secp256k1hd.ts new file mode 100644 index 00000000..fe566cfe --- /dev/null +++ b/networks/cosmos/src/wallets/secp256k1hd.ts @@ -0,0 +1,126 @@ +import { Secp256k1Auth } from '@interchainjs/auth/secp256k1'; +import { AddrDerivation, Auth, SignerConfig } from '@interchainjs/types'; + +import { AminoDocSigner } from '../amino'; +import { defaultSignerConfig } from '../defaults'; +import { DirectDocSigner } from '../direct'; +import { + CosmosAccount, + CosmosAminoDoc, + CosmosDirectDoc, + ICosmosAccount, + ICosmosWallet, +} from '../types'; +import { + AminoSignResponse, + DirectSignResponse, + OfflineAminoSigner, + OfflineDirectSigner, + WalletOptions, +} from '../types/wallet'; + +export class Secp256k1HDWallet +implements ICosmosWallet, OfflineAminoSigner, OfflineDirectSigner +{ + constructor( + public accounts: ICosmosAccount[], + public options: SignerConfig + ) { + this.options = { ...defaultSignerConfig, ...options }; + } + + static fromMnemonic( + mnemonic: string, + derivations: AddrDerivation[], + options?: WalletOptions + ) { + const hdPaths = derivations.map((derivation) => derivation.hdPath); + + const auths: Auth[] = Secp256k1Auth.fromMnemonic(mnemonic, hdPaths, { + bip39Password: options?.bip39Password, + }); + + const accounts = auths.map((auth, i) => { + const derivation = derivations[i]; + return new CosmosAccount(derivation.prefix, auth); + }); + + return new Secp256k1HDWallet(accounts, options.signerConfig); + } + + async getAccounts(): Promise { + return this.accounts; + } + + private getAcctFromBech32Addr(address: string) { + const id = this.accounts.findIndex((acct) => acct.address === address); + if (id === -1) { + throw new Error('No such signerAddress been authed.'); + } + return this.accounts[id]; + } + + async signDirect( + signerAddress: string, + signDoc: CosmosDirectDoc + ): Promise { + const account = this.getAcctFromBech32Addr(signerAddress); + + const docSigner = new DirectDocSigner(account.auth, this.options); + + const resp = await docSigner.signDoc(signDoc); + + return { + signed: signDoc, + signature: { + pub_key: { + type: 'tendermint/PubKeySecp256k1', + value: { + key: account.publicKey.toBase64(), + }, + }, + signature: resp.signature.toBase64(), + }, + }; + } + + async signAmino( + signerAddress: string, + signDoc: CosmosAminoDoc + ): Promise { + const account = this.getAcctFromBech32Addr(signerAddress); + + const docSigner = new AminoDocSigner(account.auth, this.options); + + const resp = await docSigner.signDoc(signDoc); + + return { + signed: signDoc, + signature: { + pub_key: { + type: 'tendermint/PubKeySecp256k1', + value: { + key: account.publicKey.toBase64(), + }, + }, + signature: resp.signature.toBase64(), + }, + }; + } + + toOfflineDirectSigner(): OfflineDirectSigner { + return { + getAccounts: async () => this.getAccounts(), + signDirect: async (signerAddress: string, signDoc: CosmosDirectDoc) => + this.signDirect(signerAddress, signDoc), + }; + } + + toOfflineAminoSigner(): OfflineAminoSigner { + return { + getAccounts: async () => this.getAccounts(), + signAmino: async (signerAddress: string, signDoc: CosmosAminoDoc) => + this.signAmino(signerAddress, signDoc), + }; + } +} diff --git a/packages/types/src/wallet.ts b/packages/types/src/wallet.ts index 2766bb50..048151d8 100644 --- a/packages/types/src/wallet.ts +++ b/packages/types/src/wallet.ts @@ -21,3 +21,8 @@ export interface Wallet { } export type BaseWallet = Wallet; + +export interface AddrDerivation { + readonly hdPath: string; + readonly prefix: string; +} From f6f606182d38d443c35544f66a7a97bcceb9d708 Mon Sep 17 00:00:00 2001 From: Zetazzz Date: Thu, 20 Jun 2024 10:36:06 +0800 Subject: [PATCH 4/5] fix cosmos tests --- networks/cosmos/src/types/signer.ts | 6 ++++-- networks/cosmos/src/types/wallet.ts | 11 +++-------- networks/cosmos/src/wallets/secp256k1hd.ts | 2 +- .../cosmos/starship/__tests__/staking.test.ts | 19 ++++++++++++------- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/networks/cosmos/src/types/signer.ts b/networks/cosmos/src/types/signer.ts index a5764eb1..c1d96119 100644 --- a/networks/cosmos/src/types/signer.ts +++ b/networks/cosmos/src/types/signer.ts @@ -234,7 +234,9 @@ export interface ICosmosAccount { toAccountData(): AccountData; } -export function isICosmosAccount(instance: AccountData | ICosmosAccount): instance is ICosmosAccount { +export function isICosmosAccount( + instance: AccountData | ICosmosAccount +): instance is ICosmosAccount { return (instance as ICosmosAccount).toAccountData !== undefined; } @@ -265,5 +267,5 @@ export class CosmosAccount implements ICosmosAccount { } export interface ICosmosWallet { - getAccounts(): Promise; + getAccounts: () => Promise; } diff --git a/networks/cosmos/src/types/wallet.ts b/networks/cosmos/src/types/wallet.ts index cb625ba6..914a554c 100644 --- a/networks/cosmos/src/types/wallet.ts +++ b/networks/cosmos/src/types/wallet.ts @@ -1,11 +1,6 @@ import { SignerConfig } from '@interchainjs/types'; -import { - AccountData, - CosmosAminoDoc, - CosmosDirectDoc, - ICosmosAccount, -} from './signer'; +import { CosmosAminoDoc, CosmosDirectDoc, ICosmosAccount } from './signer'; export interface WalletOptions { bip39Password?: string; @@ -28,7 +23,7 @@ export interface AminoSignResponse { } export interface OfflineAminoSigner { - getAccounts: () => Promise<(AccountData | ICosmosAccount)[]>; + getAccounts: () => Promise; signAmino: ( signerAddress: string, signDoc: CosmosAminoDoc @@ -41,7 +36,7 @@ export interface DirectSignResponse { } export interface OfflineDirectSigner { - getAccounts: () => Promise<(AccountData | ICosmosAccount)[]>; + getAccounts: () => Promise; signDirect: ( signerAddress: string, signDoc: CosmosDirectDoc diff --git a/networks/cosmos/src/wallets/secp256k1hd.ts b/networks/cosmos/src/wallets/secp256k1hd.ts index fe566cfe..b301f2a0 100644 --- a/networks/cosmos/src/wallets/secp256k1hd.ts +++ b/networks/cosmos/src/wallets/secp256k1hd.ts @@ -45,7 +45,7 @@ implements ICosmosWallet, OfflineAminoSigner, OfflineDirectSigner return new CosmosAccount(derivation.prefix, auth); }); - return new Secp256k1HDWallet(accounts, options.signerConfig); + return new Secp256k1HDWallet(accounts, options?.signerConfig); } async getAccounts(): Promise { diff --git a/networks/cosmos/starship/__tests__/staking.test.ts b/networks/cosmos/starship/__tests__/staking.test.ts index dac6c121..4121c434 100644 --- a/networks/cosmos/starship/__tests__/staking.test.ts +++ b/networks/cosmos/starship/__tests__/staking.test.ts @@ -2,11 +2,12 @@ import './setup.test'; import { ChainInfo } from '@chain-registry/client'; import { DirectSigner } from '@interchainjs/cosmos/direct'; -import { CosmosBaseWallet } from '@interchainjs/cosmos/types/signer'; +import { ICosmosWallet } from '@interchainjs/cosmos/types/signer'; import { assertIsDeliverTxSuccess, toEncoders, } from '@interchainjs/cosmos/utils'; +import { Secp256k1HDWallet } from '@interchainjs/cosmos/wallets/secp256k1hd'; import { BondStatus, bondStatusToJSON, @@ -14,13 +15,14 @@ import { import { MsgDelegate } from '@interchainjs/cosmos-types/cosmos/staking/v1beta1/tx'; import { BigNumber } from 'bignumber.js'; // Using `fromWallet` to construct Signer import { RpcQuery } from 'interchainjs/query/rpc'; -import { Secp256k1Wallet } from 'interchainjs/wallets/secp256k1'; import { useChain } from 'starshipjs'; import { generateMnemonic } from '../src'; +const cosmosHdPath = "m/44'/118'/0'/0/0"; + describe('Staking tokens testing', () => { - let directWallet: CosmosBaseWallet, denom: string, address: string; + let directWallet: ICosmosWallet, denom: string, address: string; let chainInfo: ChainInfo, getCoin, getRpcEndpoint: () => string, @@ -39,11 +41,14 @@ describe('Staking tokens testing', () => { const mnemonic = generateMnemonic(); const prefix = chainInfo.chain.bech32_prefix; - directWallet = Secp256k1Wallet.fromMnemonic(mnemonic, { - prefix, - }); + directWallet = await Secp256k1HDWallet.fromMnemonic(mnemonic, [ + { + hdPath: cosmosHdPath, + prefix, + }, + ]); - address = (await directWallet.getAccounts())[0].getAddress(prefix); + address = (await directWallet.getAccounts())[0].address; // (await directWallet.getAccount()).getAddress(prefix) as string; // Create custom cosmos interchain client From 81b2262dc70177c40c9330dd4c34a694aac57b27 Mon Sep 17 00:00:00 2001 From: Zetazzz Date: Thu, 20 Jun 2024 15:56:44 +0800 Subject: [PATCH 5/5] fix tests in libs interchainjs --- .../starship/__tests__/gov.test.ts | 41 ++++++++++----- .../starship/__tests__/staking.test.ts | 22 +++++--- .../starship/__tests__/token.test.ts | 50 ++++++++++++------- networks/cosmos/src/base/base-signer.ts | 7 +++ 4 files changed, 82 insertions(+), 38 deletions(-) diff --git a/libs/interchainjs/starship/__tests__/gov.test.ts b/libs/interchainjs/starship/__tests__/gov.test.ts index d1ef6f50..7b83117d 100644 --- a/libs/interchainjs/starship/__tests__/gov.test.ts +++ b/libs/interchainjs/starship/__tests__/gov.test.ts @@ -2,6 +2,11 @@ import './setup.test'; import { generateMnemonic } from '@confio/relayer/build/lib/helpers'; import { assertIsDeliverTxSuccess } from '@cosmjs/stargate'; +import { + OfflineAminoSigner, + OfflineDirectSigner, +} from '@interchainjs/cosmos/types/wallet'; +import { Secp256k1HDWallet } from '@interchainjs/cosmos/wallets/secp256k1hd'; import { ProposalStatus, TextProposal, @@ -20,19 +25,23 @@ import { fromBase64, toUtf8 } from '@interchainjs/utils'; import { BigNumber } from 'bignumber.js'; import { RpcQuery } from 'interchainjs/query/rpc'; import { StargateSigningClient } from 'interchainjs/stargate'; -import { OfflineAminoSigner, OfflineDirectSigner } from 'interchainjs/types'; -import { Secp256k1Wallet } from 'interchainjs/wallets/secp256k1'; import { useChain } from 'starshipjs'; import { waitUntil } from '../src'; +const cosmosHdPath = "m/44'/118'/0'/0/0"; + describe('Governance tests for osmosis', () => { let directSigner: OfflineDirectSigner, aminoSigner: OfflineAminoSigner, denom: string, directAddress: string, aminoAddress: string; - let chainInfo, getCoin, getRpcEndpoint: () => string, creditFromFaucet; + let commonPrefix: string, + chainInfo, + getCoin, + getRpcEndpoint: () => string, + creditFromFaucet; // Variables used accross testcases let queryClient: RpcQuery; @@ -44,19 +53,25 @@ describe('Governance tests for osmosis', () => { useChain('osmosis')); denom = getCoin().base; + commonPrefix = chainInfo?.chain?.bech32_prefix; + // Initialize wallet - const directWallet = Secp256k1Wallet.fromMnemonic(generateMnemonic(), { - prefix: chainInfo.chain.bech32_prefix, - }); - const aminoWallet = Secp256k1Wallet.fromMnemonic(generateMnemonic(), { - prefix: chainInfo.chain.bech32_prefix, - }); + const directWallet = Secp256k1HDWallet.fromMnemonic(generateMnemonic(), [ + { + prefix: commonPrefix, + hdPath: cosmosHdPath, + }, + ]); + const aminoWallet = Secp256k1HDWallet.fromMnemonic(generateMnemonic(), [ + { + prefix: commonPrefix, + hdPath: cosmosHdPath, + }, + ]); directSigner = directWallet.toOfflineDirectSigner(); aminoSigner = aminoWallet.toOfflineAminoSigner(); - directAddress = ( - await directSigner.getAccounts() - )[0].getAddress() as string; - aminoAddress = (await aminoSigner.getAccounts())[0].getAddress() as string; + directAddress = (await directSigner.getAccounts())[0].address; + aminoAddress = (await aminoSigner.getAccounts())[0].address; // Create custom cosmos interchain client queryClient = new RpcQuery(getRpcEndpoint()); diff --git a/libs/interchainjs/starship/__tests__/staking.test.ts b/libs/interchainjs/starship/__tests__/staking.test.ts index 23f7fbe5..e1b2ed42 100644 --- a/libs/interchainjs/starship/__tests__/staking.test.ts +++ b/libs/interchainjs/starship/__tests__/staking.test.ts @@ -4,6 +4,8 @@ import { ChainInfo } from '@chain-registry/client'; import { Asset } from '@chain-registry/types'; import { generateMnemonic } from '@confio/relayer/build/lib/helpers'; import { assertIsDeliverTxSuccess } from '@cosmjs/stargate'; +import { OfflineDirectSigner } from '@interchainjs/cosmos/types/wallet'; +import { Secp256k1HDWallet } from '@interchainjs/cosmos/wallets/secp256k1hd'; import { BondStatus, bondStatusToJSON, @@ -12,13 +14,14 @@ import { MsgDelegate } from '@interchainjs/cosmos-types/cosmos/staking/v1beta1/t import BigNumber from 'bignumber.js'; import { RpcQuery } from 'interchainjs/query/rpc'; import { StargateSigningClient } from 'interchainjs/stargate'; -import { OfflineDirectSigner } from 'interchainjs/types'; -import { Secp256k1Wallet } from 'interchainjs/wallets/secp256k1'; import { useChain } from 'starshipjs'; +const cosmosHdPath = "m/44'/118'/0'/0/0"; + describe('Staking tokens testing', () => { let protoSigner: OfflineDirectSigner, denom: string, address: string; - let chainInfo: ChainInfo, + let commonPrefix: string, + chainInfo: ChainInfo, getCoin: () => Asset, getRpcEndpoint: () => string, creditFromFaucet: (address: string, denom?: string | null) => Promise; @@ -33,13 +36,18 @@ describe('Staking tokens testing', () => { useChain('osmosis')); denom = getCoin().base; + commonPrefix = chainInfo?.chain?.bech32_prefix; + const mnemonic = generateMnemonic(); // Initialize wallet - const wallet = Secp256k1Wallet.fromMnemonic(mnemonic, { - prefix: chainInfo.chain.bech32_prefix, - }); + const wallet = Secp256k1HDWallet.fromMnemonic(mnemonic, [ + { + prefix: commonPrefix, + hdPath: cosmosHdPath, + }, + ]); protoSigner = wallet.toOfflineDirectSigner(); - address = (await protoSigner.getAccounts())[0].getAddress() as string; + address = (await protoSigner.getAccounts())[0].address; // Create custom cosmos interchain client queryClient = new RpcQuery(getRpcEndpoint()); diff --git a/libs/interchainjs/starship/__tests__/token.test.ts b/libs/interchainjs/starship/__tests__/token.test.ts index 40bdc217..5a5229ba 100644 --- a/libs/interchainjs/starship/__tests__/token.test.ts +++ b/libs/interchainjs/starship/__tests__/token.test.ts @@ -4,16 +4,19 @@ import { ChainInfo } from '@chain-registry/client'; import { Asset } from '@chain-registry/types'; import { generateMnemonic } from '@confio/relayer/build/lib/helpers'; import { assertIsDeliverTxSuccess } from '@cosmjs/stargate'; +import { OfflineDirectSigner } from '@interchainjs/cosmos/types/wallet'; +import { Secp256k1HDWallet } from '@interchainjs/cosmos/wallets/secp256k1hd'; import { MsgTransfer } from '@interchainjs/cosmos-types/ibc/applications/transfer/v1/tx'; import { RpcQuery } from 'interchainjs/query/rpc'; import { StargateSigningClient } from 'interchainjs/stargate'; -import { OfflineDirectSigner } from 'interchainjs/types'; -import { Secp256k1Wallet } from 'interchainjs/wallets/secp256k1'; import { useChain } from 'starshipjs'; +const cosmosHdPath = "m/44'/118'/0'/0/0"; + describe('Token transfers', () => { let protoSigner: OfflineDirectSigner, denom: string, address: string; - let chainInfo: ChainInfo, + let commonPrefix: string, + chainInfo: ChainInfo, getCoin: () => Asset, getRpcEndpoint: () => string, creditFromFaucet: (address: string, denom?: string | null) => Promise; @@ -24,13 +27,18 @@ describe('Token transfers', () => { useChain('osmosis')); denom = getCoin().base; + commonPrefix = chainInfo?.chain?.bech32_prefix; + const mnemonic = generateMnemonic(); // Initialize wallet - const wallet = Secp256k1Wallet.fromMnemonic(mnemonic, { - prefix: chainInfo.chain.bech32_prefix, - }); + const wallet = Secp256k1HDWallet.fromMnemonic(mnemonic, [ + { + prefix: commonPrefix, + hdPath: cosmosHdPath, + }, + ]); protoSigner = wallet.toOfflineDirectSigner(); - address = (await protoSigner.getAccounts())[0].getAddress() as string; + address = (await protoSigner.getAccounts())[0].address; // Create custom cosmos interchain client queryClient = new RpcQuery(getRpcEndpoint()); @@ -41,10 +49,13 @@ describe('Token transfers', () => { it('send osmosis token to address', async () => { const mnemonic = generateMnemonic(); // Initialize wallet - const wallet2 = Secp256k1Wallet.fromMnemonic(mnemonic, { - prefix: chainInfo.chain.bech32_prefix, - }); - const address2 = (await wallet2.getAccounts())[0].getAddress(); + const wallet2 = Secp256k1HDWallet.fromMnemonic(mnemonic, [ + { + prefix: commonPrefix, + hdPath: cosmosHdPath, + }, + ]); + const address2 = (await wallet2.getAccounts())[0].address; const signingClient = StargateSigningClient.connectWithSigner( getRpcEndpoint(), @@ -90,24 +101,27 @@ describe('Token transfers', () => { useChain('cosmos'); // Initialize wallet address for cosmos chain - const cosmosWallet = Secp256k1Wallet.fromMnemonic(generateMnemonic(), { - prefix: cosmosChainInfo.chain.bech32_prefix, - }); - const cosmosAddress = (await cosmosWallet.getAccounts())[0].getAddress(); + const cosmosWallet = Secp256k1HDWallet.fromMnemonic(generateMnemonic(), [ + { + prefix: cosmosChainInfo.chain.bech32_prefix, + hdPath: cosmosHdPath, + }, + ]); + const cosmosAddress = (await cosmosWallet.getAccounts())[0].address; const ibcInfos = chainInfo.fetcher.getChainIbcData( chainInfo.chain.chain_id ); - const ibcInfo = ibcInfos.find( + const sourceIbcInfo = ibcInfos.find( (i) => i.chain_1.chain_name === chainInfo.chain.chain_id && i.chain_2.chain_name === cosmosChainInfo.chain.chain_id ); - expect(ibcInfo).toBeTruthy(); + expect(sourceIbcInfo).toBeTruthy(); const { port_id: sourcePort, channel_id: sourceChannel } = - ibcInfo!.channels[0].chain_1; + sourceIbcInfo!.channels[0].chain_1; // Transfer osmosis tokens via IBC to cosmos chain const currentTime = Math.floor(Date.now()) * 1000000; diff --git a/networks/cosmos/src/base/base-signer.ts b/networks/cosmos/src/base/base-signer.ts index 387c68df..c6256a23 100644 --- a/networks/cosmos/src/base/base-signer.ts +++ b/networks/cosmos/src/base/base-signer.ts @@ -11,6 +11,7 @@ import { HttpEndpoint, IKey, SignDocResponse, + SignerConfig, SignResponse, } from '@interchainjs/types'; import { assertEmpty, isEmpty } from '@interchainjs/utils'; @@ -32,6 +33,12 @@ import { calculateFee } from '../utils/fee'; import { BaseCosmosSigBuilder, BaseCosmosTxBuilder } from './tx-builder'; export abstract class CosmosDocSigner extends BaseSigner { + constructor(auth: Auth, config: SignerConfig) { + super(auth, config); + + this.txBuilder = this.getTxBuilder(); + } + txBuilder: BaseCosmosSigBuilder; abstract getTxBuilder(): BaseCosmosSigBuilder; async signDoc(doc: SignDoc): Promise> {