From 753cbdf01484ec776b20e1a4910049ef987d362f Mon Sep 17 00:00:00 2001 From: Austin Chandra Date: Wed, 22 Mar 2023 17:01:03 -0700 Subject: [PATCH] test: add e2e test for EIP712 legacy TypedData (#154) --- package-lock.json | 68 ++++++++----------- packages/evmosjs/package-lock.json | 1 + packages/evmosjs/package.json | 1 + packages/evmosjs/src/tests/network.spec.ts | 9 ++- .../evmosjs/src/tests/network/broadcast.ts | 9 +-- packages/evmosjs/src/tests/network/client.ts | 27 ++++---- packages/evmosjs/src/tests/network/common.ts | 7 +- packages/evmosjs/src/tests/network/eip712.ts | 62 +++++++++++++++++ .../network/integration/convertCoin/main.ts | 2 +- .../src/tests/network/integration/types.ts | 3 +- packages/evmosjs/src/tests/network/params.ts | 14 ++-- packages/evmosjs/src/tests/network/sign.ts | 48 ++++++------- packages/evmosjs/src/tests/network/types.ts | 18 +++++ 13 files changed, 176 insertions(+), 93 deletions(-) create mode 100644 packages/evmosjs/src/tests/network/eip712.ts create mode 100644 packages/evmosjs/src/tests/network/types.ts diff --git a/package-lock.json b/package-lock.json index f6886563..aa5eaf70 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2949,9 +2949,9 @@ "license": "MIT" }, "node_modules/@ethersproject/networks": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.0.tgz", - "integrity": "sha512-MG6oHSQHd4ebvJrleEQQ4HhVu8Ichr0RDYEfHzsVAVjHNM+w36x9wp9r+hf1JstMXtseXDtkiVoARAG6M959AA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", "dev": true, "funding": [ { @@ -2963,7 +2963,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "license": "MIT", "dependencies": { "@ethersproject/logger": "^5.7.0" } @@ -3010,9 +3009,9 @@ } }, "node_modules/@ethersproject/providers": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.0.tgz", - "integrity": "sha512-+TTrrINMzZ0aXtlwO/95uhAggKm4USLm1PbeCBR/3XZ7+Oey+3pMyddzZEyRhizHpy1HXV0FRWRMI1O3EGYibA==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", + "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", "dev": true, "funding": [ { @@ -3024,7 +3023,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "license": "MIT", "dependencies": { "@ethersproject/abstract-provider": "^5.7.0", "@ethersproject/abstract-signer": "^5.7.0", @@ -3152,7 +3150,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "license": "MIT", "dependencies": { "@ethersproject/bignumber": "^5.7.0", "@ethersproject/bytes": "^5.7.0", @@ -3227,7 +3224,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "license": "MIT", "dependencies": { "@ethersproject/bignumber": "^5.7.0", "@ethersproject/constants": "^5.7.0", @@ -3269,9 +3265,9 @@ } }, "node_modules/@ethersproject/web": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.0.tgz", - "integrity": "sha512-ApHcbbj+muRASVDSCl/tgxaH2LBkRMEYfLOLVa0COipx0+nlu0QKet7U2lEg0vdkh8XRSLf2nd1f1Uk9SrVSGA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", "dev": true, "funding": [ { @@ -3283,7 +3279,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "license": "MIT", "dependencies": { "@ethersproject/base64": "^5.7.0", "@ethersproject/bytes": "^5.7.0", @@ -9169,9 +9164,9 @@ "license": "MIT" }, "node_modules/ethers": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.0.tgz", - "integrity": "sha512-5Xhzp2ZQRi0Em+0OkOcRHxPzCfoBfgtOQA+RUylSkuHbhTEaQklnYi2hsWbRgs3ztJsXVXd9VKBcO1ScWL8YfA==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", "dev": true, "funding": [ { @@ -9183,7 +9178,6 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], - "license": "MIT", "dependencies": { "@ethersproject/abi": "5.7.0", "@ethersproject/abstract-provider": "5.7.0", @@ -9200,10 +9194,10 @@ "@ethersproject/json-wallets": "5.7.0", "@ethersproject/keccak256": "5.7.0", "@ethersproject/logger": "5.7.0", - "@ethersproject/networks": "5.7.0", + "@ethersproject/networks": "5.7.1", "@ethersproject/pbkdf2": "5.7.0", "@ethersproject/properties": "5.7.0", - "@ethersproject/providers": "5.7.0", + "@ethersproject/providers": "5.7.2", "@ethersproject/random": "5.7.0", "@ethersproject/rlp": "5.7.0", "@ethersproject/sha2": "5.7.0", @@ -9213,7 +9207,7 @@ "@ethersproject/transactions": "5.7.0", "@ethersproject/units": "5.7.0", "@ethersproject/wallet": "5.7.0", - "@ethersproject/web": "5.7.0", + "@ethersproject/web": "5.7.1", "@ethersproject/wordlists": "5.7.0" } }, @@ -9222,7 +9216,6 @@ "resolved": "https://registry.npmjs.org/ethers-eip712/-/ethers-eip712-0.2.0.tgz", "integrity": "sha512-fgS196gCIXeiLwhsWycJJuxI9nL/AoUPGSQ+yvd+8wdWR+43G+J1n69LmWVWvAON0M6qNaf2BF4/M159U8fujQ==", "dev": true, - "license": "MIT", "peerDependencies": { "ethers": "^4.0.47 || ^5.0.8" } @@ -17791,7 +17784,6 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.3.0" }, @@ -20236,9 +20228,9 @@ "dev": true }, "@ethersproject/networks": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.0.tgz", - "integrity": "sha512-MG6oHSQHd4ebvJrleEQQ4HhVu8Ichr0RDYEfHzsVAVjHNM+w36x9wp9r+hf1JstMXtseXDtkiVoARAG6M959AA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", "dev": true, "requires": { "@ethersproject/logger": "^5.7.0" @@ -20264,9 +20256,9 @@ } }, "@ethersproject/providers": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.0.tgz", - "integrity": "sha512-+TTrrINMzZ0aXtlwO/95uhAggKm4USLm1PbeCBR/3XZ7+Oey+3pMyddzZEyRhizHpy1HXV0FRWRMI1O3EGYibA==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", + "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", "dev": true, "requires": { "@ethersproject/abstract-provider": "^5.7.0", @@ -20413,9 +20405,9 @@ } }, "@ethersproject/web": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.0.tgz", - "integrity": "sha512-ApHcbbj+muRASVDSCl/tgxaH2LBkRMEYfLOLVa0COipx0+nlu0QKet7U2lEg0vdkh8XRSLf2nd1f1Uk9SrVSGA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", "dev": true, "requires": { "@ethersproject/base64": "^5.7.0", @@ -25143,9 +25135,9 @@ } }, "ethers": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.0.tgz", - "integrity": "sha512-5Xhzp2ZQRi0Em+0OkOcRHxPzCfoBfgtOQA+RUylSkuHbhTEaQklnYi2hsWbRgs3ztJsXVXd9VKBcO1ScWL8YfA==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", "dev": true, "requires": { "@ethersproject/abi": "5.7.0", @@ -25163,10 +25155,10 @@ "@ethersproject/json-wallets": "5.7.0", "@ethersproject/keccak256": "5.7.0", "@ethersproject/logger": "5.7.0", - "@ethersproject/networks": "5.7.0", + "@ethersproject/networks": "5.7.1", "@ethersproject/pbkdf2": "5.7.0", "@ethersproject/properties": "5.7.0", - "@ethersproject/providers": "5.7.0", + "@ethersproject/providers": "5.7.2", "@ethersproject/random": "5.7.0", "@ethersproject/rlp": "5.7.0", "@ethersproject/sha2": "5.7.0", @@ -25176,7 +25168,7 @@ "@ethersproject/transactions": "5.7.0", "@ethersproject/units": "5.7.0", "@ethersproject/wallet": "5.7.0", - "@ethersproject/web": "5.7.0", + "@ethersproject/web": "5.7.1", "@ethersproject/wordlists": "5.7.0" } }, diff --git a/packages/evmosjs/package-lock.json b/packages/evmosjs/package-lock.json index febc79a8..a8d67554 100644 --- a/packages/evmosjs/package-lock.json +++ b/packages/evmosjs/package-lock.json @@ -21,6 +21,7 @@ "devDependencies": { "@ethersproject/bignumber": "^5.7.0", "@ethersproject/contracts": "^5.7.0", + "@ethersproject/hash": "^5.7.0", "@ethersproject/keccak256": "^5.7.0", "@ethersproject/providers": "^5.7.2", "@ethersproject/wallet": "^5.7.0", diff --git a/packages/evmosjs/package.json b/packages/evmosjs/package.json index f797b1d2..7fd95688 100644 --- a/packages/evmosjs/package.json +++ b/packages/evmosjs/package.json @@ -38,6 +38,7 @@ "devDependencies": { "@ethersproject/bignumber": "^5.7.0", "@ethersproject/contracts": "^5.7.0", + "@ethersproject/hash": "^5.7.0", "@ethersproject/keccak256": "^5.7.0", "@ethersproject/providers": "^5.7.2", "@ethersproject/wallet": "^5.7.0", diff --git a/packages/evmosjs/src/tests/network.spec.ts b/packages/evmosjs/src/tests/network.spec.ts index 6a2f87a6..6d1a0844 100644 --- a/packages/evmosjs/src/tests/network.spec.ts +++ b/packages/evmosjs/src/tests/network.spec.ts @@ -13,8 +13,15 @@ describe('evmosjs e2e integration tests', () => { expectSuccess(response) }) + it('fulfills legacy eip-712 signatures', async () => { + const response = await networkClient.signEIP712AndBroadcast( + MsgSendUtils.generateTx, + ) + expectSuccess(response) + }) + it('fulfills msgconverterc20 transactions', async () => { const client = new ConvertCoinClient(networkClient) await client.testIntegration() - }, 30000) + }, 36000) }) diff --git a/packages/evmosjs/src/tests/network/broadcast.ts b/packages/evmosjs/src/tests/network/broadcast.ts index ee0a8a92..b00f4805 100644 --- a/packages/evmosjs/src/tests/network/broadcast.ts +++ b/packages/evmosjs/src/tests/network/broadcast.ts @@ -2,14 +2,9 @@ import { generateEndpointBroadcast, generatePostBodyBroadcast, } from '@evmos/provider' -import { Proto } from '@evmos/proto' import fetch from 'node-fetch' -import { nodeUrl } from './params.js' - -interface SignedTx { - message: Proto.Cosmos.Transactions.Tx.TxRaw - path: string -} +import { nodeUrl } from './params' +import { SignedTx } from './types' export const broadcastTx = async (signedTx: SignedTx) => { const postOptions = { diff --git a/packages/evmosjs/src/tests/network/client.ts b/packages/evmosjs/src/tests/network/client.ts index e5119d9a..fdc197a1 100644 --- a/packages/evmosjs/src/tests/network/client.ts +++ b/packages/evmosjs/src/tests/network/client.ts @@ -1,29 +1,30 @@ import secp256k1 from 'secp256k1' -import { TxContext, TxPayload } from '@evmos/transactions' import { fetchSenderInfo } from './query' import { createTxContext } from './payload' -import { signDirect } from './sign' +import { signDirect, signEIP712 } from './sign' import { broadcastTx } from './broadcast' import { wallet } from './params' - -export interface TxResponse { - // eslint-disable-next-line camelcase - tx_response: { - code: number - txhash: string - } -} - -export type CreatePayloadFn = (context: TxContext) => TxPayload +import { TxResponse, CreatePayloadFn, SignPayloadFn } from './types' class NetworkTestClient { private nonce: number | undefined signDirectAndBroadcast = async (createTxPayload: CreatePayloadFn) => { + return this.signAndBroadcast(createTxPayload, signDirect) + } + + signEIP712AndBroadcast = async (createTxPayload: CreatePayloadFn) => { + return this.signAndBroadcast(createTxPayload, signEIP712) + } + + private signAndBroadcast = async ( + createTxPayload: CreatePayloadFn, + signPayload: SignPayloadFn, + ) => { const context = await this.createTxContext() const payload = createTxPayload(context) - const signedTx = await signDirect(payload) + const signedTx = await signPayload(payload) const response = await broadcastTx(signedTx) console.log(response) diff --git a/packages/evmosjs/src/tests/network/common.ts b/packages/evmosjs/src/tests/network/common.ts index 84b70c67..7572d7e5 100644 --- a/packages/evmosjs/src/tests/network/common.ts +++ b/packages/evmosjs/src/tests/network/common.ts @@ -1,4 +1,4 @@ -import { TxResponse } from './client' +import { TxResponse } from './types' export const expectSuccess = (response: TxResponse) => { // eslint-disable-next-line camelcase @@ -7,3 +7,8 @@ export const expectSuccess = (response: TxResponse) => { export const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) + +export const hexToBytes = (hex: string) => + Buffer.from(hex.replace('0x', ''), 'hex') + +export const base64ToBytes = (base64: string) => Buffer.from(base64, 'base64') diff --git a/packages/evmosjs/src/tests/network/eip712.ts b/packages/evmosjs/src/tests/network/eip712.ts new file mode 100644 index 00000000..fa368323 --- /dev/null +++ b/packages/evmosjs/src/tests/network/eip712.ts @@ -0,0 +1,62 @@ +import { _TypedDataEncoder as TypedDataEncoder } from '@ethersproject/hash' +import { keccak256 } from '@ethersproject/keccak256' +import { EIP712ToSign } from '@evmos/transactions' +import { hexToBytes } from './common' + +type EIP712Types = Record +type EIP712Domain = Record +type EIP712Message = Record + +const hashDomain = (payload: { + types: EIP712Types + domain: EIP712Domain +}): string => + TypedDataEncoder.hashStruct( + 'EIP712Domain', + { EIP712Domain: payload.types.EIP712Domain }, + payload.domain, + ) + +const hashMessage = (payload: { + types: EIP712Types + primaryType: string + message: EIP712Message +}): string => + TypedDataEncoder.from( + (() => { + const types = { ...payload.types } + delete types.EIP712Domain + + // EthersJS assumes the first type is the primary type + const primary = types[payload.primaryType] + if (!primary) { + throw new Error(`No matched primary type: ${payload.primaryType}`) + } + delete types[payload.primaryType] + + return { + [payload.primaryType]: primary, + ...types, + } + })(), + ).hash(payload.message) + +export const eip712Digest = (payload: EIP712ToSign) => { + const typedPayload = payload as { + types: EIP712Types + primaryType: string + domain: EIP712Domain + message: EIP712Message + } + + const raw = Buffer.concat([ + Buffer.from('19', 'hex'), + Buffer.from('01', 'hex'), + hexToBytes(hashDomain(typedPayload)), + hexToBytes(hashMessage(typedPayload)), + ]) + + const hashAsHex = keccak256(raw) + + return hexToBytes(hashAsHex) +} diff --git a/packages/evmosjs/src/tests/network/integration/convertCoin/main.ts b/packages/evmosjs/src/tests/network/integration/convertCoin/main.ts index bf15243e..66d24748 100644 --- a/packages/evmosjs/src/tests/network/integration/convertCoin/main.ts +++ b/packages/evmosjs/src/tests/network/integration/convertCoin/main.ts @@ -33,7 +33,7 @@ class ConvertCoinIntegrationClient extends NetworkClientHost { expectSuccess(response) // Wait for state changes to propogate to API - await delay(15000) + await delay(21000) // Verify state changes for each client. await proposalClient.verifyStateChange() diff --git a/packages/evmosjs/src/tests/network/integration/types.ts b/packages/evmosjs/src/tests/network/integration/types.ts index 13ff116f..f7652de1 100644 --- a/packages/evmosjs/src/tests/network/integration/types.ts +++ b/packages/evmosjs/src/tests/network/integration/types.ts @@ -1,5 +1,6 @@ /* eslint-disable max-classes-per-file */ -import NetworkClient, { TxResponse } from '../client' +import NetworkClient from '../client' +import { TxResponse } from '../types' export abstract class NetworkClientHost { public networkClient: NetworkClient diff --git a/packages/evmosjs/src/tests/network/params.ts b/packages/evmosjs/src/tests/network/params.ts index 6f364dac..f83bb419 100644 --- a/packages/evmosjs/src/tests/network/params.ts +++ b/packages/evmosjs/src/tests/network/params.ts @@ -1,13 +1,6 @@ import { Wallet } from '@ethersproject/wallet' import { Fee } from '@evmos/transactions' -const seed = - 'inmate snack that position deliver hybrid gasp open that wrestle siege goddess' -export const wallet = Wallet.fromMnemonic(seed) -export const senderHex = wallet.address -export const senderAddress = 'evmos16famsnv0hqks7z9h60cn052y4t46jhsk20792m' -export const destinationAddress = 'evmos1c8y9awp83aurchlzzql3ujkqgxcfg9s2uu7a0c' - export const nodeUrl = 'http://localhost:1317' export const jsonRPCUrl = 'http://localhost:8545' export const chainId = 9000 @@ -22,3 +15,10 @@ export const fee: Fee = { denom, gas: '10000000', } + +const seed = + 'inmate snack that position deliver hybrid gasp open that wrestle siege goddess' +export const wallet = Wallet.fromMnemonic(seed) +export const senderHex = wallet.address +export const senderAddress = 'evmos16famsnv0hqks7z9h60cn052y4t46jhsk20792m' +export const destinationAddress = 'evmos1c8y9awp83aurchlzzql3ujkqgxcfg9s2uu7a0c' diff --git a/packages/evmosjs/src/tests/network/sign.ts b/packages/evmosjs/src/tests/network/sign.ts index 76b92ee8..921cae36 100644 --- a/packages/evmosjs/src/tests/network/sign.ts +++ b/packages/evmosjs/src/tests/network/sign.ts @@ -1,33 +1,33 @@ -import { createTxRaw, Proto } from '@evmos/proto' +import { createTxRaw } from '@evmos/proto' +import { TxPayload } from '@evmos/transactions' +import { eip712Digest } from './eip712' import { wallet } from './params' +import { hexToBytes, base64ToBytes } from './common' -interface TxPayload { - signDirect: { - body: Proto.Cosmos.Transactions.Tx.TxBody - authInfo: Proto.Cosmos.Transactions.Tx.AuthInfo - signBytes: string - } -} - -// Signs a hashed digest in base64 format and returns a 64-byte -// signature (excluding the parity byte). -const signDigest32 = (digestBase64: string) => { +const signDigest32 = (digest: Buffer) => { // eslint-disable-next-line no-underscore-dangle - const signature = wallet - ._signingKey() - .signDigest(Buffer.from(digestBase64, 'base64')) - - return Buffer.concat([ - Buffer.from(signature.r.replace('0x', ''), 'hex'), - Buffer.from(signature.s.replace('0x', ''), 'hex'), - ]) -} + const signature = wallet._signingKey().signDigest(digest) -export const signDirect = async (tx: TxPayload) => { - const signatureBytes = signDigest32(tx.signDirect.signBytes) + return Buffer.concat([hexToBytes(signature.r), hexToBytes(signature.s)]) +} +const signedPayload = (tx: TxPayload, signature: Buffer) => { const bodyBytes = tx.signDirect.body.toBinary() const authInfoBytes = tx.signDirect.authInfo.toBinary() - return createTxRaw(bodyBytes, authInfoBytes, [signatureBytes]) + return createTxRaw(bodyBytes, authInfoBytes, [signature]) +} + +export const signDirect = (tx: TxPayload) => { + const digest = base64ToBytes(tx.signDirect.signBytes) + const signature = signDigest32(digest) + + return signedPayload(tx, signature) +} + +export const signEIP712 = (tx: TxPayload) => { + const digest = eip712Digest(tx.eipToSign) + const signature = signDigest32(digest) + + return signedPayload(tx, signature) } diff --git a/packages/evmosjs/src/tests/network/types.ts b/packages/evmosjs/src/tests/network/types.ts new file mode 100644 index 00000000..d069daf8 --- /dev/null +++ b/packages/evmosjs/src/tests/network/types.ts @@ -0,0 +1,18 @@ +import { Proto } from '@evmos/proto' +import { TxContext, TxPayload } from '@evmos/transactions' + +export interface TxResponse { + // eslint-disable-next-line camelcase + tx_response: { + code: number + txhash: string + } +} + +export interface SignedTx { + message: Proto.Cosmos.Transactions.Tx.TxRaw + path: string +} + +export type CreatePayloadFn = (context: TxContext) => TxPayload +export type SignPayloadFn = (payload: TxPayload) => SignedTx