diff --git a/packages/bcp/src/cosmwasmcodec.ts b/packages/bcp/src/cosmwasmcodec.ts index d2964397..584ea5e7 100644 --- a/packages/bcp/src/cosmwasmcodec.ts +++ b/packages/bcp/src/cosmwasmcodec.ts @@ -81,7 +81,7 @@ export class CosmWasmCodec implements TxCodec { throw new Error("Nonce is required"); } const parsed = unmarshalTx(bytes); - return parseSignedTx(parsed, chainId, nonce, this.bankTokens); + return parseSignedTx(parsed, chainId, nonce, this.bankTokens, this.erc20Tokens); } public identityToAddress(identity: Identity): Address { diff --git a/packages/bcp/src/cosmwasmconnection.spec.ts b/packages/bcp/src/cosmwasmconnection.spec.ts index 2301b7fb..a5505e0e 100644 --- a/packages/bcp/src/cosmwasmconnection.spec.ts +++ b/packages/bcp/src/cosmwasmconnection.spec.ts @@ -49,6 +49,7 @@ const faucet = { describe("CosmWasmConnection", () => { const cosm = "COSM" as TokenTicker; + const bash = "BASH" as TokenTicker; const httpUrl = "http://localhost:1317"; const defaultChainId = "cosmos:testing" as ChainId; const defaultEmptyAddress = "cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r" as Address; @@ -267,7 +268,7 @@ describe("CosmWasmConnection", () => { }); describe("getTx", () => { - it("can get a recently posted transaction", async () => { + it("can get a recently posted bank send transaction", async () => { pendingWithoutWasmd(); const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); const profile = new UserProfile(); @@ -318,6 +319,56 @@ describe("CosmWasmConnection", () => { connection.disconnect(); }); + it("can get a recently posted ERC20 send transaction", async () => { + pendingWithoutWasmd(); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); + const profile = new UserProfile(); + const wallet = profile.addWallet(Secp256k1HdWallet.fromMnemonic(faucet.mnemonic)); + const senderIdentity = await profile.createIdentity(wallet.id, defaultChainId, faucet.path); + const senderAddress = connection.codec.identityToAddress(senderIdentity); + + const unsigned = await connection.withDefaultFee({ + kind: "bcp/send", + chainId: defaultChainId, + sender: senderAddress, + recipient: defaultRecipient, + memo: "An ERC20 payment", + amount: { + quantity: "345", + fractionalDigits: 0, + tokenTicker: bash, + }, + }); + const nonce = await connection.getNonce({ address: senderAddress }); + const signed = await profile.signTransaction(senderIdentity, unsigned, connection.codec, nonce); + const postableBytes = connection.codec.bytesToPost(signed); + const response = await connection.postTx(postableBytes); + const { transactionId } = response; + await response.blockInfo.waitFor(info => isBlockInfoSucceeded(info)); + + const getResponse = await connection.getTx(transactionId); + expect(getResponse.transactionId).toEqual(transactionId); + assert(isConfirmedTransaction(getResponse), "Expected transaction to succeed"); + assert(getResponse.log, "Log must be available"); + const [firstLog] = JSON.parse(getResponse.log); + expect(firstLog.events.length).toEqual(1); + + const { transaction, signatures } = getResponse; + assert(isSendTransaction(transaction), "Expected send transaction"); + expect(transaction).toEqual(unsigned); + expect(signatures.length).toEqual(1); + expect(signatures[0]).toEqual({ + nonce: signed.signatures[0].nonce, + pubkey: { + algo: signed.signatures[0].pubkey.algo, + data: Secp256k1.compressPubkey(signed.signatures[0].pubkey.data), + }, + signature: Secp256k1.trimRecoveryByte(signed.signatures[0].signature), + }); + + connection.disconnect(); + }); + it("can get an old transaction", async () => { pendingWithoutWasmd(); const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index 07c6e391..6a659c6a 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -348,7 +348,13 @@ export class CosmWasmConnection implements BlockchainConnection { private parseAndPopulateTxResponseUnsigned( response: TxsResponse, ): ConfirmedTransaction | FailedTransaction { - return parseTxsResponseUnsigned(this.chainId, parseInt(response.height, 10), response, this.bankTokens); + return parseTxsResponseUnsigned( + this.chainId, + parseInt(response.height, 10), + response, + this.bankTokens, + this.erc20Tokens, + ); } private async parseAndPopulateTxResponseSigned( @@ -387,6 +393,7 @@ export class CosmWasmConnection implements BlockchainConnection { nonce, response, this.bankTokens, + this.erc20Tokens, ); } } diff --git a/packages/bcp/src/decode.spec.ts b/packages/bcp/src/decode.spec.ts index 346bf27d..80f9a531 100644 --- a/packages/bcp/src/decode.spec.ts +++ b/packages/bcp/src/decode.spec.ts @@ -264,9 +264,15 @@ describe("decode", () => { describe("parseSignedTx", () => { it("works", () => { - expect(parseSignedTx(cosmoshub.tx.value, testdata.chainId, testdata.nonce, defaultTokens)).toEqual( - testdata.signedTxJson, - ); + expect( + parseSignedTx( + cosmoshub.tx.value, + testdata.chainId, + testdata.nonce, + defaultTokens, + defaultErc20Tokens, + ), + ).toEqual(testdata.signedTxJson); }); }); @@ -287,9 +293,15 @@ describe("decode", () => { transactionId: testdata.txId, log: '[{"msg_index":0,"success":true,"log":""}]', }; - expect(parseTxsResponseUnsigned(testdata.chainId, currentHeight, txsResponse, defaultTokens)).toEqual( - expected, - ); + expect( + parseTxsResponseUnsigned( + testdata.chainId, + currentHeight, + txsResponse, + defaultTokens, + defaultErc20Tokens, + ), + ).toEqual(expected); }); }); @@ -311,7 +323,14 @@ describe("decode", () => { log: '[{"msg_index":0,"success":true,"log":""}]', }; expect( - parseTxsResponseSigned(testdata.chainId, currentHeight, testdata.nonce, txsResponse, defaultTokens), + parseTxsResponseSigned( + testdata.chainId, + currentHeight, + testdata.nonce, + txsResponse, + defaultTokens, + defaultErc20Tokens, + ), ).toEqual(expected); }); }); diff --git a/packages/bcp/src/decode.ts b/packages/bcp/src/decode.ts index 37420252..b28bbde5 100644 --- a/packages/bcp/src/decode.ts +++ b/packages/bcp/src/decode.ts @@ -168,10 +168,11 @@ export function parseSignedTx( chainId: ChainId, nonce: Nonce, tokens: BankTokens, + erc20Tokens: readonly Erc20Token[], ): SignedTransaction { const [primarySignature] = txValue.signatures.map(signature => decodeFullSignature(signature, nonce)); return { - transaction: parseUnsignedTx(txValue, chainId, tokens, []), + transaction: parseUnsignedTx(txValue, chainId, tokens, erc20Tokens), signatures: [primarySignature], }; } @@ -181,10 +182,11 @@ export function parseTxsResponseUnsigned( currentHeight: number, response: TxsResponse, tokens: BankTokens, + erc20Tokens: readonly Erc20Token[], ): ConfirmedTransaction { const height = parseInt(response.height, 10); return { - transaction: parseUnsignedTx(response.tx.value, chainId, tokens, []), + transaction: parseUnsignedTx(response.tx.value, chainId, tokens, erc20Tokens), height: height, confirmations: currentHeight - height + 1, transactionId: response.txhash as TransactionId, @@ -198,10 +200,11 @@ export function parseTxsResponseSigned( nonce: Nonce, response: TxsResponse, tokens: BankTokens, + erc20Tokens: readonly Erc20Token[], ): ConfirmedAndSignedTransaction { const height = parseInt(response.height, 10); return { - ...parseSignedTx(response.tx.value, chainId, nonce, tokens), + ...parseSignedTx(response.tx.value, chainId, nonce, tokens, erc20Tokens), height: height, confirmations: currentHeight - height + 1, transactionId: response.txhash as TransactionId, diff --git a/packages/bcp/types/decode.d.ts b/packages/bcp/types/decode.d.ts index 65105b0c..35c99db4 100644 --- a/packages/bcp/types/decode.d.ts +++ b/packages/bcp/types/decode.d.ts @@ -38,12 +38,14 @@ export declare function parseSignedTx( chainId: ChainId, nonce: Nonce, tokens: BankTokens, + erc20Tokens: readonly Erc20Token[], ): SignedTransaction; export declare function parseTxsResponseUnsigned( chainId: ChainId, currentHeight: number, response: TxsResponse, tokens: BankTokens, + erc20Tokens: readonly Erc20Token[], ): ConfirmedTransaction; export declare function parseTxsResponseSigned( chainId: ChainId, @@ -51,4 +53,5 @@ export declare function parseTxsResponseSigned( nonce: Nonce, response: TxsResponse, tokens: BankTokens, + erc20Tokens: readonly Erc20Token[], ): ConfirmedAndSignedTransaction;