From c94a49a1fb76d138e1f75144d82826f5eca7997b Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 17 Feb 2020 14:27:38 +0100 Subject: [PATCH] Add ERC20 supprt to parseMsg --- packages/bcp/src/decode.spec.ts | 55 +++++++++++++++++++++++++++++++-- packages/bcp/src/decode.ts | 32 ++++++++++++++++++- packages/bcp/types/decode.d.ts | 3 +- 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/packages/bcp/src/decode.spec.ts b/packages/bcp/src/decode.spec.ts index dde064c6..b769c00b 100644 --- a/packages/bcp/src/decode.spec.ts +++ b/packages/bcp/src/decode.spec.ts @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/camelcase */ import { types } from "@cosmwasm/sdk"; -import { Address, Algorithm, SendTransaction, TokenTicker } from "@iov/bcp"; +import { Address, Algorithm, isSendTransaction, SendTransaction, TokenTicker } from "@iov/bcp"; import { Encoding } from "@iov/encoding"; +import { assert } from "@iov/utils"; import { decodeAmount, @@ -17,7 +18,7 @@ import { } from "./decode"; import * as testdata from "./testdata.spec"; import cosmoshub from "./testdata/cosmoshub.json"; -import { BankTokens } from "./types"; +import { BankTokens, Erc20Token } from "./types"; const { fromBase64, fromHex } = Encoding; @@ -63,6 +64,23 @@ describe("decode", () => { denom: "uatom", }, ]; + const defaultErc20Tokens: Erc20Token[] = [ + { + contractAddress: "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5", + fractionalDigits: 5, + ticker: "ASH", + }, + { + contractAddress: "cosmos1hqrdl6wstt8qzshwc6mrumpjk9338k0lr4dqxd", + fractionalDigits: 0, + ticker: "BASH", + }, + { + contractAddress: "cosmos18r5szma8hm93pvx6lwpjwyxruw27e0k5uw835c", + fractionalDigits: 18, + ticker: "CASH", + }, + ]; describe("decodePubkey", () => { it("works for secp256k1", () => { @@ -126,7 +144,7 @@ describe("decode", () => { }); describe("parseMsg", () => { - it("works", () => { + it("works for bank send transaction", () => { const msg: types.Msg = { type: "cosmos-sdk/MsgSend", value: { @@ -142,6 +160,37 @@ describe("decode", () => { }; expect(parseMsg(msg, defaultMemo, testdata.chainId, defaultTokens)).toEqual(defaultSendTransaction); }); + + it("works for ERC20 send transaction", () => { + const msg: types.MsgExecuteContract = { + type: "wasm/execute", + value: { + sender: "cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r", + contract: defaultErc20Tokens[0].contractAddress, + msg: { + transfer: { + amount: "887878484", + recipient: "cosmos1z7g5w84ynmjyg0kqpahdjqpj7yq34v3suckp0e", + }, + }, + sent_funds: [], + }, + }; + const transaction = parseMsg(msg, defaultMemo, testdata.chainId, defaultTokens, defaultErc20Tokens); + assert(isSendTransaction(transaction)); + expect(transaction).toEqual({ + kind: "bcp/send", + chainId: testdata.chainId, + sender: "cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r" as Address, + recipient: "cosmos1z7g5w84ynmjyg0kqpahdjqpj7yq34v3suckp0e" as Address, + amount: { + quantity: "887878484", + tokenTicker: "ASH" as TokenTicker, + fractionalDigits: 5, + }, + memo: defaultMemo, + }); + }); }); describe("parseFee", () => { diff --git a/packages/bcp/src/decode.ts b/packages/bcp/src/decode.ts index b57eb8bd..0aede7e5 100644 --- a/packages/bcp/src/decode.ts +++ b/packages/bcp/src/decode.ts @@ -19,8 +19,9 @@ import { UnsignedTransaction, } from "@iov/bcp"; import { Decimal, Encoding } from "@iov/encoding"; +import BN from "bn.js"; -import { BankTokens } from "./types"; +import { BankTokens, Erc20Token } from "./types"; const { fromBase64 } = Encoding; @@ -76,6 +77,7 @@ export function parseMsg( memo: string | undefined, chainId: ChainId, tokens: BankTokens, + erc20Tokens: readonly Erc20Token[] = [], ): UnsignedTransaction { if (types.isMsgSend(msg)) { if (msg.value.amount.length !== 1) { @@ -90,6 +92,34 @@ export function parseMsg( memo: memo, }; return send; + } else if (types.isMsgExecuteContract(msg)) { + const matchingTokenContract = erc20Tokens.find(t => t.contractAddress === msg.value.contract); + if (!matchingTokenContract) { + return { + chainId: chainId, + kind: "bcp/unknown", + }; + } + + const recipient: string | undefined = (msg.value.msg as any).transfer?.recipient; + if (!recipient) throw new Error("Could not read recipient"); + + const amount: string | undefined = (msg.value.msg as any).transfer?.amount; + if (!amount) throw new Error("Could not read recipient"); + + const send: SendTransaction = { + kind: "bcp/send", + chainId: chainId, + sender: msg.value.sender as Address, + recipient: recipient as Address, + amount: { + quantity: new BN(amount).toString(), + fractionalDigits: matchingTokenContract.fractionalDigits, + tokenTicker: matchingTokenContract.ticker as TokenTicker, + }, + memo: memo, + }; + return send; } else { // Unknown transaction type const unknown = { diff --git a/packages/bcp/types/decode.d.ts b/packages/bcp/types/decode.d.ts index c895d2a0..95035554 100644 --- a/packages/bcp/types/decode.d.ts +++ b/packages/bcp/types/decode.d.ts @@ -13,7 +13,7 @@ import { UnsignedTransaction, } from "@iov/bcp"; import { Decimal } from "@iov/encoding"; -import { BankTokens } from "./types"; +import { BankTokens, Erc20Token } from "./types"; export declare function decodePubkey(pubkey: types.PubKey): PubkeyBundle; export declare function decodeSignature(signature: string): SignatureBytes; export declare function decodeFullSignature(signature: types.StdSignature, nonce: number): FullSignature; @@ -24,6 +24,7 @@ export declare function parseMsg( memo: string | undefined, chainId: ChainId, tokens: BankTokens, + erc20Tokens?: readonly Erc20Token[], ): UnsignedTransaction; export declare function parseFee(fee: types.StdFee, tokens: BankTokens): Fee; export declare function parseUnsignedTx(