From 0fe0d3bec37c442132f1262380284dc94a9d75cd Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 17:43:40 +0100 Subject: [PATCH] Add ERC20 support in codec --- packages/bcp/src/cosmwasmcodec.spec.ts | 58 ++++++++++++++++++++++++-- packages/bcp/src/cosmwasmcodec.ts | 20 +++++---- packages/bcp/types/cosmwasmcodec.d.ts | 11 +++-- 3 files changed, 75 insertions(+), 14 deletions(-) diff --git a/packages/bcp/src/cosmwasmcodec.spec.ts b/packages/bcp/src/cosmwasmcodec.spec.ts index a061f7f3..d55a2939 100644 --- a/packages/bcp/src/cosmwasmcodec.spec.ts +++ b/packages/bcp/src/cosmwasmcodec.spec.ts @@ -1,16 +1,16 @@ import { CosmosAddressBech32Prefix } from "@cosmwasm/sdk"; -import { PostableBytes, PrehashType } from "@iov/bcp"; +import { Address, PostableBytes, PrehashType, SendTransaction, TokenTicker } from "@iov/bcp"; import { Encoding } from "@iov/encoding"; import { CosmWasmCodec } from "./cosmwasmcodec"; import { chainId, nonce, sendTxJson, signedTxBin, signedTxEncodedJson, signedTxJson } from "./testdata.spec"; -import { BankTokens } from "./types"; +import { BankToken, Erc20Token } from "./types"; const { toUtf8 } = Encoding; const defaultPrefix = "cosmos" as CosmosAddressBech32Prefix; -const defaultTokens: BankTokens = [ +const defaultBankTokens: readonly BankToken[] = [ { fractionalDigits: 6, ticker: "ATOM", @@ -18,8 +18,26 @@ const defaultTokens: BankTokens = [ }, ]; +const defaultErc20Tokens: readonly Erc20Token[] = [ + { + contractAddress: "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5", + fractionalDigits: 5, + ticker: "ASH", + }, + { + contractAddress: "cosmos1hqrdl6wstt8qzshwc6mrumpjk9338k0lr4dqxd", + fractionalDigits: 0, + ticker: "BASH", + }, + { + contractAddress: "cosmos18r5szma8hm93pvx6lwpjwyxruw27e0k5uw835c", + fractionalDigits: 18, + ticker: "CASH", + }, +]; + describe("CosmWasmCodec", () => { - const codec = new CosmWasmCodec(defaultPrefix, defaultTokens); + const codec = new CosmWasmCodec(defaultPrefix, defaultBankTokens, defaultErc20Tokens); describe("isValidAddress", () => { it("accepts valid addresses", () => { @@ -48,6 +66,38 @@ describe("CosmWasmCodec", () => { }; expect(codec.bytesToSign(sendTxJson, nonce)).toEqual(expected); }); + + it("works for ERC20 send", () => { + const bashSendTx: SendTransaction = { + kind: "bcp/send", + chainId: chainId, + sender: "cosmos1txqfn5jmcts0x0q7krdxj8tgf98tj0965vqlmq" as Address, + recipient: "cosmos1dddd" as Address, + memo: "My first BASH payment", + amount: { + fractionalDigits: 6, + quantity: "345", + tokenTicker: "BASH" as TokenTicker, + }, + fee: { + tokens: { + fractionalDigits: 6, + quantity: "2500", + tokenTicker: "ATOM" as TokenTicker, + }, + gasLimit: "100000", + }, + }; + + const expected = { + bytes: toUtf8( + '{"account_number":"0","chain_id":"cosmoshub-3","fee":{"amount":[{"amount":"2500","denom":"uatom"}],"gas":"100000"},"memo":"My first BASH payment","msgs":[{"type":"wasm/execute","value":{"contract":"cosmos1hqrdl6wstt8qzshwc6mrumpjk9338k0lr4dqxd","msg":{"transfer":{"amount":"345","recipient":"cosmos1dddd"}},"sender":"cosmos1txqfn5jmcts0x0q7krdxj8tgf98tj0965vqlmq","sent_funds":[]}}],"sequence":"99"}', + ), + prehashType: PrehashType.Sha256, + }; + + expect(codec.bytesToSign(bashSendTx, nonce)).toEqual(expected); + }); }); describe("bytesToPost", () => { diff --git a/packages/bcp/src/cosmwasmcodec.ts b/packages/bcp/src/cosmwasmcodec.ts index 447694c6..21a2c55c 100644 --- a/packages/bcp/src/cosmwasmcodec.ts +++ b/packages/bcp/src/cosmwasmcodec.ts @@ -26,19 +26,25 @@ import { pubkeyToAddress } from "./address"; import { Caip5 } from "./caip5"; import { parseTx } from "./decode"; import { buildSignedTx, buildUnsignedTx } from "./encode"; -import { BankTokens, nonceToAccountNumber, nonceToSequence } from "./types"; +import { BankTokens, Erc20Token, nonceToAccountNumber, nonceToSequence } from "./types"; export class CosmWasmCodec implements TxCodec { private readonly addressPrefix: CosmosAddressBech32Prefix; - private readonly tokens: BankTokens; + private readonly bankTokens: BankTokens; + private readonly erc20Tokens: readonly Erc20Token[]; - public constructor(addressPrefix: CosmosAddressBech32Prefix, tokens: BankTokens) { + public constructor( + addressPrefix: CosmosAddressBech32Prefix, + bankTokens: BankTokens, + erc20Tokens: readonly Erc20Token[] = [], + ) { this.addressPrefix = addressPrefix; - this.tokens = tokens; + this.bankTokens = bankTokens; + this.erc20Tokens = erc20Tokens; } public bytesToSign(unsigned: UnsignedTransaction, nonce: Nonce): SigningJob { - const built = buildUnsignedTx(unsigned, this.tokens); + const built = buildUnsignedTx(unsigned, this.bankTokens, this.erc20Tokens); const nonceInfo: types.NonceInfo = { account_number: nonceToAccountNumber(nonce), @@ -61,7 +67,7 @@ export class CosmWasmCodec implements TxCodec { // PostableBytes are JSON-encoded StdTx public bytesToPost(signed: SignedTransaction): PostableBytes { // TODO: change this as well (return StdTx, not AminoTx)? - const built = buildSignedTx(signed, this.tokens); + const built = buildSignedTx(signed, this.bankTokens, this.erc20Tokens); return marshalTx(built.value) as PostableBytes; } @@ -79,7 +85,7 @@ export class CosmWasmCodec implements TxCodec { throw new Error("Nonce is required"); } const parsed = unmarshalTx(bytes); - return parseTx(parsed, chainId, nonce, this.tokens); + return parseTx(parsed, chainId, nonce, this.bankTokens); } public identityToAddress(identity: Identity): Address { diff --git a/packages/bcp/types/cosmwasmcodec.d.ts b/packages/bcp/types/cosmwasmcodec.d.ts index e9429a00..c44c38b5 100644 --- a/packages/bcp/types/cosmwasmcodec.d.ts +++ b/packages/bcp/types/cosmwasmcodec.d.ts @@ -11,11 +11,16 @@ import { TxCodec, UnsignedTransaction, } from "@iov/bcp"; -import { BankTokens } from "./types"; +import { BankTokens, Erc20Token } from "./types"; export declare class CosmWasmCodec implements TxCodec { private readonly addressPrefix; - private readonly tokens; - constructor(addressPrefix: CosmosAddressBech32Prefix, tokens: BankTokens); + private readonly bankTokens; + private readonly erc20Tokens; + constructor( + addressPrefix: CosmosAddressBech32Prefix, + bankTokens: BankTokens, + erc20Tokens?: readonly Erc20Token[], + ); bytesToSign(unsigned: UnsignedTransaction, nonce: Nonce): SigningJob; bytesToPost(signed: SignedTransaction): PostableBytes; identifier(_signed: SignedTransaction): TransactionId;