diff --git a/CHANGELOG.md b/CHANGELOG.md index 3565f11f..e9b9f68e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,20 @@ `isSearchBySentFromOrToQuery` and `isSearchByTagsQuery`. - @cosmjs/launchpad: Change type of `TxsResponse.logs` and `BroadcastTxsResponse.logs` to `unknown[]`. +- @cosmjs/launchpad: Export `StdSignDoc` and create helpers to make and + serialize a `StdSignDoc`: `makeSignDoc` and `serializeSignDoc`. +- @cosmjs/launchpad: Let `OfflineSigner.sign` take an `StdSignDoc` instead of an + encoded message and return a `SignResponse` that includes the document which + was signed. +- @cosmjs/launchpad: Remove `PrehashType` and the prehash type argument in + `OfflineSigner.sign` because the signer now needs to know how to serialize an + `StdSignDoc`. +- @cosmjs/launchpad: Remove `makeSignBytes` in favour of `makeSignDoc` and + `serializeSignDoc`. +- @cosmjs/launchpad: Create `WrappedTx`, `WrappedStdTx` and `isWrappedStdTx` to + better represent the Amino tx interface. Deprecate `CosmosSdkTx`, which is an + alias for `WrappedStdTx`. +- @cosmjs/launchpad: Add `makeStdTx` to create an `StdTx`. - @cosmjs/launchpad-ledger: Add package supporting Ledger device integration for Launchpad. Two new classes are provided: `LedgerSigner` (for most use cases) and `LaunchpadLedger` for more fine-grained access. diff --git a/packages/cli/README.md b/packages/cli/README.md index 67fdef44..47818805 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -66,7 +66,7 @@ const sendTokensMsg: MsgSend = { }, }; -const signBytes = makeSignBytes( +const signDoc = makeSignDoc( [sendTokensMsg], defaultFee, defaultNetworkId, @@ -74,13 +74,8 @@ const signBytes = makeSignBytes( account_number, sequence, ); -const signature = await pen.sign(signBytes); -const signedTx: StdTx = { - msg: [sendTokensMsg], - fee: defaultFee, - memo: memo, - signatures: [signature], -}; +const { signed, signature } = await wallet.sign(faucetAddress, signDoc); +const signedTx = makeStdTx(signed, signature); const broadcastResult = await client.broadcastTx(signedTx); ``` diff --git a/packages/cli/examples/delegate.ts b/packages/cli/examples/delegate.ts index 51892dee..edcdfcfb 100644 --- a/packages/cli/examples/delegate.ts +++ b/packages/cli/examples/delegate.ts @@ -27,14 +27,9 @@ console.log("Connected to chain:", chainId); const { accountNumber, sequence } = await client.getSequence(senderAddress); console.log("Account/sequence:", accountNumber, sequence); -const signBytes = makeSignBytes([msg], fee, chainId, memo, accountNumber, sequence); -const signature = await wallet.sign(senderAddress, signBytes); -const signedTx: StdTx = { - msg: [msg], - fee: fee, - memo: memo, - signatures: [signature], -}; +const signDoc = makeSignDoc([msg], fee, chainId, memo, accountNumber, sequence); +const { signed, signature } = await wallet.sign(senderAddress, signDoc); +const signedTx = makeStdTx(signed, signature); const result = await client.broadcastTx(signedTx); console.log("Broadcast result:", result); diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index b338bce1..cc3ad5e9 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -98,7 +98,8 @@ export async function main(originalArgs: readonly string[]): Promise { "encodeSecp256k1Signature", "logs", "makeCosmoshubPath", - "makeSignBytes", + "makeSignDoc", + "makeStdTx", "IndexedTx", "BroadcastTxResult", "Coin", @@ -115,6 +116,7 @@ export async function main(originalArgs: readonly string[]): Promise { "Secp256k1Wallet", "SigningCosmosClient", "StdFee", + "StdSignDoc", "StdTx", ], ], @@ -161,7 +163,13 @@ export async function main(originalArgs: readonly string[]): Promise { const wallet = await Secp256k1Wallet.fromMnemonic(mnemonic, makeCosmoshubPath(0)); const [{ address }] = await wallet.getAccounts(); const data = toAscii("foo bar"); - const signature = await wallet.sign(address, data); + const fee: StdFee = { + amount: coins(5000000, "ucosm"), + gas: "89000000", + }; + const signDoc = makeSignDoc([], fee, "chain-xyz", "hello, world", 1, 2); + const { signed, signature } = await wallet.sign(address, signDoc); + assert(signed.memo === "hello, world"); const bechPubkey = "coralvalconspub1zcjduepqvxg72ccnl9r65fv0wn3amlk4sfzqfe2k36l073kjx2qyaf6sk23qw7j8wq"; assert(encodeBech32Pubkey(decodeBech32Pubkey(bechPubkey), "coralvalconspub") == bechPubkey); diff --git a/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts b/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts index b9d6d359..9c386d77 100644 --- a/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts +++ b/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts @@ -2,13 +2,14 @@ import { Coin, coins, - CosmosSdkTx, isBroadcastTxFailure, isMsgSend, LcdClient, - makeSignBytes, + makeSignDoc, + makeStdTx, MsgSend, Secp256k1Wallet, + WrappedStdTx, } from "@cosmjs/launchpad"; import { assert, sleep } from "@cosmjs/utils"; @@ -30,7 +31,7 @@ interface TestTxSend { readonly recipient: string; readonly hash: string; readonly height: number; - readonly tx: CosmosSdkTx; + readonly tx: WrappedStdTx; } interface TestTxExecute { @@ -38,7 +39,7 @@ interface TestTxExecute { readonly contract: string; readonly hash: string; readonly height: number; - readonly tx: CosmosSdkTx; + readonly tx: WrappedStdTx; } describe("CosmWasmClient.searchTx", () => { @@ -103,16 +104,11 @@ describe("CosmWasmClient.searchTx", () => { }; const { accountNumber, sequence } = await client.getSequence(); const chainId = await client.getChainId(); - const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence); - const signature = await wallet.sign(alice.address0, signBytes); - const tx: CosmosSdkTx = { + const signDoc = makeSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence); + const { signed, signature } = await wallet.sign(alice.address0, signDoc); + const tx: WrappedStdTx = { type: "cosmos-sdk/StdTx", - value: { - msg: [sendMsg], - fee: fee, - memo: memo, - signatures: [signature], - }, + value: makeStdTx(signed, signature), }; const transactionId = await client.getIdentifier(tx); const result = await client.broadcastTx(tx.value); diff --git a/packages/cosmwasm/src/cosmwasmclient.spec.ts b/packages/cosmwasm/src/cosmwasmclient.spec.ts index 868aa02a..1e1143d1 100644 --- a/packages/cosmwasm/src/cosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/cosmwasmclient.spec.ts @@ -3,7 +3,9 @@ import { Sha256 } from "@cosmjs/crypto"; import { Bech32, fromHex, fromUtf8, toAscii, toBase64 } from "@cosmjs/encoding"; import { assertIsBroadcastTxSuccess, - makeSignBytes, + isWrappedStdTx, + makeSignDoc, + makeStdTx, MsgSend, Secp256k1Wallet, StdFee, @@ -199,6 +201,7 @@ describe("CosmWasmClient", () => { it("works", async () => { pendingWithoutWasmd(); const client = new CosmWasmClient(wasmd.endpoint); + assert(isWrappedStdTx(cosmoshub.tx)); expect(await client.getIdentifier(cosmoshub.tx)).toEqual(cosmoshub.id); }); }); @@ -236,14 +239,9 @@ describe("CosmWasmClient", () => { const chainId = await client.getChainId(); const { accountNumber, sequence } = await client.getSequence(alice.address0); - const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence); - const signature = await wallet.sign(alice.address0, signBytes); - const signedTx = { - msg: [sendMsg], - fee: fee, - memo: memo, - signatures: [signature], - }; + const signDoc = makeSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence); + const { signed, signature } = await wallet.sign(alice.address0, signDoc); + const signedTx = makeStdTx(signed, signature); const result = await client.broadcastTx(signedTx); assertIsBroadcastTxSuccess(result); const { logs, transactionHash } = result; diff --git a/packages/cosmwasm/src/cosmwasmclient.ts b/packages/cosmwasm/src/cosmwasmclient.ts index 4541ed86..f2b26ab0 100644 --- a/packages/cosmwasm/src/cosmwasmclient.ts +++ b/packages/cosmwasm/src/cosmwasmclient.ts @@ -5,7 +5,6 @@ import { BroadcastMode, BroadcastTxResult, Coin, - CosmosSdkTx, IndexedTx, LcdClient, normalizePubkey, @@ -13,6 +12,7 @@ import { setupAuthExtension, StdTx, uint64ToNumber, + WrappedStdTx, } from "@cosmjs/launchpad"; import { Uint53 } from "@cosmjs/math"; @@ -199,7 +199,7 @@ export class CosmWasmClient { /** * Returns a 32 byte upper-case hex transaction hash (typically used as the transaction ID) */ - public async getIdentifier(tx: CosmosSdkTx): Promise { + public async getIdentifier(tx: WrappedStdTx): Promise { // We consult the REST API because we don't have a local amino encoder const response = await this.lcdClient.encodeTx(tx); const hash = new Sha256(fromBase64(response.tx)).digest(); diff --git a/packages/cosmwasm/src/lcdapi/wasm.spec.ts b/packages/cosmwasm/src/lcdapi/wasm.spec.ts index 0614d990..459ecdf7 100644 --- a/packages/cosmwasm/src/lcdapi/wasm.spec.ts +++ b/packages/cosmwasm/src/lcdapi/wasm.spec.ts @@ -10,7 +10,8 @@ import { coin, coins, LcdClient, - makeSignBytes, + makeSignDoc, + makeStdTx, OfflineSigner, Secp256k1Wallet, setupAuthExtension, @@ -36,7 +37,6 @@ import { fromOneElementArray, getHackatom, makeRandomAddress, - makeSignedTx, pendingWithoutWasmd, wasmd, wasmdEnabled, @@ -125,9 +125,9 @@ async function executeContract( }; const { account_number, sequence } = (await client.auth.account(alice.address0)).result.value; - const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); - const signature = await signer.sign(alice.address0, signBytes); - const signedTx = makeSignedTx(theMsg, fee, memo, signature); + const signDoc = makeSignDoc([theMsg], fee, wasmd.chainId, memo, account_number, sequence); + const { signed, signature } = await signer.sign(alice.address0, signDoc); + const signedTx = makeStdTx(signed, signature); return client.broadcastTx(signedTx); } diff --git a/packages/cosmwasm/src/signingcosmwasmclient.ts b/packages/cosmwasm/src/signingcosmwasmclient.ts index d0fff774..1930deb7 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.ts @@ -11,12 +11,12 @@ import { GasLimits, GasPrice, isBroadcastTxFailure, - makeSignBytes, + makeSignDoc, + makeStdTx, Msg, MsgSend, OfflineSigner, StdFee, - StdTx, } from "@cosmjs/launchpad"; import { Uint53 } from "@cosmjs/math"; import pako from "pako"; @@ -360,14 +360,9 @@ export class SigningCosmWasmClient extends CosmWasmClient { public async signAndBroadcast(msgs: readonly Msg[], fee: StdFee, memo = ""): Promise { const { accountNumber, sequence } = await this.getSequence(); const chainId = await this.getChainId(); - const signBytes = makeSignBytes(msgs, fee, chainId, memo, accountNumber, sequence); - const signature = await this.signer.sign(this.senderAddress, signBytes); - const signedTx: StdTx = { - msg: msgs, - fee: fee, - memo: memo, - signatures: [signature], - }; + const signDoc = makeSignDoc(msgs, fee, chainId, memo, accountNumber, sequence); + const { signed, signature } = await this.signer.sign(this.senderAddress, signDoc); + const signedTx = makeStdTx(signed, signature); return this.broadcastTx(signedTx); } } diff --git a/packages/cosmwasm/src/testutils.spec.ts b/packages/cosmwasm/src/testutils.spec.ts index 765d2e8a..fd780bf6 100644 --- a/packages/cosmwasm/src/testutils.spec.ts +++ b/packages/cosmwasm/src/testutils.spec.ts @@ -1,6 +1,5 @@ import { Random } from "@cosmjs/crypto"; import { Bech32, fromBase64 } from "@cosmjs/encoding"; -import { Msg, StdFee, StdSignature, StdTx } from "@cosmjs/launchpad"; import hackatom from "./testdata/contract.json"; @@ -89,12 +88,3 @@ export function fromOneElementArray(elements: ArrayLike): T { if (elements.length !== 1) throw new Error(`Expected exactly one element but got ${elements.length}`); return elements[0]; } - -export function makeSignedTx(firstMsg: Msg, fee: StdFee, memo: string, firstSignature: StdSignature): StdTx { - return { - msg: [firstMsg], - fee: fee, - memo: memo, - signatures: [firstSignature], - }; -} diff --git a/packages/cosmwasm/types/cosmwasmclient.d.ts b/packages/cosmwasm/types/cosmwasmclient.d.ts index ca145907..bd4f4cb5 100644 --- a/packages/cosmwasm/types/cosmwasmclient.d.ts +++ b/packages/cosmwasm/types/cosmwasmclient.d.ts @@ -3,11 +3,11 @@ import { BroadcastMode, BroadcastTxResult, Coin, - CosmosSdkTx, IndexedTx, LcdClient, PubKey, StdTx, + WrappedStdTx, } from "@cosmjs/launchpad"; import { WasmExtension } from "./lcdapi/wasm"; import { JsonObject } from "./types"; @@ -132,7 +132,7 @@ export declare class CosmWasmClient { /** * Returns a 32 byte upper-case hex transaction hash (typically used as the transaction ID) */ - getIdentifier(tx: CosmosSdkTx): Promise; + getIdentifier(tx: WrappedStdTx): Promise; /** * Returns account number and sequence. * diff --git a/packages/launchpad-ledger/demo/index.html b/packages/launchpad-ledger/demo/index.html index 7a04b1cd..7da23c11 100644 --- a/packages/launchpad-ledger/demo/index.html +++ b/packages/launchpad-ledger/demo/index.html @@ -34,7 +34,7 @@
-
diff --git a/packages/launchpad-ledger/src/demo/node.ts b/packages/launchpad-ledger/src/demo/node.ts index ef3b3bb1..f90e45e4 100644 --- a/packages/launchpad-ledger/src/demo/node.ts +++ b/packages/launchpad-ledger/src/demo/node.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { toBase64 } from "@cosmjs/encoding"; -import { makeCosmoshubPath, makeSignBytes, StdFee, StdSignature } from "@cosmjs/launchpad"; +import { makeCosmoshubPath, makeSignDoc, StdFee, StdSignature } from "@cosmjs/launchpad"; import { LedgerSigner } from "../ledgersigner"; @@ -48,13 +48,7 @@ export async function sign( }, }, ]; - const signBytes = makeSignBytes( - msgs, - defaultFee, - defaultChainId, - defaultMemo, - accountNumber, - defaultSequence, - ); - return signer.sign(fromAddress, signBytes); + const signDoc = makeSignDoc(msgs, defaultFee, defaultChainId, defaultMemo, accountNumber, defaultSequence); + const { signature } = await signer.sign(fromAddress, signDoc); + return signature; } diff --git a/packages/launchpad-ledger/src/demo/web.ts b/packages/launchpad-ledger/src/demo/web.ts index e656a871..c9297c47 100644 --- a/packages/launchpad-ledger/src/demo/web.ts +++ b/packages/launchpad-ledger/src/demo/web.ts @@ -1,5 +1,7 @@ -import { toBase64, toUtf8 } from "@cosmjs/encoding"; -import { AccountData, makeCosmoshubPath } from "@cosmjs/launchpad"; +import { toBase64 } from "@cosmjs/encoding"; +import { AccountData, makeCosmoshubPath, StdSignDoc } from "@cosmjs/launchpad"; +import { Uint53 } from "@cosmjs/math"; +import { assert } from "@cosmjs/utils"; import { LedgerSigner } from "../ledgersigner"; @@ -8,28 +10,37 @@ declare const document: any; let accounts: readonly AccountData[] = []; -function createMessage(accountNumber: number, address: string): string { - return `{ - "account_number": ${accountNumber}, - "chain_id": "testing", - "fee": { - "amount": [{ "amount": 100, "denom": "ucosm" }], - "gas": 250 +function createSignDoc(accountNumber: number, address: string): string { + const signDoc: StdSignDoc = { + // eslint-disable-next-line @typescript-eslint/naming-convention + chain_id: "testing", + // eslint-disable-next-line @typescript-eslint/naming-convention + account_number: `${accountNumber}`, + sequence: "0", + fee: { + amount: [{ amount: "100", denom: "ucosm" }], + gas: "250", }, - "memo": "Some memo", - "msgs": [{ - "type": "cosmos-sdk/MsgSend", - "value": { - "amount": [{ - "amount": "1234567", - "denom": "ucosm" - }], - "from_address": "${address}", - "to_address": "${address}" - } - }], - "sequence": 0 - }`; + memo: "Some memo", + msgs: [ + { + type: "cosmos-sdk/MsgSend", + value: { + amount: [ + { + amount: "1234567", + denom: "ucosm", + }, + ], + // eslint-disable-next-line @typescript-eslint/naming-convention + from_address: address, + // eslint-disable-next-line @typescript-eslint/naming-convention + to_address: address, + }, + }, + ], + }; + return JSON.stringify(signDoc, null, 2); } const signer = new LedgerSigner({ @@ -37,7 +48,9 @@ const signer = new LedgerSigner({ hdPaths: [makeCosmoshubPath(0), makeCosmoshubPath(1), makeCosmoshubPath(2)], }); -window.updateMessage = (accountNumber: number) => { +window.updateMessage = (accountNumberInput: unknown) => { + assert(typeof accountNumberInput === "string"); + const accountNumber = Uint53.fromString(accountNumberInput).toNumber(); const account = accounts[accountNumber]; if (account === undefined) { return; @@ -46,15 +59,15 @@ window.updateMessage = (accountNumber: number) => { const address = accounts[accountNumber].address; const addressInput = document.getElementById("address"); addressInput.value = address; - const messageTextArea = document.getElementById("message"); - messageTextArea.textContent = createMessage(accountNumber, address); + const signDocTextArea = document.getElementById("sign-doc"); + signDocTextArea.textContent = createSignDoc(accountNumber, address); }; window.getAccounts = async function getAccounts(): Promise { const accountNumberInput = document.getElementById("account-number"); const addressInput = document.getElementById("address"); const accountsDiv = document.getElementById("accounts"); - const messageTextArea = document.getElementById("message"); + const signDocTextArea = document.getElementById("sign-doc"); accountsDiv.textContent = "Loading..."; try { @@ -69,7 +82,7 @@ window.getAccounts = async function getAccounts(): Promise { accountNumberInput.value = accountNumber; const address = accounts[0].address; addressInput.value = address; - messageTextArea.textContent = createMessage(accountNumber, address); + signDocTextArea.textContent = createSignDoc(accountNumber, address); } catch (error) { accountsDiv.textContent = error; } @@ -81,9 +94,9 @@ window.sign = async function sign(): Promise { try { const address = document.getElementById("address").value; - const rawMessage = document.getElementById("message").textContent; - const message = JSON.stringify(JSON.parse(rawMessage)); - const signature = await signer.sign(address, toUtf8(message)); + const signDocJson = document.getElementById("sign-doc").textContent; + const signDoc: StdSignDoc = JSON.parse(signDocJson); + const signature = await signer.sign(address, signDoc); signatureDiv.textContent = JSON.stringify(signature, null, "\t"); } catch (error) { signatureDiv.textContent = error; diff --git a/packages/launchpad-ledger/src/ledgersigner.ts b/packages/launchpad-ledger/src/ledgersigner.ts index da6892ae..a7d55bfe 100644 --- a/packages/launchpad-ledger/src/ledgersigner.ts +++ b/packages/launchpad-ledger/src/ledgersigner.ts @@ -4,8 +4,9 @@ import { encodeSecp256k1Signature, makeCosmoshubPath, OfflineSigner, - StdSignature, + StdSignDoc, } from "@cosmjs/launchpad"; +import { serializeSignDoc, SignResponse } from "@cosmjs/launchpad"; import { LaunchpadLedger, LaunchpadLedgerOptions } from "./launchpadledger"; @@ -34,17 +35,21 @@ export class LedgerSigner implements OfflineSigner { return this.accounts; } - public async sign(address: string, message: Uint8Array): Promise { + public async sign(signerAddress: string, signDoc: StdSignDoc): Promise { const accounts = this.accounts || (await this.getAccounts()); - const accountIndex = accounts.findIndex((account) => account.address === address); + const accountIndex = accounts.findIndex((account) => account.address === signerAddress); if (accountIndex === -1) { - throw new Error(`Address ${address} not found in wallet`); + throw new Error(`Address ${signerAddress} not found in wallet`); } + const message = serializeSignDoc(signDoc); const accountForAddress = accounts[accountIndex]; const hdPath = this.hdPaths[accountIndex]; const signature = await this.ledger.sign(message, hdPath); - return encodeSecp256k1Signature(accountForAddress.pubkey, signature); + return { + signed: signDoc, + signature: encodeSecp256k1Signature(accountForAddress.pubkey, signature), + }; } } diff --git a/packages/launchpad-ledger/types/ledgersigner.d.ts b/packages/launchpad-ledger/types/ledgersigner.d.ts index 1ebfdcdd..6ddfee8b 100644 --- a/packages/launchpad-ledger/types/ledgersigner.d.ts +++ b/packages/launchpad-ledger/types/ledgersigner.d.ts @@ -1,4 +1,5 @@ -import { AccountData, OfflineSigner, StdSignature } from "@cosmjs/launchpad"; +import { AccountData, OfflineSigner, StdSignDoc } from "@cosmjs/launchpad"; +import { SignResponse } from "@cosmjs/launchpad"; import { LaunchpadLedgerOptions } from "./launchpadledger"; export declare class LedgerSigner implements OfflineSigner { private readonly ledger; @@ -6,5 +7,5 @@ export declare class LedgerSigner implements OfflineSigner { private accounts?; constructor(options?: LaunchpadLedgerOptions); getAccounts(): Promise; - sign(address: string, message: Uint8Array): Promise; + sign(signerAddress: string, signDoc: StdSignDoc): Promise; } diff --git a/packages/launchpad/README.md b/packages/launchpad/README.md index 0985bb06..3ecedbf0 100644 --- a/packages/launchpad/README.md +++ b/packages/launchpad/README.md @@ -173,7 +173,7 @@ import { MsgExecuteContract, setupWasmExtension } from "@cosmjs/cosmwasm"; import { assertIsPostTxSuccess, LcdClient, - makeSignBytes, + makeSignDoc, setupAuthExtension, StdFee, StdTx, @@ -205,21 +205,9 @@ const memo = "Time for action"; const { account_number, sequence } = ( await client.auth.account(myAddress) ).result.value; -const signBytes = makeSignBytes( - [msg], - fee, - apiUrl, - memo, - account_number, - sequence, -); -const signature = await signer.sign(myAddress, signBytes); -const signedTx: StdTx = { - msg: [msg], - fee: fee, - memo: memo, - signatures: [signature], -}; +const signDoc = makeSignDoc([msg], fee, apiUrl, memo, account_number, sequence); +const { signed, signature } = await signer.sign(myAddress, signDoc); +const signedTx = makeStdTx(signed, signature); const result = await client.postTx(signedTx); assertIsPostTxSuccess(result); ``` diff --git a/packages/launchpad/src/cosmosclient.searchtx.spec.ts b/packages/launchpad/src/cosmosclient.searchtx.spec.ts index f3bbda31..5ff15c3a 100644 --- a/packages/launchpad/src/cosmosclient.searchtx.spec.ts +++ b/packages/launchpad/src/cosmosclient.searchtx.spec.ts @@ -3,7 +3,7 @@ import { assert, sleep } from "@cosmjs/utils"; import { coins } from "./coins"; import { CosmosClient, isBroadcastTxFailure } from "./cosmosclient"; -import { makeSignBytes } from "./encoding"; +import { makeSignDoc } from "./encoding"; import { LcdClient } from "./lcdapi"; import { isMsgSend, MsgSend } from "./msgs"; import { Secp256k1Wallet } from "./secp256k1wallet"; @@ -16,14 +16,14 @@ import { wasmd, wasmdEnabled, } from "./testutils.spec"; -import { CosmosSdkTx } from "./types"; +import { makeStdTx, WrappedStdTx } from "./tx"; interface TestTxSend { readonly sender: string; readonly recipient: string; readonly hash: string; readonly height: number; - readonly tx: CosmosSdkTx; + readonly tx: WrappedStdTx; } describe("CosmosClient.searchTx", () => { @@ -55,16 +55,11 @@ describe("CosmosClient.searchTx", () => { }; const { accountNumber, sequence } = await client.getSequence(); const chainId = await client.getChainId(); - const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence); - const signature = await wallet.sign(walletAddress, signBytes); - const tx: CosmosSdkTx = { + const signDoc = makeSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence); + const { signed, signature } = await wallet.sign(walletAddress, signDoc); + const tx: WrappedStdTx = { type: "cosmos-sdk/StdTx", - value: { - msg: [sendMsg], - fee: fee, - memo: memo, - signatures: [signature], - }, + value: makeStdTx(signed, signature), }; const transactionId = await client.getIdentifier(tx); const result = await client.broadcastTx(tx.value); diff --git a/packages/launchpad/src/cosmosclient.spec.ts b/packages/launchpad/src/cosmosclient.spec.ts index 03eb672f..95ee11b3 100644 --- a/packages/launchpad/src/cosmosclient.spec.ts +++ b/packages/launchpad/src/cosmosclient.spec.ts @@ -1,9 +1,9 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { sleep } from "@cosmjs/utils"; +import { assert, sleep } from "@cosmjs/utils"; import { ReadonlyDate } from "readonly-date"; import { assertIsBroadcastTxSuccess, CosmosClient, PrivateCosmosClient } from "./cosmosclient"; -import { makeSignBytes } from "./encoding"; +import { makeSignDoc } from "./encoding"; import { findAttribute } from "./logs"; import { MsgSend } from "./msgs"; import { Secp256k1Wallet } from "./secp256k1wallet"; @@ -16,6 +16,7 @@ import { unused, wasmd, } from "./testutils.spec"; +import { isWrappedStdTx, makeStdTx } from "./tx"; import { StdFee } from "./types"; const blockTime = 1_000; // ms @@ -190,6 +191,7 @@ describe("CosmosClient", () => { it("works", async () => { pendingWithoutWasmd(); const client = new CosmosClient(wasmd.endpoint); + assert(isWrappedStdTx(cosmoshub.tx)); expect(await client.getIdentifier(cosmoshub.tx)).toEqual(cosmoshub.id); }); }); @@ -229,14 +231,9 @@ describe("CosmosClient", () => { const chainId = await client.getChainId(); const { accountNumber, sequence } = await client.getSequence(faucet.address); - const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence); - const signature = await wallet.sign(walletAddress, signBytes); - const signedTx = { - msg: [sendMsg], - fee: fee, - memo: memo, - signatures: [signature], - }; + const signDoc = makeSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence); + const { signed, signature } = await wallet.sign(walletAddress, signDoc); + const signedTx = makeStdTx(signed, signature); const txResult = await client.broadcastTx(signedTx); assertIsBroadcastTxSuccess(txResult); const { logs, transactionHash } = txResult; diff --git a/packages/launchpad/src/cosmosclient.ts b/packages/launchpad/src/cosmosclient.ts index 9bb0d2ca..c59ec73f 100644 --- a/packages/launchpad/src/cosmosclient.ts +++ b/packages/launchpad/src/cosmosclient.ts @@ -12,7 +12,8 @@ import { uint64ToNumber, } from "./lcdapi"; import { Log, parseLogs } from "./logs"; -import { CosmosSdkTx, PubKey, StdTx } from "./types"; +import { StdTx, WrappedStdTx } from "./tx"; +import { PubKey } from "./types"; export interface GetSequenceResult { readonly accountNumber: number; @@ -121,7 +122,7 @@ export interface IndexedTx { readonly code: number; readonly rawLog: string; readonly logs: readonly Log[]; - readonly tx: CosmosSdkTx; + readonly tx: WrappedStdTx; /** The gas limit as set by the user */ readonly gasWanted?: number; /** The gas used by the execution */ @@ -203,7 +204,7 @@ export class CosmosClient { /** * Returns a 32 byte upper-case hex transaction hash (typically used as the transaction ID) */ - public async getIdentifier(tx: CosmosSdkTx): Promise { + public async getIdentifier(tx: WrappedStdTx): Promise { // We consult the REST API because we don't have a local amino encoder const response = await this.lcdClient.encodeTx(tx); const hash = new Sha256(fromBase64(response.tx)).digest(); diff --git a/packages/launchpad/src/encoding.ts b/packages/launchpad/src/encoding.ts index 3c09e775..81bafa2f 100644 --- a/packages/launchpad/src/encoding.ts +++ b/packages/launchpad/src/encoding.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { toUtf8 } from "@cosmjs/encoding"; +import { Uint53 } from "@cosmjs/math"; -import { uint64ToString } from "./lcdapi"; import { Msg } from "./msgs"; import { StdFee } from "./types"; @@ -29,30 +29,33 @@ function sortJson(json: any): any { * @see https://docs.cosmos.network/master/modules/auth/03_types.html#stdsigndoc */ export interface StdSignDoc { - readonly account_number: string; readonly chain_id: string; - readonly fee: StdFee; - readonly memo: string; - readonly msgs: readonly Msg[]; + readonly account_number: string; readonly sequence: string; + readonly fee: StdFee; + readonly msgs: readonly Msg[]; + readonly memo: string; } -export function makeSignBytes( +export function makeSignDoc( msgs: readonly Msg[], fee: StdFee, chainId: string, memo: string, accountNumber: number | string, sequence: number | string, -): Uint8Array { - const signDoc: StdSignDoc = { - account_number: uint64ToString(accountNumber), +): StdSignDoc { + return { chain_id: chainId, + account_number: Uint53.fromString(accountNumber.toString()).toString(), + sequence: Uint53.fromString(sequence.toString()).toString(), fee: fee, - memo: memo, msgs: msgs, - sequence: uint64ToString(sequence), + memo: memo, }; +} + +export function serializeSignDoc(signDoc: StdSignDoc): Uint8Array { const sortedSignDoc = sortJson(signDoc); return toUtf8(JSON.stringify(sortedSignDoc)); } diff --git a/packages/launchpad/src/index.ts b/packages/launchpad/src/index.ts index 3701ab33..a703e61f 100644 --- a/packages/launchpad/src/index.ts +++ b/packages/launchpad/src/index.ts @@ -28,7 +28,7 @@ export { isSearchBySentFromOrToQuery, isSearchByTagsQuery, } from "./cosmosclient"; -export { makeSignBytes, StdSignDoc } from "./encoding"; +export { makeSignDoc, serializeSignDoc, StdSignDoc } from "./encoding"; export { buildFeeTable, FeeTable, GasLimits, GasPrice } from "./gas"; export { AuthAccountsResponse, @@ -98,15 +98,9 @@ export { } from "./pubkey"; export { findSequenceForSignedTx } from "./sequence"; export { encodeSecp256k1Signature, decodeSignature } from "./signature"; +export { AccountData, Algo, OfflineSigner, SignResponse } from "./signer"; export { CosmosFeeTable, SigningCosmosClient } from "./signingcosmosclient"; -export { isStdTx, pubkeyType, CosmosSdkTx, PubKey, StdFee, StdSignature, StdTx } from "./types"; -export { - AccountData, - Algo, - PrehashType, - OfflineSigner, - makeCosmoshubPath, - executeKdf, - KdfConfiguration, -} from "./wallet"; +export { isStdTx, isWrappedStdTx, makeStdTx, CosmosSdkTx, StdTx, WrappedStdTx, WrappedTx } from "./tx"; +export { pubkeyType, PubKey, StdFee, StdSignature } from "./types"; +export { makeCosmoshubPath, executeKdf, KdfConfiguration } from "./wallet"; export { extractKdfConfiguration, Secp256k1Wallet } from "./secp256k1wallet"; diff --git a/packages/launchpad/src/lcdapi/base.ts b/packages/launchpad/src/lcdapi/base.ts index 85465454..442eda3f 100644 --- a/packages/launchpad/src/lcdapi/base.ts +++ b/packages/launchpad/src/lcdapi/base.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ -import { CosmosSdkTx } from "../types"; +import { WrappedStdTx } from "../tx"; /** * The mode used to send transaction @@ -109,7 +109,7 @@ export interface TxsResponse { readonly code?: number; readonly raw_log: string; readonly logs?: unknown[]; - readonly tx: CosmosSdkTx; + readonly tx: WrappedStdTx; /** The gas limit as set by the user */ readonly gas_wanted?: string; /** The gas used by the execution */ diff --git a/packages/launchpad/src/lcdapi/distribution.spec.ts b/packages/launchpad/src/lcdapi/distribution.spec.ts index 1f6165dd..dc9f402e 100644 --- a/packages/launchpad/src/lcdapi/distribution.spec.ts +++ b/packages/launchpad/src/lcdapi/distribution.spec.ts @@ -4,7 +4,7 @@ import { sleep } from "@cosmjs/utils"; import { coin, coins } from "../coins"; import { assertIsBroadcastTxSuccess } from "../cosmosclient"; -import { makeSignBytes } from "../encoding"; +import { makeSignDoc } from "../encoding"; import { MsgDelegate } from "../msgs"; import { Secp256k1Wallet } from "../secp256k1wallet"; import { SigningCosmosClient } from "../signingcosmosclient"; @@ -16,6 +16,7 @@ import { wasmd, wasmdEnabled, } from "../testutils.spec"; +import { makeStdTx } from "../tx"; import { DistributionExtension, setupDistributionExtension } from "./distribution"; import { LcdClient } from "./lcdclient"; @@ -45,16 +46,11 @@ describe("DistributionExtension", () => { }; const memo = "Test delegation for wasmd"; const { accountNumber, sequence } = await client.getSequence(); - const signBytes = makeSignBytes([msg], defaultFee, chainId, memo, accountNumber, sequence); - const signature = await wallet.sign(faucet.address, signBytes); - const tx = { - msg: [msg], - fee: defaultFee, - memo: memo, - signatures: [signature], - }; + const signDoc = makeSignDoc([msg], defaultFee, chainId, memo, accountNumber, sequence); + const { signed, signature } = await wallet.sign(faucet.address, signDoc); + const signedTx = makeStdTx(signed, signature); - const result = await client.broadcastTx(tx); + const result = await client.broadcastTx(signedTx); assertIsBroadcastTxSuccess(result); await sleep(75); // wait until transactions are indexed diff --git a/packages/launchpad/src/lcdapi/gov.spec.ts b/packages/launchpad/src/lcdapi/gov.spec.ts index c6cd10d1..5438915d 100644 --- a/packages/launchpad/src/lcdapi/gov.spec.ts +++ b/packages/launchpad/src/lcdapi/gov.spec.ts @@ -3,7 +3,7 @@ import { sleep } from "@cosmjs/utils"; import { coins } from "../coins"; import { assertIsBroadcastTxSuccess } from "../cosmosclient"; -import { makeSignBytes } from "../encoding"; +import { makeSignDoc } from "../encoding"; import { Secp256k1Wallet } from "../secp256k1wallet"; import { SigningCosmosClient } from "../signingcosmosclient"; import { @@ -50,7 +50,7 @@ describe("GovExtension", () => { }; const proposalMemo = "Test proposal for wasmd"; const { accountNumber: proposalAccountNumber, sequence: proposalSequence } = await client.getSequence(); - const proposalSignBytes = makeSignBytes( + const proposalSignDoc = makeSignDoc( [proposalMsg], defaultFee, chainId, @@ -58,7 +58,7 @@ describe("GovExtension", () => { proposalAccountNumber, proposalSequence, ); - const proposalSignature = await wallet.sign(faucet.address, proposalSignBytes); + const { signature: proposalSignature } = await wallet.sign(faucet.address, proposalSignDoc); const proposalTx = { msg: [proposalMsg], fee: defaultFee, @@ -82,7 +82,7 @@ describe("GovExtension", () => { }; const voteMemo = "Test vote for wasmd"; const { accountNumber: voteAccountNumber, sequence: voteSequence } = await client.getSequence(); - const voteSignBytes = makeSignBytes( + const voteSignDoc = makeSignDoc( [voteMsg], defaultFee, chainId, @@ -90,7 +90,7 @@ describe("GovExtension", () => { voteAccountNumber, voteSequence, ); - const voteSignature = await wallet.sign(faucet.address, voteSignBytes); + const { signature: voteSignature } = await wallet.sign(faucet.address, voteSignDoc); const voteTx = { msg: [voteMsg], fee: defaultFee, diff --git a/packages/launchpad/src/lcdapi/lcdclient.spec.ts b/packages/launchpad/src/lcdapi/lcdclient.spec.ts index 42418e18..051d8d86 100644 --- a/packages/launchpad/src/lcdapi/lcdclient.spec.ts +++ b/packages/launchpad/src/lcdapi/lcdclient.spec.ts @@ -3,7 +3,7 @@ import { assert, sleep } from "@cosmjs/utils"; import { Coin } from "../coins"; import { isBroadcastTxFailure } from "../cosmosclient"; -import { makeSignBytes } from "../encoding"; +import { makeSignDoc } from "../encoding"; import { parseLogs } from "../logs"; import { MsgSend } from "../msgs"; import { Secp256k1Wallet } from "../secp256k1wallet"; @@ -12,7 +12,6 @@ import cosmoshub from "../testdata/cosmoshub.json"; import { faucet, makeRandomAddress, - makeSignedTx, nonNegativeIntegerMatcher, pendingWithoutWasmd, tendermintIdMatcher, @@ -20,6 +19,7 @@ import { wasmd, wasmdEnabled, } from "../testutils.spec"; +import { isWrappedStdTx, makeStdTx, StdTx } from "../tx"; import { StdFee } from "../types"; import { makeCosmoshubPath } from "../wallet"; import { setupAuthExtension } from "./auth"; @@ -239,14 +239,9 @@ describe("LcdClient", () => { }; const { accountNumber, sequence } = await client.getSequence(); const chainId = await client.getChainId(); - const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence); - const signature = await wallet.sign(walletAddress, signBytes); - const signedTx = { - msg: [sendMsg], - fee: fee, - memo: memo, - signatures: [signature], - }; + const signDoc = makeSignDoc([sendMsg], fee, chainId, memo, accountNumber, sequence); + const { signed, signature } = await wallet.sign(walletAddress, signDoc); + const signedTx = makeStdTx(signed, signature); const transactionId = await client.getIdentifier({ type: "cosmos-sdk/StdTx", value: signedTx }); const result = await client.broadcastTx(signedTx); assert(isBroadcastTxFailure(result)); @@ -493,6 +488,7 @@ describe("LcdClient", () => { it("works for cosmoshub example", async () => { pendingWithoutWasmd(); const client = new LcdClient(wasmd.endpoint); + assert(isWrappedStdTx(cosmoshub.tx)); const response = await client.encodeTx(cosmoshub.tx); expect(response).toEqual( jasmine.objectContaining({ @@ -537,9 +533,9 @@ describe("LcdClient", () => { const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }, setupAuthExtension); const { account_number, sequence } = (await client.auth.account(faucet.address)).result.value; - const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); - const signature = await wallet.sign(walletAddress, signBytes); - const signedTx = makeSignedTx(theMsg, fee, memo, signature); + const signDoc = makeSignDoc([theMsg], fee, wasmd.chainId, memo, account_number, sequence); + const { signed, signature } = await wallet.sign(walletAddress, signDoc); + const signedTx = makeStdTx(signed, signature); const result = await client.broadcastTx(signedTx); expect(result.code).toBeUndefined(); expect(result).toEqual({ @@ -594,13 +590,13 @@ describe("LcdClient", () => { const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value; const { account_number: an3, sequence: sequence3 } = (await client.auth.account(address3)).result.value; - const signBytes1 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an1, sequence1); - const signBytes2 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an2, sequence2); - const signBytes3 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an3, sequence3); - const signature1 = await account1.sign(address1, signBytes1); - const signature2 = await account2.sign(address2, signBytes2); - const signature3 = await account3.sign(address3, signBytes3); - const signedTx = { + const signDoc1 = makeSignDoc([theMsg], fee, wasmd.chainId, memo, an1, sequence1); + const signDoc2 = makeSignDoc([theMsg], fee, wasmd.chainId, memo, an2, sequence2); + const signDoc3 = makeSignDoc([theMsg], fee, wasmd.chainId, memo, an3, sequence3); + const { signature: signature1 } = await account1.sign(address1, signDoc1); + const { signature: signature2 } = await account2.sign(address2, signDoc2); + const { signature: signature3 } = await account3.sign(address3, signDoc3); + const signedTx: StdTx = { msg: [theMsg], fee: fee, memo: memo, @@ -658,14 +654,9 @@ describe("LcdClient", () => { const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }, setupAuthExtension); const { account_number, sequence } = (await client.auth.account(walletAddress)).result.value; - const signBytes = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, account_number, sequence); - const signature1 = await wallet.sign(walletAddress, signBytes); - const signedTx = { - msg: [msg1, msg2], - fee: fee, - memo: memo, - signatures: [signature1], - }; + const signDoc = makeSignDoc([msg1, msg2], fee, wasmd.chainId, memo, account_number, sequence); + const { signed, signature } = await wallet.sign(walletAddress, signDoc); + const signedTx = makeStdTx(signed, signature); const broadcastResult = await client.broadcastTx(signedTx); expect(broadcastResult.code).toBeUndefined(); }); @@ -722,11 +713,11 @@ describe("LcdClient", () => { const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value; const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value; - const signBytes1 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1); - const signBytes2 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2); - const signature1 = await account1.sign(address1, signBytes1); - const signature2 = await account2.sign(address2, signBytes2); - const signedTx = { + const signDoc1 = makeSignDoc([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1); + const signDoc2 = makeSignDoc([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2); + const { signature: signature1 } = await account1.sign(address1, signDoc1); + const { signature: signature2 } = await account2.sign(address2, signDoc2); + const signedTx: StdTx = { msg: [msg2, msg1], fee: fee, memo: memo, @@ -793,11 +784,11 @@ describe("LcdClient", () => { const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value; const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value; - const signBytes1 = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, an1, sequence1); - const signBytes2 = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, an2, sequence2); - const signature1 = await account1.sign(address1, signBytes1); - const signature2 = await account2.sign(address2, signBytes2); - const signedTx = { + const signDoc1 = makeSignDoc([msg1, msg2], fee, wasmd.chainId, memo, an1, sequence1); + const signDoc2 = makeSignDoc([msg1, msg2], fee, wasmd.chainId, memo, an2, sequence2); + const { signature: signature1 } = await account1.sign(address1, signDoc1); + const { signature: signature2 } = await account2.sign(address2, signDoc2); + const signedTx: StdTx = { msg: [msg1, msg2], fee: fee, memo: memo, @@ -859,11 +850,11 @@ describe("LcdClient", () => { const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value; const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value; - const signBytes1 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1); - const signBytes2 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2); - const signature1 = await account1.sign(address1, signBytes1); - const signature2 = await account2.sign(address2, signBytes2); - const signedTx = { + const signDoc1 = makeSignDoc([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1); + const signDoc2 = makeSignDoc([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2); + const { signature: signature1 } = await account1.sign(address1, signDoc1); + const { signature: signature2 } = await account2.sign(address2, signDoc2); + const signedTx: StdTx = { msg: [msg2, msg1], fee: fee, memo: memo, diff --git a/packages/launchpad/src/lcdapi/lcdclient.ts b/packages/launchpad/src/lcdapi/lcdclient.ts index 25bcb64a..02afc4cc 100644 --- a/packages/launchpad/src/lcdapi/lcdclient.ts +++ b/packages/launchpad/src/lcdapi/lcdclient.ts @@ -2,7 +2,7 @@ import { assert, isNonNullObject } from "@cosmjs/utils"; import axios, { AxiosError, AxiosInstance } from "axios"; -import { CosmosSdkTx, StdTx } from "../types"; +import { StdTx, WrappedStdTx } from "../tx"; import { BlockResponse, BroadcastMode, @@ -284,7 +284,7 @@ export class LcdClient { } /** returns the amino-encoding of the transaction performed by the server */ - public async encodeTx(tx: CosmosSdkTx): Promise { + public async encodeTx(tx: WrappedStdTx): Promise { const responseData = await this.post("/txs/encode", tx); if (!responseData.tx) { throw new Error("Unexpected response data format"); diff --git a/packages/launchpad/src/lcdapi/staking.spec.ts b/packages/launchpad/src/lcdapi/staking.spec.ts index db75603c..c7e1439a 100644 --- a/packages/launchpad/src/lcdapi/staking.spec.ts +++ b/packages/launchpad/src/lcdapi/staking.spec.ts @@ -3,7 +3,7 @@ import { assert, sleep } from "@cosmjs/utils"; import { coin, coins } from "../coins"; import { assertIsBroadcastTxSuccess } from "../cosmosclient"; -import { makeSignBytes } from "../encoding"; +import { makeSignDoc } from "../encoding"; import { MsgDelegate, MsgUndelegate } from "../msgs"; import { Secp256k1Wallet } from "../secp256k1wallet"; import { SigningCosmosClient } from "../signingcosmosclient"; @@ -16,6 +16,7 @@ import { wasmd, wasmdEnabled, } from "../testutils.spec"; +import { makeStdTx } from "../tx"; import { LcdClient } from "./lcdclient"; import { BondStatus, setupStakingExtension, StakingExtension } from "./staking"; @@ -46,16 +47,11 @@ describe("StakingExtension", () => { }; const memo = "Test delegation for wasmd"; const { accountNumber, sequence } = await client.getSequence(); - const signBytes = makeSignBytes([msg], defaultFee, chainId, memo, accountNumber, sequence); - const signature = await wallet.sign(faucet.address, signBytes); - const tx = { - msg: [msg], - fee: defaultFee, - memo: memo, - signatures: [signature], - }; + const signDoc = makeSignDoc([msg], defaultFee, chainId, memo, accountNumber, sequence); + const { signed, signature } = await wallet.sign(faucet.address, signDoc); + const signedTx = makeStdTx(signed, signature); - const result = await client.broadcastTx(tx); + const result = await client.broadcastTx(signedTx); assertIsBroadcastTxSuccess(result); } { @@ -69,16 +65,11 @@ describe("StakingExtension", () => { }; const memo = "Test undelegation for wasmd"; const { accountNumber, sequence } = await client.getSequence(); - const signBytes = makeSignBytes([msg], defaultFee, chainId, memo, accountNumber, sequence); - const signature = await wallet.sign(faucet.address, signBytes); - const tx = { - msg: [msg], - fee: defaultFee, - memo: memo, - signatures: [signature], - }; + const signDoc = makeSignDoc([msg], defaultFee, chainId, memo, accountNumber, sequence); + const { signed, signature } = await wallet.sign(faucet.address, signDoc); + const signedTx = makeStdTx(signed, signature); - const result = await client.broadcastTx(tx); + const result = await client.broadcastTx(signedTx); assertIsBroadcastTxSuccess(result); } diff --git a/packages/launchpad/src/secp256k1wallet.spec.ts b/packages/launchpad/src/secp256k1wallet.spec.ts index a16e8534..f7f6f867 100644 --- a/packages/launchpad/src/secp256k1wallet.spec.ts +++ b/packages/launchpad/src/secp256k1wallet.spec.ts @@ -1,6 +1,8 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import { Secp256k1, Secp256k1Signature, Sha256 } from "@cosmjs/crypto"; -import { fromBase64, fromHex, toAscii } from "@cosmjs/encoding"; +import { fromBase64, fromHex } from "@cosmjs/encoding"; +import { serializeSignDoc, StdSignDoc } from "./encoding"; import { extractKdfConfiguration, Secp256k1Wallet } from "./secp256k1wallet"; import { base64Matcher } from "./testutils.spec"; import { executeKdf, KdfConfiguration } from "./wallet"; @@ -110,11 +112,19 @@ describe("Secp256k1Wallet", () => { describe("sign", () => { it("resolves to valid signature if enabled", async () => { const wallet = await Secp256k1Wallet.fromMnemonic(defaultMnemonic); - const message = toAscii("foo bar"); - const signature = await wallet.sign(defaultAddress, message); + const signDoc: StdSignDoc = { + msgs: [], + fee: { amount: [], gas: "23" }, + chain_id: "foochain", + memo: "hello, world", + account_number: "7", + sequence: "54", + }; + const { signed, signature } = await wallet.sign(defaultAddress, signDoc); + expect(signed).toEqual(signDoc); const valid = await Secp256k1.verifySignature( Secp256k1Signature.fromFixedLength(fromBase64(signature.signature)), - new Sha256(message).digest(), + new Sha256(serializeSignDoc(signed)).digest(), defaultPubkey, ); expect(valid).toEqual(true); diff --git a/packages/launchpad/src/secp256k1wallet.ts b/packages/launchpad/src/secp256k1wallet.ts index 5f5aea9a..a515d3de 100644 --- a/packages/launchpad/src/secp256k1wallet.ts +++ b/packages/launchpad/src/secp256k1wallet.ts @@ -5,6 +5,7 @@ import { pathToString, Random, Secp256k1, + Sha256, Slip10, Slip10Curve, stringToPath, @@ -13,19 +14,16 @@ import { fromBase64, fromUtf8, toBase64, toUtf8 } from "@cosmjs/encoding"; import { assert, isNonNullObject } from "@cosmjs/utils"; import { rawSecp256k1PubkeyToAddress } from "./address"; +import { serializeSignDoc, StdSignDoc } from "./encoding"; import { encodeSecp256k1Signature } from "./signature"; -import { StdSignature } from "./types"; +import { AccountData, OfflineSigner, SignResponse } from "./signer"; import { - AccountData, decrypt, encrypt, EncryptionConfiguration, executeKdf, KdfConfiguration, makeCosmoshubPath, - OfflineSigner, - prehash, - PrehashType, supportedAlgorithms, } from "./wallet"; @@ -257,18 +255,17 @@ export class Secp256k1Wallet implements OfflineSigner { ]; } - public async sign( - address: string, - message: Uint8Array, - prehashType: PrehashType = "sha256", - ): Promise { - if (address !== this.address) { - throw new Error(`Address ${address} not found in wallet`); + public async sign(signerAddress: string, signDoc: StdSignDoc): Promise { + if (signerAddress !== this.address) { + throw new Error(`Address ${signerAddress} not found in wallet`); } - const hashedMessage = prehash(message, prehashType); - const signature = await Secp256k1.createSignature(hashedMessage, this.privkey); + const message = new Sha256(serializeSignDoc(signDoc)).digest(); + const signature = await Secp256k1.createSignature(message, this.privkey); const signatureBytes = new Uint8Array([...signature.r(32), ...signature.s(32)]); - return encodeSecp256k1Signature(this.pubkey, signatureBytes); + return { + signed: signDoc, + signature: encodeSecp256k1Signature(this.pubkey, signatureBytes), + }; } /** diff --git a/packages/launchpad/src/sequence.spec.ts b/packages/launchpad/src/sequence.spec.ts index e5d8f4cd..24c8006a 100644 --- a/packages/launchpad/src/sequence.spec.ts +++ b/packages/launchpad/src/sequence.spec.ts @@ -1,7 +1,10 @@ +import { assert } from "@cosmjs/utils"; + import { findSequenceForSignedTx } from "./sequence"; import response1 from "./testdata/txresponse1.json"; import response2 from "./testdata/txresponse2.json"; import response3 from "./testdata/txresponse3.json"; +import { isWrappedStdTx } from "./tx"; // Those values must match ./testdata/txresponse*.json const chainId = "testing"; @@ -10,6 +13,10 @@ const accountNumber = 4; describe("sequence", () => { describe("findSequenceForSignedTx", () => { it("works", async () => { + assert(isWrappedStdTx(response1.tx)); + assert(isWrappedStdTx(response2.tx)); + assert(isWrappedStdTx(response3.tx)); + const current = 100; // what we get from GET /auth/accounts/{address} expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, current)).toEqual(10); // We know response3.height > response1.height, so the sequence must be at least 10+1 @@ -19,6 +26,10 @@ describe("sequence", () => { }); it("returns undefined when sequence is not in range", async () => { + assert(isWrappedStdTx(response1.tx)); + assert(isWrappedStdTx(response2.tx)); + assert(isWrappedStdTx(response3.tx)); + expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, 5)).toBeUndefined(); expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, 20, 11)).toBeUndefined(); expect(await findSequenceForSignedTx(response1.tx, chainId, accountNumber, 20, 50)).toBeUndefined(); diff --git a/packages/launchpad/src/sequence.ts b/packages/launchpad/src/sequence.ts index cc9a8a38..a11c1eb2 100644 --- a/packages/launchpad/src/sequence.ts +++ b/packages/launchpad/src/sequence.ts @@ -1,8 +1,8 @@ import { Secp256k1, Secp256k1Signature, Sha256 } from "@cosmjs/crypto"; -import { makeSignBytes } from "./encoding"; +import { makeSignDoc, serializeSignDoc } from "./encoding"; import { decodeSignature } from "./signature"; -import { CosmosSdkTx } from "./types"; +import { WrappedStdTx } from "./tx"; /** * Serach for sequence s with `min` <= `s` < `upperBound` to find the sequence that was used to sign the transaction @@ -16,7 +16,7 @@ import { CosmosSdkTx } from "./types"; * @returns the sequence if a match was found and undefined otherwise */ export async function findSequenceForSignedTx( - tx: CosmosSdkTx, + tx: WrappedStdTx, chainId: string, accountNumber: number, upperBound: number, @@ -30,13 +30,8 @@ export async function findSequenceForSignedTx( for (let s = min; s < upperBound; s++) { // console.log(`Trying sequence ${s}`); - const signBytes = makeSignBytes( - tx.value.msg, - tx.value.fee, - chainId, - tx.value.memo || "", - accountNumber, - s, + const signBytes = serializeSignDoc( + makeSignDoc(tx.value.msg, tx.value.fee, chainId, tx.value.memo || "", accountNumber, s), ); const prehashed = new Sha256(signBytes).digest(); const valid = await Secp256k1.verifySignature(secp256keSignature, prehashed, pubkey); diff --git a/packages/launchpad/src/signer.ts b/packages/launchpad/src/signer.ts new file mode 100644 index 00000000..f7218bb1 --- /dev/null +++ b/packages/launchpad/src/signer.ts @@ -0,0 +1,38 @@ +import { StdSignDoc } from "./encoding"; +import { StdSignature } from "./types"; + +export type Algo = "secp256k1" | "ed25519" | "sr25519"; + +export interface AccountData { + /** A printable address (typically bech32 encoded) */ + readonly address: string; + readonly algo: Algo; + readonly pubkey: Uint8Array; +} + +export interface SignResponse { + /** + * The sign doc that was signed. + * This may be different from the input signDoc when the signer modifies it as part of the signing process. + */ + readonly signed: StdSignDoc; + readonly signature: StdSignature; +} + +export interface OfflineSigner { + /** + * Get AccountData array from wallet. Rejects if not enabled. + */ + readonly getAccounts: () => Promise; + + /** + * Request signature from whichever key corresponds to provided bech32-encoded address. Rejects if not enabled. + * + * The signer implementation may offer the user the ability to override parts of the signDoc. It must + * return the doc that was signed in the response. + * + * @param signerAddress The address of the account that should sign the transaction + * @param signDoc The content that should be signed + */ + readonly sign: (signerAddress: string, signDoc: StdSignDoc) => Promise; +} diff --git a/packages/launchpad/src/signingcosmosclient.ts b/packages/launchpad/src/signingcosmosclient.ts index 5ffbd236..77090274 100644 --- a/packages/launchpad/src/signingcosmosclient.ts +++ b/packages/launchpad/src/signingcosmosclient.ts @@ -1,12 +1,13 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { Coin } from "./coins"; import { Account, BroadcastTxResult, CosmosClient, GetSequenceResult } from "./cosmosclient"; -import { makeSignBytes } from "./encoding"; +import { makeSignDoc } from "./encoding"; import { buildFeeTable, FeeTable, GasLimits, GasPrice } from "./gas"; import { BroadcastMode } from "./lcdapi"; import { Msg, MsgSend } from "./msgs"; -import { StdFee, StdTx } from "./types"; -import { OfflineSigner } from "./wallet"; +import { OfflineSigner } from "./signer"; +import { makeStdTx } from "./tx"; +import { StdFee } from "./types"; /** * These fees are used by the higher level methods of SigningCosmosClient @@ -88,14 +89,9 @@ export class SigningCosmosClient extends CosmosClient { public async signAndBroadcast(msgs: readonly Msg[], fee: StdFee, memo = ""): Promise { const { accountNumber, sequence } = await this.getSequence(); const chainId = await this.getChainId(); - const signBytes = makeSignBytes(msgs, fee, chainId, memo, accountNumber, sequence); - const signature = await this.signer.sign(this.senderAddress, signBytes); - const signedTx: StdTx = { - msg: msgs, - fee: fee, - memo: memo, - signatures: [signature], - }; + const signDoc = makeSignDoc(msgs, fee, chainId, memo, accountNumber, sequence); + const { signed, signature } = await this.signer.sign(this.senderAddress, signDoc); + const signedTx = makeStdTx(signed, signature); return this.broadcastTx(signedTx); } } diff --git a/packages/launchpad/src/testutils.spec.ts b/packages/launchpad/src/testutils.spec.ts index 2093b56e..66a0f592 100644 --- a/packages/launchpad/src/testutils.spec.ts +++ b/packages/launchpad/src/testutils.spec.ts @@ -1,9 +1,6 @@ import { Random } from "@cosmjs/crypto"; import { Bech32 } from "@cosmjs/encoding"; -import { Msg } from "./msgs"; -import { StdFee, StdSignature, StdTx } from "./types"; - export function makeRandomAddress(): string { return Bech32.encode("cosmos", Random.getBytes(20)); } @@ -74,12 +71,3 @@ export function fromOneElementArray(elements: ArrayLike): T { if (elements.length !== 1) throw new Error(`Expected exactly one element but got ${elements.length}`); return elements[0]; } - -export function makeSignedTx(firstMsg: Msg, fee: StdFee, memo: string, firstSignature: StdSignature): StdTx { - return { - msg: [firstMsg], - fee: fee, - memo: memo, - signatures: [firstSignature], - }; -} diff --git a/packages/launchpad/src/tx.spec.ts b/packages/launchpad/src/tx.spec.ts new file mode 100644 index 00000000..21bf834f --- /dev/null +++ b/packages/launchpad/src/tx.spec.ts @@ -0,0 +1,54 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { coins } from "./coins"; +import { makeSignDoc } from "./encoding"; +import { makeStdTx } from "./tx"; +import { StdFee, StdSignature } from "./types"; + +describe("tx", () => { + describe("makeStdTx", () => { + it("can make an StdTx from a SignDoc and one signature", () => { + const fee: StdFee = { amount: coins(123, "ucosm"), gas: "22" }; + const signDoc = makeSignDoc([], fee, "chain-xy", "hello", 3, 4); + const signature: StdSignature = { + pub_key: { + type: "tendermint/PubKeySecp256k1", + value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP", + }, + signature: "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", + }; + const signedTx = makeStdTx(signDoc, signature); + expect(signedTx).toEqual({ + msg: [], + memo: "hello", + fee: fee, + signatures: [signature], + }); + }); + + it("can make an StdTx from a SignDoc and multiple signatures", () => { + const fee: StdFee = { amount: coins(123, "ucosm"), gas: "22" }; + const signDoc = makeSignDoc([], fee, "chain-xy", "hello", 3, 4); + const signature1: StdSignature = { + pub_key: { + type: "tendermint/PubKeySecp256k1", + value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP", + }, + signature: "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", + }; + const signature2: StdSignature = { + pub_key: { + type: "tendermint/PubKeySecp256k1", + value: "A5qFcJBJvEK/fOmEAY0DHNWwSRZ9TEfNZyH8VoVvDtAq", + }, + signature: "NK1Oy4EUGAsoC03c1wi9GG03JC/39LEdautC5Jk643oIbEPqeXHMwaqbdvO/Jws0X/NAXaN8SAy2KNY5Qml+5Q==", + }; + const signedTx = makeStdTx(signDoc, [signature1, signature2]); + expect(signedTx).toEqual({ + msg: [], + memo: "hello", + fee: fee, + signatures: [signature1, signature2], + }); + }); + }); +}); diff --git a/packages/launchpad/src/tx.ts b/packages/launchpad/src/tx.ts new file mode 100644 index 00000000..a466c9f4 --- /dev/null +++ b/packages/launchpad/src/tx.ts @@ -0,0 +1,57 @@ +import { StdSignDoc } from "./encoding"; +import { Msg } from "./msgs"; +import { StdFee, StdSignature } from "./types"; + +/** + * A Cosmos SDK StdTx + * + * @see https://docs.cosmos.network/master/modules/auth/03_types.html#stdtx + */ +export interface StdTx { + readonly msg: readonly Msg[]; + readonly fee: StdFee; + readonly signatures: readonly StdSignature[]; + readonly memo: string | undefined; +} + +export function isStdTx(txValue: unknown): txValue is StdTx { + const { memo, msg, fee, signatures } = txValue as StdTx; + return ( + typeof memo === "string" && Array.isArray(msg) && typeof fee === "object" && Array.isArray(signatures) + ); +} + +export function makeStdTx( + content: Pick, + signatures: StdSignature | readonly StdSignature[], +): StdTx { + return { + msg: content.msgs, + fee: content.fee, + memo: content.memo, + signatures: Array.isArray(signatures) ? signatures : [signatures], + }; +} + +/** + * An Amino JSON wrapper around the Tx interface + */ +export interface WrappedTx { + readonly type: string; + readonly value: any; +} + +/** + * An Amino JSON wrapper around StdTx + */ +export interface WrappedStdTx extends WrappedTx { + readonly type: "cosmos-sdk/StdTx"; + readonly value: StdTx; +} + +export function isWrappedStdTx(wrapped: WrappedTx): wrapped is WrappedStdTx { + return (wrapped as WrappedStdTx).type === "cosmos-sdk/StdTx" && isStdTx(wrapped.value); +} + +/** @deprecated use WrappedStdTx */ +export type CosmosSdkTx = WrappedStdTx; diff --git a/packages/launchpad/src/types.ts b/packages/launchpad/src/types.ts index dc799014..01126078 100644 --- a/packages/launchpad/src/types.ts +++ b/packages/launchpad/src/types.ts @@ -1,30 +1,5 @@ /* eslint-disable @typescript-eslint/naming-convention */ import { Coin } from "./coins"; -import { Msg } from "./msgs"; - -/** - * A Cosmos SDK StdTx - * - * @see https://docs.cosmos.network/master/modules/auth/03_types.html#stdtx - */ -export interface StdTx { - readonly msg: readonly Msg[]; - readonly fee: StdFee; - readonly signatures: readonly StdSignature[]; - readonly memo: string | undefined; -} - -export function isStdTx(txValue: unknown): txValue is StdTx { - const { memo, msg, fee, signatures } = txValue as StdTx; - return ( - typeof memo === "string" && Array.isArray(msg) && typeof fee === "object" && Array.isArray(signatures) - ); -} - -export interface CosmosSdkTx { - readonly type: string; - readonly value: StdTx; -} export interface StdFee { readonly amount: readonly Coin[]; diff --git a/packages/launchpad/src/wallet.ts b/packages/launchpad/src/wallet.ts index eaded173..1c64e773 100644 --- a/packages/launchpad/src/wallet.ts +++ b/packages/launchpad/src/wallet.ts @@ -3,52 +3,12 @@ import { HdPath, isArgon2idOptions, Random, - Sha256, - Sha512, Slip10RawIndex, xchacha20NonceLength, Xchacha20poly1305Ietf, } from "@cosmjs/crypto"; import { toAscii } from "@cosmjs/encoding"; -import { StdSignature } from "./types"; - -export type PrehashType = "sha256" | "sha512" | null; - -export type Algo = "secp256k1" | "ed25519" | "sr25519"; - -export interface AccountData { - // bech32-encoded - readonly address: string; - readonly algo: Algo; - readonly pubkey: Uint8Array; -} - -export interface OfflineSigner { - /** - * Get AccountData array from wallet. Rejects if not enabled. - */ - readonly getAccounts: () => Promise; - - /** - * Request signature from whichever key corresponds to provided bech32-encoded address. Rejects if not enabled. - */ - readonly sign: (address: string, message: Uint8Array, prehashType?: PrehashType) => Promise; -} - -export function prehash(bytes: Uint8Array, type: PrehashType): Uint8Array { - switch (type) { - case null: - return new Uint8Array([...bytes]); - case "sha256": - return new Sha256(bytes).digest(); - case "sha512": - return new Sha512(bytes).digest(); - default: - throw new Error("Unknown prehash type"); - } -} - /** * The Cosmoshub derivation path in the form `m/44'/118'/0'/0/a` * with 0-based account index `a`. diff --git a/packages/launchpad/types/cosmosclient.d.ts b/packages/launchpad/types/cosmosclient.d.ts index dcdda89d..12bb6f2d 100644 --- a/packages/launchpad/types/cosmosclient.d.ts +++ b/packages/launchpad/types/cosmosclient.d.ts @@ -1,7 +1,8 @@ import { Coin } from "./coins"; import { AuthExtension, BroadcastMode, LcdClient } from "./lcdapi"; import { Log } from "./logs"; -import { CosmosSdkTx, PubKey, StdTx } from "./types"; +import { StdTx, WrappedStdTx } from "./tx"; +import { PubKey } from "./types"; export interface GetSequenceResult { readonly accountNumber: number; readonly sequence: number; @@ -78,7 +79,7 @@ export interface IndexedTx { readonly code: number; readonly rawLog: string; readonly logs: readonly Log[]; - readonly tx: CosmosSdkTx; + readonly tx: WrappedStdTx; /** The gas limit as set by the user */ readonly gasWanted?: number; /** The gas used by the execution */ @@ -127,7 +128,7 @@ export declare class CosmosClient { /** * Returns a 32 byte upper-case hex transaction hash (typically used as the transaction ID) */ - getIdentifier(tx: CosmosSdkTx): Promise; + getIdentifier(tx: WrappedStdTx): Promise; /** * Returns account number and sequence. * diff --git a/packages/launchpad/types/encoding.d.ts b/packages/launchpad/types/encoding.d.ts index 51c2c0c4..05f8d8ce 100644 --- a/packages/launchpad/types/encoding.d.ts +++ b/packages/launchpad/types/encoding.d.ts @@ -6,18 +6,19 @@ import { StdFee } from "./types"; * @see https://docs.cosmos.network/master/modules/auth/03_types.html#stdsigndoc */ export interface StdSignDoc { - readonly account_number: string; readonly chain_id: string; - readonly fee: StdFee; - readonly memo: string; - readonly msgs: readonly Msg[]; + readonly account_number: string; readonly sequence: string; + readonly fee: StdFee; + readonly msgs: readonly Msg[]; + readonly memo: string; } -export declare function makeSignBytes( +export declare function makeSignDoc( msgs: readonly Msg[], fee: StdFee, chainId: string, memo: string, accountNumber: number | string, sequence: number | string, -): Uint8Array; +): StdSignDoc; +export declare function serializeSignDoc(signDoc: StdSignDoc): Uint8Array; diff --git a/packages/launchpad/types/index.d.ts b/packages/launchpad/types/index.d.ts index 9d137f0b..196653ce 100644 --- a/packages/launchpad/types/index.d.ts +++ b/packages/launchpad/types/index.d.ts @@ -26,7 +26,7 @@ export { isSearchBySentFromOrToQuery, isSearchByTagsQuery, } from "./cosmosclient"; -export { makeSignBytes, StdSignDoc } from "./encoding"; +export { makeSignDoc, serializeSignDoc, StdSignDoc } from "./encoding"; export { buildFeeTable, FeeTable, GasLimits, GasPrice } from "./gas"; export { AuthAccountsResponse, @@ -96,15 +96,9 @@ export { } from "./pubkey"; export { findSequenceForSignedTx } from "./sequence"; export { encodeSecp256k1Signature, decodeSignature } from "./signature"; +export { AccountData, Algo, OfflineSigner, SignResponse } from "./signer"; export { CosmosFeeTable, SigningCosmosClient } from "./signingcosmosclient"; -export { isStdTx, pubkeyType, CosmosSdkTx, PubKey, StdFee, StdSignature, StdTx } from "./types"; -export { - AccountData, - Algo, - PrehashType, - OfflineSigner, - makeCosmoshubPath, - executeKdf, - KdfConfiguration, -} from "./wallet"; +export { isStdTx, isWrappedStdTx, makeStdTx, CosmosSdkTx, StdTx, WrappedStdTx, WrappedTx } from "./tx"; +export { pubkeyType, PubKey, StdFee, StdSignature } from "./types"; +export { makeCosmoshubPath, executeKdf, KdfConfiguration } from "./wallet"; export { extractKdfConfiguration, Secp256k1Wallet } from "./secp256k1wallet"; diff --git a/packages/launchpad/types/lcdapi/base.d.ts b/packages/launchpad/types/lcdapi/base.d.ts index c8f58d7c..f35cfcd0 100644 --- a/packages/launchpad/types/lcdapi/base.d.ts +++ b/packages/launchpad/types/lcdapi/base.d.ts @@ -1,4 +1,4 @@ -import { CosmosSdkTx } from "../types"; +import { WrappedStdTx } from "../tx"; /** * The mode used to send transaction * @@ -93,7 +93,7 @@ export interface TxsResponse { readonly code?: number; readonly raw_log: string; readonly logs?: unknown[]; - readonly tx: CosmosSdkTx; + readonly tx: WrappedStdTx; /** The gas limit as set by the user */ readonly gas_wanted?: string; /** The gas used by the execution */ diff --git a/packages/launchpad/types/lcdapi/lcdclient.d.ts b/packages/launchpad/types/lcdapi/lcdclient.d.ts index b5cfd8ba..56682379 100644 --- a/packages/launchpad/types/lcdapi/lcdclient.d.ts +++ b/packages/launchpad/types/lcdapi/lcdclient.d.ts @@ -1,4 +1,4 @@ -import { CosmosSdkTx, StdTx } from "../types"; +import { StdTx, WrappedStdTx } from "../tx"; import { BlockResponse, BroadcastMode, @@ -151,7 +151,7 @@ export declare class LcdClient { txById(id: string): Promise; txsQuery(query: string): Promise; /** returns the amino-encoding of the transaction performed by the server */ - encodeTx(tx: CosmosSdkTx): Promise; + encodeTx(tx: WrappedStdTx): Promise; /** * Broadcasts a signed transaction to the transaction pool. * Depending on the client's broadcast mode, this might or might diff --git a/packages/launchpad/types/secp256k1wallet.d.ts b/packages/launchpad/types/secp256k1wallet.d.ts index a24a3366..29c7b63d 100644 --- a/packages/launchpad/types/secp256k1wallet.d.ts +++ b/packages/launchpad/types/secp256k1wallet.d.ts @@ -1,6 +1,7 @@ import { HdPath } from "@cosmjs/crypto"; -import { StdSignature } from "./types"; -import { AccountData, EncryptionConfiguration, KdfConfiguration, OfflineSigner, PrehashType } from "./wallet"; +import { StdSignDoc } from "./encoding"; +import { AccountData, OfflineSigner, SignResponse } from "./signer"; +import { EncryptionConfiguration, KdfConfiguration } from "./wallet"; /** * This interface describes a JSON object holding the encrypted wallet and the meta data. * All fields in here must be JSON types. @@ -85,7 +86,7 @@ export declare class Secp256k1Wallet implements OfflineSigner { get mnemonic(): string; private get address(); getAccounts(): Promise; - sign(address: string, message: Uint8Array, prehashType?: PrehashType): Promise; + sign(signerAddress: string, signDoc: StdSignDoc): Promise; /** * Generates an encrypted serialization of this wallet. * diff --git a/packages/launchpad/types/sequence.d.ts b/packages/launchpad/types/sequence.d.ts index 70d38469..3bd3430d 100644 --- a/packages/launchpad/types/sequence.d.ts +++ b/packages/launchpad/types/sequence.d.ts @@ -1,4 +1,4 @@ -import { CosmosSdkTx } from "./types"; +import { WrappedStdTx } from "./tx"; /** * Serach for sequence s with `min` <= `s` < `upperBound` to find the sequence that was used to sign the transaction * @@ -11,7 +11,7 @@ import { CosmosSdkTx } from "./types"; * @returns the sequence if a match was found and undefined otherwise */ export declare function findSequenceForSignedTx( - tx: CosmosSdkTx, + tx: WrappedStdTx, chainId: string, accountNumber: number, upperBound: number, diff --git a/packages/launchpad/types/signer.d.ts b/packages/launchpad/types/signer.d.ts new file mode 100644 index 00000000..e0e8d1a8 --- /dev/null +++ b/packages/launchpad/types/signer.d.ts @@ -0,0 +1,33 @@ +import { StdSignDoc } from "./encoding"; +import { StdSignature } from "./types"; +export declare type Algo = "secp256k1" | "ed25519" | "sr25519"; +export interface AccountData { + /** A printable address (typically bech32 encoded) */ + readonly address: string; + readonly algo: Algo; + readonly pubkey: Uint8Array; +} +export interface SignResponse { + /** + * The sign doc that was signed. + * This may be different from the input signDoc when the signer modifies it as part of the signing process. + */ + readonly signed: StdSignDoc; + readonly signature: StdSignature; +} +export interface OfflineSigner { + /** + * Get AccountData array from wallet. Rejects if not enabled. + */ + readonly getAccounts: () => Promise; + /** + * Request signature from whichever key corresponds to provided bech32-encoded address. Rejects if not enabled. + * + * The signer implementation may offer the user the ability to override parts of the signDoc. It must + * return the doc that was signed in the response. + * + * @param signerAddress The address of the account that should sign the transaction + * @param signDoc The content that should be signed + */ + readonly sign: (signerAddress: string, signDoc: StdSignDoc) => Promise; +} diff --git a/packages/launchpad/types/signingcosmosclient.d.ts b/packages/launchpad/types/signingcosmosclient.d.ts index e1110ad9..839ad665 100644 --- a/packages/launchpad/types/signingcosmosclient.d.ts +++ b/packages/launchpad/types/signingcosmosclient.d.ts @@ -3,8 +3,8 @@ import { Account, BroadcastTxResult, CosmosClient, GetSequenceResult } from "./c import { FeeTable, GasLimits, GasPrice } from "./gas"; import { BroadcastMode } from "./lcdapi"; import { Msg } from "./msgs"; +import { OfflineSigner } from "./signer"; import { StdFee } from "./types"; -import { OfflineSigner } from "./wallet"; /** * These fees are used by the higher level methods of SigningCosmosClient */ diff --git a/packages/launchpad/types/tx.d.ts b/packages/launchpad/types/tx.d.ts new file mode 100644 index 00000000..501dc880 --- /dev/null +++ b/packages/launchpad/types/tx.d.ts @@ -0,0 +1,36 @@ +import { StdSignDoc } from "./encoding"; +import { Msg } from "./msgs"; +import { StdFee, StdSignature } from "./types"; +/** + * A Cosmos SDK StdTx + * + * @see https://docs.cosmos.network/master/modules/auth/03_types.html#stdtx + */ +export interface StdTx { + readonly msg: readonly Msg[]; + readonly fee: StdFee; + readonly signatures: readonly StdSignature[]; + readonly memo: string | undefined; +} +export declare function isStdTx(txValue: unknown): txValue is StdTx; +export declare function makeStdTx( + content: Pick, + signatures: StdSignature | readonly StdSignature[], +): StdTx; +/** + * An Amino JSON wrapper around the Tx interface + */ +export interface WrappedTx { + readonly type: string; + readonly value: any; +} +/** + * An Amino JSON wrapper around StdTx + */ +export interface WrappedStdTx extends WrappedTx { + readonly type: "cosmos-sdk/StdTx"; + readonly value: StdTx; +} +export declare function isWrappedStdTx(wrapped: WrappedTx): wrapped is WrappedStdTx; +/** @deprecated use WrappedStdTx */ +export declare type CosmosSdkTx = WrappedStdTx; diff --git a/packages/launchpad/types/types.d.ts b/packages/launchpad/types/types.d.ts index 1a513b41..9c22056a 100644 --- a/packages/launchpad/types/types.d.ts +++ b/packages/launchpad/types/types.d.ts @@ -1,21 +1,4 @@ import { Coin } from "./coins"; -import { Msg } from "./msgs"; -/** - * A Cosmos SDK StdTx - * - * @see https://docs.cosmos.network/master/modules/auth/03_types.html#stdtx - */ -export interface StdTx { - readonly msg: readonly Msg[]; - readonly fee: StdFee; - readonly signatures: readonly StdSignature[]; - readonly memo: string | undefined; -} -export declare function isStdTx(txValue: unknown): txValue is StdTx; -export interface CosmosSdkTx { - readonly type: string; - readonly value: StdTx; -} export interface StdFee { readonly amount: readonly Coin[]; readonly gas: string; diff --git a/packages/launchpad/types/wallet.d.ts b/packages/launchpad/types/wallet.d.ts index 5c284f5d..6c433d32 100644 --- a/packages/launchpad/types/wallet.d.ts +++ b/packages/launchpad/types/wallet.d.ts @@ -1,23 +1,4 @@ import { HdPath } from "@cosmjs/crypto"; -import { StdSignature } from "./types"; -export declare type PrehashType = "sha256" | "sha512" | null; -export declare type Algo = "secp256k1" | "ed25519" | "sr25519"; -export interface AccountData { - readonly address: string; - readonly algo: Algo; - readonly pubkey: Uint8Array; -} -export interface OfflineSigner { - /** - * Get AccountData array from wallet. Rejects if not enabled. - */ - readonly getAccounts: () => Promise; - /** - * Request signature from whichever key corresponds to provided bech32-encoded address. Rejects if not enabled. - */ - readonly sign: (address: string, message: Uint8Array, prehashType?: PrehashType) => Promise; -} -export declare function prehash(bytes: Uint8Array, type: PrehashType): Uint8Array; /** * The Cosmoshub derivation path in the form `m/44'/118'/0'/0/a` * with 0-based account index `a`.