From 09ad90c9afd79cfaa27818590ad1c3c7a84db8c9 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 27 Jul 2020 09:34:38 +0200 Subject: [PATCH] Add SigningCosmWasmClient.signAndPost --- CHANGELOG.md | 3 + .../src/signingcosmwasmclient.spec.ts | 42 +++++++- .../cosmwasm/src/signingcosmwasmclient.ts | 101 +++--------------- packages/cosmwasm/src/testutils.spec.ts | 2 + .../cosmwasm/types/signingcosmwasmclient.d.ts | 7 +- 5 files changed, 68 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6231d1ba..d922209f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ - @cosmjs/cosmwasm: Rename `CosmWasmClient.getNonce` method to `.getSequence`. - @cosmjs/cosmwasm: Remove `RestClient` class in favour of new modular `LcdClient` class from @cosmjs/sdk38. +- @cosmjs/cosmwasm: Add `SigningCosmWasmClient.signAndPost` as a mid-level + abstraction between `SigningCosmWasmClient.upload`/`.instantiate`/`.execute` + and `.postTx`. - @cosmjs/sdk38: Rename `CosmosClient.getNonce` method to `.getSequence`. - @cosmjs/sdk38: Remove `RestClient` class in favour of new modular `LcdClient` class. diff --git a/packages/cosmwasm/src/signingcosmwasmclient.spec.ts b/packages/cosmwasm/src/signingcosmwasmclient.spec.ts index 6c249071..7a7a2936 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.spec.ts @@ -1,13 +1,28 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { Sha256 } from "@cosmjs/crypto"; import { toHex } from "@cosmjs/encoding"; -import { AuthExtension, coin, coins, LcdClient, Secp256k1Wallet, setupAuthExtension } from "@cosmjs/sdk38"; +import { + AuthExtension, + coin, + coins, + LcdClient, + MsgDelegate, + Secp256k1Wallet, + setupAuthExtension, +} from "@cosmjs/sdk38"; import { assert } from "@cosmjs/utils"; import { isPostTxFailure, PrivateCosmWasmClient } from "./cosmwasmclient"; import { setupWasmExtension, WasmExtension } from "./lcdapi/wasm"; import { SigningCosmWasmClient, UploadMeta } from "./signingcosmwasmclient"; -import { alice, getHackatom, makeRandomAddress, pendingWithoutWasmd, unused } from "./testutils.spec"; +import { + alice, + getHackatom, + makeRandomAddress, + pendingWithoutWasmd, + unused, + validatorAddress, +} from "./testutils.spec"; const httpUrl = "http://localhost:1317"; @@ -327,4 +342,27 @@ describe("SigningCosmWasmClient", () => { expect(after.balance).toEqual(transferAmount); }); }); + + describe("signAndPost", () => { + it("works", async () => { + pendingWithoutWasmd(); + const wallet = await Secp256k1Wallet.fromMnemonic(alice.mnemonic); + const client = new SigningCosmWasmClient(httpUrl, alice.address0, wallet); + + const msg: MsgDelegate = { + type: "cosmos-sdk/MsgDelegate", + value: { + delegator_address: alice.address0, + validator_address: validatorAddress, + amount: coin(1234, "ustake"), + }, + }; + const fee = { + amount: coins(2000, "ucosm"), + gas: "120000", // 120k + }; + const result = await client.signAndPost([msg], fee, "Use your power wisely"); + assert(!isPostTxFailure(result)); + }); + }); }); diff --git a/packages/cosmwasm/src/signingcosmwasmclient.ts b/packages/cosmwasm/src/signingcosmwasmclient.ts index 175176f6..c6fd17cc 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.ts @@ -7,6 +7,7 @@ import { Coin, coins, makeSignBytes, + Msg, MsgSend, OfflineSigner, StdFee, @@ -219,19 +220,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { builder: builder, }, }; - const fee = this.fees.upload; - const { accountNumber, sequence } = await this.getSequence(); - const chainId = await this.getChainId(); - const signBytes = makeSignBytes([storeCodeMsg], fee, chainId, memo, accountNumber, sequence); - const signature = await this.signer.sign(this.senderAddress, signBytes); - const signedTx: StdTx = { - msg: [storeCodeMsg], - fee: fee, - memo: memo, - signatures: [signature], - }; - - const result = await this.postTx(signedTx); + const result = await this.signAndPost([storeCodeMsg], this.fees.upload, memo); if (isPostTxFailure(result)) { throw new Error(createPostTxErrorMessage(result)); } @@ -264,21 +253,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { admin: options.admin, }, }; - const memo = options.memo || ""; - const fee = this.fees.init; - const { accountNumber, sequence } = await this.getSequence(); - const chainId = await this.getChainId(); - const signBytes = makeSignBytes([instantiateMsg], fee, chainId, memo, accountNumber, sequence); - - const signature = await this.signer.sign(this.senderAddress, signBytes); - const signedTx: StdTx = { - msg: [instantiateMsg], - fee: fee, - memo: memo, - signatures: [signature], - }; - - const result = await this.postTx(signedTx); + const result = await this.signAndPost([instantiateMsg], this.fees.init, options.memo); if (isPostTxFailure(result)) { throw new Error(createPostTxErrorMessage(result)); } @@ -299,19 +274,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { new_admin: newAdmin, }, }; - const fee = this.fees.changeAdmin; - const { accountNumber, sequence } = await this.getSequence(); - const chainId = await this.getChainId(); - const signBytes = makeSignBytes([updateAdminMsg], fee, chainId, memo, accountNumber, sequence); - const signature = await this.signer.sign(this.senderAddress, signBytes); - const signedTx: StdTx = { - msg: [updateAdminMsg], - fee: fee, - memo: memo, - signatures: [signature], - }; - - const result = await this.postTx(signedTx); + const result = await this.signAndPost([updateAdminMsg], this.fees.changeAdmin, memo); if (isPostTxFailure(result)) { throw new Error(createPostTxErrorMessage(result)); } @@ -329,19 +292,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { contract: contractAddress, }, }; - const fee = this.fees.changeAdmin; - const { accountNumber, sequence } = await this.getSequence(); - const chainId = await this.getChainId(); - const signBytes = makeSignBytes([clearAdminMsg], fee, chainId, memo, accountNumber, sequence); - const signature = await this.signer.sign(this.senderAddress, signBytes); - const signedTx: StdTx = { - msg: [clearAdminMsg], - fee: fee, - memo: memo, - signatures: [signature], - }; - - const result = await this.postTx(signedTx); + const result = await this.signAndPost([clearAdminMsg], this.fees.changeAdmin, memo); if (isPostTxFailure(result)) { throw new Error(createPostTxErrorMessage(result)); } @@ -366,19 +317,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { msg: migrateMsg, }, }; - const fee = this.fees.migrate; - const { accountNumber, sequence } = await this.getSequence(); - const chainId = await this.getChainId(); - const signBytes = makeSignBytes([msg], fee, chainId, memo, accountNumber, sequence); - const signature = await this.signer.sign(this.senderAddress, signBytes); - const signedTx: StdTx = { - msg: [msg], - fee: fee, - memo: memo, - signatures: [signature], - }; - - const result = await this.postTx(signedTx); + const result = await this.signAndPost([msg], this.fees.migrate, memo); if (isPostTxFailure(result)) { throw new Error(createPostTxErrorMessage(result)); } @@ -403,19 +342,7 @@ export class SigningCosmWasmClient extends CosmWasmClient { sent_funds: transferAmount || [], }, }; - const fee = this.fees.exec; - const { accountNumber, sequence } = await this.getSequence(); - const chainId = await this.getChainId(); - const signBytes = makeSignBytes([executeMsg], fee, chainId, memo, accountNumber, sequence); - const signature = await this.signer.sign(this.senderAddress, signBytes); - const signedTx: StdTx = { - msg: [executeMsg], - fee: fee, - memo: memo, - signatures: [signature], - }; - - const result = await this.postTx(signedTx); + const result = await this.signAndPost([executeMsg], this.fees.exec, memo); if (isPostTxFailure(result)) { throw new Error(createPostTxErrorMessage(result)); } @@ -438,18 +365,24 @@ export class SigningCosmWasmClient extends CosmWasmClient { amount: transferAmount, }, }; - const fee = this.fees.send; + return this.signAndPost([sendMsg], this.fees.send, memo); + } + + /** + * Gets account number and sequence from the API, creates a sign doc, + * creates a single signature, assembles the signed transaction and broadcasts it. + */ + public async signAndPost(msgs: readonly Msg[], fee: StdFee, memo = ""): Promise { const { accountNumber, sequence } = await this.getSequence(); const chainId = await this.getChainId(); - const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence); + const signBytes = makeSignBytes(msgs, fee, chainId, memo, accountNumber, sequence); const signature = await this.signer.sign(this.senderAddress, signBytes); const signedTx: StdTx = { - msg: [sendMsg], + msg: msgs, fee: fee, memo: memo, signatures: [signature], }; - return this.postTx(signedTx); } } diff --git a/packages/cosmwasm/src/testutils.spec.ts b/packages/cosmwasm/src/testutils.spec.ts index 5cfe24a4..c7ed6518 100644 --- a/packages/cosmwasm/src/testutils.spec.ts +++ b/packages/cosmwasm/src/testutils.spec.ts @@ -47,6 +47,8 @@ export const wasmd = { chainId: "testing", }; +export const validatorAddress = "cosmosvaloper1gjvanqxc774u6ed9thj4gpn9gj5zus5u32enqn"; + export const alice = { mnemonic: "enlist hip relief stomach skate base shallow young switch frequent cry park", pubkey0: { diff --git a/packages/cosmwasm/types/signingcosmwasmclient.d.ts b/packages/cosmwasm/types/signingcosmwasmclient.d.ts index 6825aaa5..5c3e0fc4 100644 --- a/packages/cosmwasm/types/signingcosmwasmclient.d.ts +++ b/packages/cosmwasm/types/signingcosmwasmclient.d.ts @@ -1,4 +1,4 @@ -import { BroadcastMode, Coin, OfflineSigner, StdFee, StdSignature } from "@cosmjs/sdk38"; +import { BroadcastMode, Coin, Msg, OfflineSigner, StdFee, StdSignature } from "@cosmjs/sdk38"; import { Account, CosmWasmClient, GetSequenceResult, PostTxResult } from "./cosmwasmclient"; import { Log } from "./logs"; export interface SigningCallback { @@ -124,4 +124,9 @@ export declare class SigningCosmWasmClient extends CosmWasmClient { transferAmount?: readonly Coin[], ): Promise; sendTokens(recipientAddress: string, transferAmount: readonly Coin[], memo?: string): Promise; + /** + * Gets account number and sequence from the API, creates a sign doc, + * creates a single signature, assembles the signed transaction and broadcasts it. + */ + signAndPost(msgs: readonly Msg[], fee: StdFee, memo?: string): Promise; }