From c0dbdd0a14312008d84687540b967f8c92707069 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 14:43:06 +0100 Subject: [PATCH 01/15] Ensure getAccount does not list zero balances of ERC20s --- packages/bcp/package.json | 2 ++ packages/bcp/src/cosmwasmconnection.spec.ts | 11 +++++++++++ packages/bcp/src/cosmwasmconnection.ts | 7 +++++-- scripts/cosm/deploy_erc20.js | 13 ++++++++++++- yarn.lock | 12 ++++++++++++ 5 files changed, 42 insertions(+), 3 deletions(-) diff --git a/packages/bcp/package.json b/packages/bcp/package.json index 4721a2d6..193fb5d9 100644 --- a/packages/bcp/package.json +++ b/packages/bcp/package.json @@ -44,6 +44,8 @@ "@iov/encoding": "^2.0.0-alpha.7", "@iov/stream": "^2.0.0-alpha.7", "@iov/utils": "^2.0.0-alpha.7", + "@types/bn.js": "^4.11.6", + "bn.js": "^5.1.1", "fast-deep-equal": "^3.1.1", "readonly-date": "^1.0.0", "xstream": "^11.11.0" diff --git a/packages/bcp/src/cosmwasmconnection.spec.ts b/packages/bcp/src/cosmwasmconnection.spec.ts index 6c14f0d1..dee2d922 100644 --- a/packages/bcp/src/cosmwasmconnection.spec.ts +++ b/packages/bcp/src/cosmwasmconnection.spec.ts @@ -84,6 +84,12 @@ describe("CosmWasmConnection", () => { ticker: "BASH", name: "Bash Token", }, + { + contractAddress: "cosmos18r5szma8hm93pvx6lwpjwyxruw27e0k5uw835c", + fractionalDigits: 18, + ticker: "CASH", + name: "Cash Token", + }, ], }; @@ -154,6 +160,11 @@ describe("CosmWasmConnection", () => { tokenName: "Bash Token", tokenTicker: "BASH" as TokenTicker, }, + { + fractionalDigits: 18, + tokenName: "Cash Token", + tokenTicker: "CASH" as TokenTicker, + }, { fractionalDigits: 6, tokenName: "Fee Token", diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index d8e8dbb5..7a3e50e2 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -31,6 +31,7 @@ import { import { Sha256 } from "@iov/crypto"; import { Encoding, Uint53 } from "@iov/encoding"; import { DefaultValueProducer, ValueAndUpdates } from "@iov/stream"; +import BN from "bn.js"; import equal from "fast-deep-equal"; import { ReadonlyDate } from "readonly-date"; import { Stream } from "xstream"; @@ -172,18 +173,20 @@ export class CosmWasmConnection implements BlockchainConnection { const response = JSON.parse( await this.restClient.queryContractSmart(erc20.contractAddress, queryMsg), ); + const normalizedBalance = new BN(response.balance).toString(); return { fractionalDigits: erc20.fractionalDigits, - quantity: response.balance, + quantity: normalizedBalance, tokenTicker: erc20.ticker as TokenTicker, }; }, ), ); + const nonZeroErc20Amounts = erc20Amounts.filter(amount => amount.quantity !== "0"); const balance = [ ...supportedBankCoins.map(coin => decodeAmount(this.bankTokens, coin)), - ...erc20Amounts, + ...nonZeroErc20Amounts, ].sort((a, b) => a.tokenTicker.localeCompare(b.tokenTicker)); const pubkey = !account.public_key ? undefined : decodeCosmosPubkey(account.public_key); diff --git a/scripts/cosm/deploy_erc20.js b/scripts/cosm/deploy_erc20.js index 953f433b..78901bf1 100755 --- a/scripts/cosm/deploy_erc20.js +++ b/scripts/cosm/deploy_erc20.js @@ -118,7 +118,18 @@ async function main() { }, ], }; - for (const initMsg of [initMsgAsh, initMsgBash]) { + const initMsgCash = { + decimals: 18, + name: "Cash Token", + symbol: "CASH", + initial_balances: [ + { + address: faucetAddress, + amount: "189189189000000000000000000", // 189189189 CASH + }, + ], + }; + for (const initMsg of [initMsgAsh, initMsgBash, initMsgCash]) { const initResult = await instantiateContract(client, pen, codeId, initMsg); if (initResult.code) { throw new Error(`Instantiation failed with code: ${initResult.code}; log: '${initResult.raw_log}'`); diff --git a/yarn.lock b/yarn.lock index 634f4e2f..2f12d9fe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -966,6 +966,13 @@ dependencies: "@types/babel-types" "*" +"@types/bn.js@^4.11.6": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + "@types/body-parser@*": version "1.17.1" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.1.tgz#18fcf61768fb5c30ccc508c21d6fd2e8b3bf7897" @@ -1826,6 +1833,11 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.8, bn.js@^4.4.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== +bn.js@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.1.tgz#48efc4031a9c4041b9c99c6941d903463ab62eb5" + integrity sha512-IUTD/REb78Z2eodka1QZyyEk66pciRcP6Sroka0aI3tG/iwIdYLrBD62RsubR7vqdt3WyX8p4jxeatzmRSphtA== + body-parser@^1.16.1: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" From a763b8a2eab4eb72add28e9d6a31756acc9c6481 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 16:25:59 +0100 Subject: [PATCH 02/15] Organize cosmWasmCodec test code --- packages/bcp/src/cosmwasmcodec.spec.ts | 56 ++++++++++++++------------ 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/packages/bcp/src/cosmwasmcodec.spec.ts b/packages/bcp/src/cosmwasmcodec.spec.ts index e82d3870..f3c29b4b 100644 --- a/packages/bcp/src/cosmwasmcodec.spec.ts +++ b/packages/bcp/src/cosmwasmcodec.spec.ts @@ -28,37 +28,41 @@ describe("cosmWasmCodec", () => { }); }); - it("properly generates bytes to sign", () => { - const expected = { - bytes: toUtf8( - '{"account_number":"0","chain_id":"cosmoshub-3","fee":{"amount":[{"amount":"2500","denom":"uatom"}],"gas":"100000"},"memo":"","msgs":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"35997500","denom":"uatom"}],"from_address":"cosmos1txqfn5jmcts0x0q7krdxj8tgf98tj0965vqlmq","to_address":"cosmos1nynns8ex9fq6sjjfj8k79ymkdz4sqth06xexae"}}],"sequence":"99"}', - ), - prehashType: PrehashType.Sha256, - }; - const bytesToSign = cosmWasmCodec.bytesToSign(sendTxJson, nonce); - - expect(bytesToSign).toEqual(expected); + describe("bytesToSign", () => { + it("works for SendTransaction via bank module", () => { + const expected = { + bytes: toUtf8( + '{"account_number":"0","chain_id":"cosmoshub-3","fee":{"amount":[{"amount":"2500","denom":"uatom"}],"gas":"100000"},"memo":"","msgs":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"35997500","denom":"uatom"}],"from_address":"cosmos1txqfn5jmcts0x0q7krdxj8tgf98tj0965vqlmq","to_address":"cosmos1nynns8ex9fq6sjjfj8k79ymkdz4sqth06xexae"}}],"sequence":"99"}', + ), + prehashType: PrehashType.Sha256, + }; + expect(cosmWasmCodec.bytesToSign(sendTxJson, nonce)).toEqual(expected); + }); }); - it("properly encodes transactions", () => { - const encoded = cosmWasmCodec.bytesToPost(signedTxJson); - expect(encoded).toEqual(signedTxEncodedJson); + describe("bytesToPost", () => { + it("works for SendTransaction via bank module", () => { + const encoded = cosmWasmCodec.bytesToPost(signedTxJson); + expect(encoded).toEqual(signedTxEncodedJson); + }); }); - it("throws when trying to decode a transaction without a nonce", () => { - expect(() => cosmWasmCodec.parseBytes(signedTxBin as PostableBytes, chainId)).toThrowError( - /nonce is required/i, - ); - }); + describe("parseBytes", () => { + it("throws when trying to decode a transaction without a nonce", () => { + expect(() => cosmWasmCodec.parseBytes(signedTxBin as PostableBytes, chainId)).toThrowError( + /nonce is required/i, + ); + }); - it("properly decodes transactions", () => { - const decoded = cosmWasmCodec.parseBytes(signedTxEncodedJson as PostableBytes, chainId, nonce); - expect(decoded).toEqual(signedTxJson); - }); + it("properly decodes transactions", () => { + const decoded = cosmWasmCodec.parseBytes(signedTxEncodedJson as PostableBytes, chainId, nonce); + expect(decoded).toEqual(signedTxJson); + }); - it("round trip works", () => { - const encoded = cosmWasmCodec.bytesToPost(signedTxJson); - const decoded = cosmWasmCodec.parseBytes(encoded, chainId, nonce); - expect(decoded).toEqual(signedTxJson); + it("round trip works", () => { + const encoded = cosmWasmCodec.bytesToPost(signedTxJson); + const decoded = cosmWasmCodec.parseBytes(encoded, chainId, nonce); + expect(decoded).toEqual(signedTxJson); + }); }); }); From 6dc61379b98998af526f67bd8034f28a7b1bd243 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 16:31:34 +0100 Subject: [PATCH 03/15] Deprecate default codec --- packages/bcp/src/cosmwasmcodec.spec.ts | 46 +++++++++++++-------- packages/bcp/src/cosmwasmcodec.ts | 6 ++- packages/bcp/src/cosmwasmconnection.spec.ts | 3 ++ packages/bcp/types/cosmwasmcodec.d.ts | 6 ++- 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/packages/bcp/src/cosmwasmcodec.spec.ts b/packages/bcp/src/cosmwasmcodec.spec.ts index f3c29b4b..a061f7f3 100644 --- a/packages/bcp/src/cosmwasmcodec.spec.ts +++ b/packages/bcp/src/cosmwasmcodec.spec.ts @@ -1,30 +1,40 @@ +import { CosmosAddressBech32Prefix } from "@cosmwasm/sdk"; import { PostableBytes, PrehashType } from "@iov/bcp"; import { Encoding } from "@iov/encoding"; -import { cosmWasmCodec } from "./cosmwasmcodec"; +import { CosmWasmCodec } from "./cosmwasmcodec"; import { chainId, nonce, sendTxJson, signedTxBin, signedTxEncodedJson, signedTxJson } from "./testdata.spec"; +import { BankTokens } from "./types"; const { toUtf8 } = Encoding; -describe("cosmWasmCodec", () => { +const defaultPrefix = "cosmos" as CosmosAddressBech32Prefix; + +const defaultTokens: BankTokens = [ + { + fractionalDigits: 6, + ticker: "ATOM", + denom: "uatom", + }, +]; + +describe("CosmWasmCodec", () => { + const codec = new CosmWasmCodec(defaultPrefix, defaultTokens); + describe("isValidAddress", () => { it("accepts valid addresses", () => { - expect(cosmWasmCodec.isValidAddress("cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6")).toEqual(true); - expect(cosmWasmCodec.isValidAddress("cosmosvalcons10q82zkzzmaku5lazhsvxv7hsg4ntpuhdwadmss")).toEqual( - true, - ); - expect(cosmWasmCodec.isValidAddress("cosmosvaloper17mggn4znyeyg25wd7498qxl7r2jhgue8u4qjcq")).toEqual( - true, - ); + expect(codec.isValidAddress("cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6")).toEqual(true); + expect(codec.isValidAddress("cosmosvalcons10q82zkzzmaku5lazhsvxv7hsg4ntpuhdwadmss")).toEqual(true); + expect(codec.isValidAddress("cosmosvaloper17mggn4znyeyg25wd7498qxl7r2jhgue8u4qjcq")).toEqual(true); }); it("rejects invalid addresses", () => { // Bad size - expect(cosmWasmCodec.isValidAddress("cosmos10q82zkzzmaku5lazhsvxv7hsg4ntpuhh8289f")).toEqual(false); + expect(codec.isValidAddress("cosmos10q82zkzzmaku5lazhsvxv7hsg4ntpuhh8289f")).toEqual(false); // Bad checksum - expect(cosmWasmCodec.isValidAddress("cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs7")).toEqual(false); + expect(codec.isValidAddress("cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs7")).toEqual(false); // Bad prefix - expect(cosmWasmCodec.isValidAddress("cosmot10q82zkzzmaku5lazhsvxv7hsg4ntpuhd8j5266")).toEqual(false); + expect(codec.isValidAddress("cosmot10q82zkzzmaku5lazhsvxv7hsg4ntpuhd8j5266")).toEqual(false); }); }); @@ -36,32 +46,32 @@ describe("cosmWasmCodec", () => { ), prehashType: PrehashType.Sha256, }; - expect(cosmWasmCodec.bytesToSign(sendTxJson, nonce)).toEqual(expected); + expect(codec.bytesToSign(sendTxJson, nonce)).toEqual(expected); }); }); describe("bytesToPost", () => { it("works for SendTransaction via bank module", () => { - const encoded = cosmWasmCodec.bytesToPost(signedTxJson); + const encoded = codec.bytesToPost(signedTxJson); expect(encoded).toEqual(signedTxEncodedJson); }); }); describe("parseBytes", () => { it("throws when trying to decode a transaction without a nonce", () => { - expect(() => cosmWasmCodec.parseBytes(signedTxBin as PostableBytes, chainId)).toThrowError( + expect(() => codec.parseBytes(signedTxBin as PostableBytes, chainId)).toThrowError( /nonce is required/i, ); }); it("properly decodes transactions", () => { - const decoded = cosmWasmCodec.parseBytes(signedTxEncodedJson as PostableBytes, chainId, nonce); + const decoded = codec.parseBytes(signedTxEncodedJson as PostableBytes, chainId, nonce); expect(decoded).toEqual(signedTxJson); }); it("round trip works", () => { - const encoded = cosmWasmCodec.bytesToPost(signedTxJson); - const decoded = cosmWasmCodec.parseBytes(encoded, chainId, nonce); + const encoded = codec.bytesToPost(signedTxJson); + const decoded = codec.parseBytes(encoded, chainId, nonce); expect(decoded).toEqual(signedTxJson); }); }); diff --git a/packages/bcp/src/cosmwasmcodec.ts b/packages/bcp/src/cosmwasmcodec.ts index 2df8f0ef..447694c6 100644 --- a/packages/bcp/src/cosmwasmcodec.ts +++ b/packages/bcp/src/cosmwasmcodec.ts @@ -101,5 +101,9 @@ const defaultTokens: BankTokens = [ }, ]; -/** Unconfigured codec is useful for testing only */ +/** + * Unconfigured codec is useful for testing only + * + * @deprecated use CosmWasmCodec constructor + */ export const cosmWasmCodec = new CosmWasmCodec(defaultPrefix, defaultTokens); diff --git a/packages/bcp/src/cosmwasmconnection.spec.ts b/packages/bcp/src/cosmwasmconnection.spec.ts index dee2d922..9bf71d95 100644 --- a/packages/bcp/src/cosmwasmconnection.spec.ts +++ b/packages/bcp/src/cosmwasmconnection.spec.ts @@ -184,6 +184,7 @@ describe("CosmWasmConnection", () => { it("calculates tx hash from PostableBytes", async () => { pendingWithoutCosmos(); const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); + // tslint:disable-next-line: deprecation const postable = cosmWasmCodec.bytesToPost(signedTxJson); const id = await connection.identifier(postable); expect(id).toMatch(/^[0-9A-F]{64}$/); @@ -257,6 +258,7 @@ describe("CosmWasmConnection", () => { const profile = new UserProfile(); const wallet = profile.addWallet(Secp256k1HdWallet.fromMnemonic(faucetMnemonic)); const faucet = await profile.createIdentity(wallet.id, defaultChainId, faucetPath); + // tslint:disable-next-line: deprecation const faucetAddress = cosmWasmCodec.identityToAddress(faucet); const unsigned = await connection.withDefaultFee({ @@ -322,6 +324,7 @@ describe("CosmWasmConnection", () => { const profile = new UserProfile(); const wallet = profile.addWallet(Secp256k1HdWallet.fromMnemonic(faucetMnemonic)); const faucet = await profile.createIdentity(wallet.id, defaultChainId, faucetPath); + // tslint:disable-next-line: deprecation const faucetAddress = cosmWasmCodec.identityToAddress(faucet); const unsigned = await connection.withDefaultFee({ diff --git a/packages/bcp/types/cosmwasmcodec.d.ts b/packages/bcp/types/cosmwasmcodec.d.ts index 6161b65d..e9429a00 100644 --- a/packages/bcp/types/cosmwasmcodec.d.ts +++ b/packages/bcp/types/cosmwasmcodec.d.ts @@ -23,5 +23,9 @@ export declare class CosmWasmCodec implements TxCodec { identityToAddress(identity: Identity): Address; isValidAddress(address: string): boolean; } -/** Unconfigured codec is useful for testing only */ +/** + * Unconfigured codec is useful for testing only + * + * @deprecated use CosmWasmCodec constructor + */ export declare const cosmWasmCodec: CosmWasmCodec; From 220a8ddd989f5737c0cf13fcdd1b39ef1eb2ab3f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 17:06:25 +0100 Subject: [PATCH 04/15] Add erc20 support to buildUnsignedTx --- packages/bcp/src/encode.spec.ts | 69 +++++++++++++++++++++++- packages/bcp/src/encode.ts | 96 ++++++++++++++++++++++++--------- packages/bcp/types/encode.d.ts | 14 +++-- 3 files changed, 150 insertions(+), 29 deletions(-) diff --git a/packages/bcp/src/encode.spec.ts b/packages/bcp/src/encode.spec.ts index 436430d5..220d6d2e 100644 --- a/packages/bcp/src/encode.spec.ts +++ b/packages/bcp/src/encode.spec.ts @@ -21,7 +21,7 @@ import { encodeFullSignature, encodePubkey, } from "./encode"; -import { BankTokens } from "./types"; +import { BankTokens, Erc20Token } from "./types"; const { fromBase64 } = Encoding; @@ -48,6 +48,23 @@ describe("encode", () => { denom: "uatom", }, ]; + const defaultErc20Tokens: Erc20Token[] = [ + { + contractAddress: "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5", + fractionalDigits: 5, + ticker: "ASH", + }, + { + contractAddress: "cosmos1hqrdl6wstt8qzshwc6mrumpjk9338k0lr4dqxd", + fractionalDigits: 0, + ticker: "BASH", + }, + { + contractAddress: "cosmos18r5szma8hm93pvx6lwpjwyxruw27e0k5uw835c", + fractionalDigits: 18, + ticker: "CASH", + }, + ]; describe("encodePubKey", () => { it("encodes a Secp256k1 pubkey", () => { @@ -262,6 +279,56 @@ describe("encode", () => { }, }); }); + + it("works for ERC20 send", () => { + const bashSendTx: SendTransaction = { + kind: "bcp/send", + chainId: defaultChainId, + sender: "cosmos1txqfn5jmcts0x0q7krdxj8tgf98tj0965vqlmq" as Address, + recipient: "cosmos1dddd" as Address, + memo: defaultMemo, + amount: { + fractionalDigits: 6, + quantity: "345", + tokenTicker: "BASH" as TokenTicker, + }, + fee: { + tokens: { + fractionalDigits: 6, + quantity: "3333", + tokenTicker: "ATOM" as TokenTicker, + }, + gasLimit: "234000", + }, + }; + expect(buildUnsignedTx(bashSendTx, defaultTokens, defaultErc20Tokens)).toEqual({ + type: "cosmos-sdk/StdTx", + value: { + msg: [ + { + type: "wasm/execute", + value: { + sender: "cosmos1txqfn5jmcts0x0q7krdxj8tgf98tj0965vqlmq", + contract: "cosmos1hqrdl6wstt8qzshwc6mrumpjk9338k0lr4dqxd", + msg: { + transfer: { + recipient: "cosmos1dddd", + amount: "345", + }, + }, + sent_funds: [], + }, + }, + ], + fee: { + amount: [{ denom: "uatom", amount: "3333" }], + gas: "234000", + }, + signatures: [], + memo: defaultMemo, + }, + }); + }); }); describe("buildSignedTx", () => { diff --git a/packages/bcp/src/encode.ts b/packages/bcp/src/encode.ts index a0a84d8e..52dd0670 100644 --- a/packages/bcp/src/encode.ts +++ b/packages/bcp/src/encode.ts @@ -12,7 +12,7 @@ import { } from "@iov/bcp"; import { Decimal, Encoding } from "@iov/encoding"; -import { BankTokens } from "./types"; +import { BankTokens, Erc20Token } from "./types"; const { toBase64 } = Encoding; @@ -79,37 +79,83 @@ export function encodeFullSignature(fullSignature: FullSignature): types.StdSign } } -export function buildUnsignedTx(tx: UnsignedTransaction, tokens: BankTokens): types.AminoTx { +export function buildUnsignedTx( + tx: UnsignedTransaction, + bankTokens: BankTokens, + erc20Tokens: readonly Erc20Token[] = [], +): types.AminoTx { if (!isSendTransaction(tx)) { throw new Error("Received transaction of unsupported kind"); } - return { - type: "cosmos-sdk/StdTx", - value: { - msg: [ - { - type: "cosmos-sdk/MsgSend", - value: { - from_address: tx.sender, - to_address: tx.recipient, - amount: [encodeAmount(tx.amount, tokens)], + + const matchingBankToken = bankTokens.find(t => t.ticker === tx.amount.tokenTicker); + const matchingErc20Token = erc20Tokens.find(t => t.ticker === tx.amount.tokenTicker); + + if (matchingBankToken) { + return { + type: "cosmos-sdk/StdTx", + value: { + msg: [ + { + type: "cosmos-sdk/MsgSend", + value: { + from_address: tx.sender, + to_address: tx.recipient, + amount: [encodeAmount(tx.amount, bankTokens)], + }, }, - }, - ], - memo: tx.memo || "", - signatures: [], - fee: tx.fee - ? encodeFee(tx.fee, tokens) - : { - amount: [], - gas: "", + ], + memo: tx.memo || "", + signatures: [], + fee: tx.fee + ? encodeFee(tx.fee, bankTokens) + : { + amount: [], + gas: "", + }, + }, + }; + } else if (matchingErc20Token) { + return { + type: "cosmos-sdk/StdTx", + value: { + msg: [ + { + type: "wasm/execute", + value: { + sender: tx.sender, + contract: matchingErc20Token.contractAddress, + msg: { + transfer: { + amount: tx.amount.quantity, + recipient: tx.recipient, + }, + }, + sent_funds: [], + }, }, - }, - }; + ], + memo: tx.memo || "", + signatures: [], + fee: tx.fee + ? encodeFee(tx.fee, bankTokens) + : { + amount: [], + gas: "", + }, + }, + }; + } else { + throw new Error("Cannot encode this type of transaction"); + } } -export function buildSignedTx(tx: SignedTransaction, tokens: BankTokens): types.AminoTx { - const built = buildUnsignedTx(tx.transaction, tokens); +export function buildSignedTx( + tx: SignedTransaction, + bankTokens: BankTokens, + erc20Tokens: readonly Erc20Token[] = [], +): types.AminoTx { + const built = buildUnsignedTx(tx.transaction, bankTokens, erc20Tokens); return { ...built, value: { diff --git a/packages/bcp/types/encode.d.ts b/packages/bcp/types/encode.d.ts index c55e4240..2c84e8e9 100644 --- a/packages/bcp/types/encode.d.ts +++ b/packages/bcp/types/encode.d.ts @@ -1,11 +1,19 @@ import { types } from "@cosmwasm/sdk"; import { Amount, Fee, FullSignature, PubkeyBundle, SignedTransaction, UnsignedTransaction } from "@iov/bcp"; import { Decimal } from "@iov/encoding"; -import { BankTokens } from "./types"; +import { BankTokens, Erc20Token } from "./types"; export declare function encodePubkey(pubkey: PubkeyBundle): types.PubKey; export declare function decimalToCoin(lookup: BankTokens, value: Decimal, ticker: string): types.Coin; export declare function encodeAmount(amount: Amount, tokens: BankTokens): types.Coin; export declare function encodeFee(fee: Fee, tokens: BankTokens): types.StdFee; export declare function encodeFullSignature(fullSignature: FullSignature): types.StdSignature; -export declare function buildUnsignedTx(tx: UnsignedTransaction, tokens: BankTokens): types.AminoTx; -export declare function buildSignedTx(tx: SignedTransaction, tokens: BankTokens): types.AminoTx; +export declare function buildUnsignedTx( + tx: UnsignedTransaction, + bankTokens: BankTokens, + erc20Tokens?: readonly Erc20Token[], +): types.AminoTx; +export declare function buildSignedTx( + tx: SignedTransaction, + bankTokens: BankTokens, + erc20Tokens?: readonly Erc20Token[], +): types.AminoTx; From 0fe0d3bec37c442132f1262380284dc94a9d75cd Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 17:43:40 +0100 Subject: [PATCH 05/15] 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; From 97363cd40459262983d651a311613205e4bcb27f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 17:44:01 +0100 Subject: [PATCH 06/15] Add ERC20 support in connection --- packages/bcp/src/cosmwasmconnection.spec.ts | 54 +++++++++++++++++++-- packages/bcp/src/cosmwasmconnection.ts | 33 +++++++------ 2 files changed, 67 insertions(+), 20 deletions(-) diff --git a/packages/bcp/src/cosmwasmconnection.spec.ts b/packages/bcp/src/cosmwasmconnection.spec.ts index 9bf71d95..6baa9f01 100644 --- a/packages/bcp/src/cosmwasmconnection.spec.ts +++ b/packages/bcp/src/cosmwasmconnection.spec.ts @@ -11,8 +11,8 @@ import { TokenTicker, TransactionState, } from "@iov/bcp"; -import { Secp256k1 } from "@iov/crypto"; -import { Encoding } from "@iov/encoding"; +import { Random, Secp256k1 } from "@iov/crypto"; +import { Bech32, Encoding } from "@iov/encoding"; import { HdPaths, Secp256k1HdWallet, UserProfile } from "@iov/keycontrol"; import { assert } from "@iov/utils"; @@ -29,6 +29,12 @@ function pendingWithoutCosmos(): void { } } +const defaultPrefix = "cosmos" as CosmosAddressBech32Prefix; + +function makeRandomAddress(): Address { + return Bech32.encode(defaultPrefix, Random.getBytes(20)) as Address; +} + describe("CosmWasmConnection", () => { const cosm = "COSM" as TokenTicker; const httpUrl = "http://localhost:1317"; @@ -53,8 +59,6 @@ describe("CosmWasmConnection", () => { address: "cosmos1cjsxept9rkggzxztslae9ndgpdyt2408lk850u" as Address, }; - const defaultPrefix = "cosmos" as CosmosAddressBech32Prefix; - // this is for wasmd blockchain const defaultConfig: TokenConfiguration = { bankTokens: [ @@ -447,5 +451,47 @@ describe("CosmWasmConnection", () => { connection.disconnect(); }); + + it("can send ERC20 tokens", async () => { + pendingWithoutCosmos(); + const codec = new CosmWasmCodec(defaultPrefix, defaultConfig.bankTokens, defaultConfig.erc20Tokens); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); + const profile = new UserProfile(); + const wallet = profile.addWallet(Secp256k1HdWallet.fromMnemonic(faucetMnemonic)); + const faucet = await profile.createIdentity(wallet.id, defaultChainId, faucetPath); + const faucetAddress = codec.identityToAddress(faucet); + const recipient = makeRandomAddress(); + + const unsigned = await connection.withDefaultFee({ + kind: "bcp/send", + chainId: defaultChainId, + sender: faucetAddress, + recipient: recipient, + memo: "My first payment", + amount: { + quantity: "75", + fractionalDigits: 3, // todo: BROKEN! + tokenTicker: "BASH" as TokenTicker, + }, + }); + const nonce = await connection.getNonce({ address: faucetAddress }); + const signed = await profile.signTransaction(faucet, unsigned, codec, nonce); + const postableBytes = codec.bytesToPost(signed); + const response = await connection.postTx(postableBytes); + const blockInfo = await response.blockInfo.waitFor(info => !isBlockInfoPending(info)); + expect(blockInfo.state).toEqual(TransactionState.Succeeded); + + const recipientAccount = await connection.getAccount({ address: recipient }); + assert(recipientAccount, "Recipient account must have BASH tokens"); + expect(recipientAccount.balance).toEqual([ + { + tokenTicker: "BASH" as TokenTicker, + quantity: "75", + fractionalDigits: 0, + }, + ]); + + connection.disconnect(); + }); }); }); diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index 7a3e50e2..bb52c528 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -158,12 +158,10 @@ export class CosmWasmConnection implements BlockchainConnection { public async getAccount(query: AccountQuery): Promise { const address = isPubkeyQuery(query) ? pubkeyToAddress(query.pubkey, this.addressPrefix) : query.address; const { result } = await this.restClient.authAccounts(address); - const account = result.value; - if (!account.address) { - return undefined; - } + const bankAccount = result.value; + const hasBankAccount = !!bankAccount.address; - const supportedBankCoins = account.coins.filter(({ denom }) => + const supportedBankCoins = bankAccount.coins.filter(({ denom }) => this.bankTokens.find(token => token.denom === denom), ); const erc20Amounts = await Promise.all( @@ -184,17 +182,20 @@ export class CosmWasmConnection implements BlockchainConnection { ); const nonZeroErc20Amounts = erc20Amounts.filter(amount => amount.quantity !== "0"); - const balance = [ - ...supportedBankCoins.map(coin => decodeAmount(this.bankTokens, coin)), - ...nonZeroErc20Amounts, - ].sort((a, b) => a.tokenTicker.localeCompare(b.tokenTicker)); - - const pubkey = !account.public_key ? undefined : decodeCosmosPubkey(account.public_key); - return { - address: address, - balance: balance, - pubkey: pubkey, - }; + if (!hasBankAccount && nonZeroErc20Amounts.length === 0) { + return undefined; + } else { + const balance = [ + ...supportedBankCoins.map(coin => decodeAmount(this.bankTokens, coin)), + ...nonZeroErc20Amounts, + ].sort((a, b) => a.tokenTicker.localeCompare(b.tokenTicker)); + const pubkey = !bankAccount.public_key ? undefined : decodeCosmosPubkey(bankAccount.public_key); + return { + address: address, + balance: balance, + pubkey: pubkey, + }; + } } public watchAccount(_account: AccountQuery): Stream { From 69c57c97589ef78b06ba8af4c58231547b0d916c Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 17:59:17 +0100 Subject: [PATCH 07/15] Remove default codec --- packages/bcp/src/cosmwasmcodec.ts | 17 ----------- packages/bcp/src/cosmwasmconnection.spec.ts | 31 +++++++++++++-------- packages/bcp/types/cosmwasmcodec.d.ts | 6 ---- 3 files changed, 19 insertions(+), 35 deletions(-) diff --git a/packages/bcp/src/cosmwasmcodec.ts b/packages/bcp/src/cosmwasmcodec.ts index 21a2c55c..1b2f1e76 100644 --- a/packages/bcp/src/cosmwasmcodec.ts +++ b/packages/bcp/src/cosmwasmcodec.ts @@ -96,20 +96,3 @@ export class CosmWasmCodec implements TxCodec { return isValidAddress(address); } } - -const defaultPrefix = "cosmos" as CosmosAddressBech32Prefix; - -const defaultTokens: BankTokens = [ - { - fractionalDigits: 6, - ticker: "ATOM", - denom: "uatom", - }, -]; - -/** - * Unconfigured codec is useful for testing only - * - * @deprecated use CosmWasmCodec constructor - */ -export const cosmWasmCodec = new CosmWasmCodec(defaultPrefix, defaultTokens); diff --git a/packages/bcp/src/cosmwasmconnection.spec.ts b/packages/bcp/src/cosmwasmconnection.spec.ts index 6baa9f01..769eb07a 100644 --- a/packages/bcp/src/cosmwasmconnection.spec.ts +++ b/packages/bcp/src/cosmwasmconnection.spec.ts @@ -16,7 +16,7 @@ import { Bech32, Encoding } from "@iov/encoding"; import { HdPaths, Secp256k1HdWallet, UserProfile } from "@iov/keycontrol"; import { assert } from "@iov/utils"; -import { CosmWasmCodec, cosmWasmCodec } from "./cosmwasmcodec"; +import { CosmWasmCodec } from "./cosmwasmcodec"; import { CosmWasmConnection, TokenConfiguration } from "./cosmwasmconnection"; import { signedTxJson, txId } from "./testdata.spec"; import { nonceToSequence } from "./types"; @@ -97,6 +97,17 @@ describe("CosmWasmConnection", () => { ], }; + const atomConfig: TokenConfiguration = { + bankTokens: [ + { + fractionalDigits: 6, + name: "Atom", + ticker: "ATOM", + denom: "uatom", + }, + ], + }; + describe("establish", () => { it("can connect to Cosmos via http", async () => { pendingWithoutCosmos(); @@ -187,9 +198,9 @@ describe("CosmWasmConnection", () => { describe("identifier", () => { it("calculates tx hash from PostableBytes", async () => { pendingWithoutCosmos(); - const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); - // tslint:disable-next-line: deprecation - const postable = cosmWasmCodec.bytesToPost(signedTxJson); + const codec = new CosmWasmCodec(defaultPrefix, atomConfig.bankTokens); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, atomConfig); + const postable = codec.bytesToPost(signedTxJson); const id = await connection.identifier(postable); expect(id).toMatch(/^[0-9A-F]{64}$/); expect(id).toEqual(txId); @@ -258,12 +269,12 @@ describe("CosmWasmConnection", () => { describe("integration tests", () => { it("can post and get a transaction", async () => { pendingWithoutCosmos(); + const codec = new CosmWasmCodec(defaultPrefix, defaultConfig.bankTokens); const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); const profile = new UserProfile(); const wallet = profile.addWallet(Secp256k1HdWallet.fromMnemonic(faucetMnemonic)); const faucet = await profile.createIdentity(wallet.id, defaultChainId, faucetPath); - // tslint:disable-next-line: deprecation - const faucetAddress = cosmWasmCodec.identityToAddress(faucet); + const faucetAddress = codec.identityToAddress(faucet); const unsigned = await connection.withDefaultFee({ kind: "bcp/send", @@ -278,8 +289,6 @@ describe("CosmWasmConnection", () => { }, }); const nonce = await connection.getNonce({ address: faucetAddress }); - // TODO: we need to use custom codecs everywhere - const codec = new CosmWasmCodec(defaultPrefix, defaultConfig.bankTokens); const signed = await profile.signTransaction(faucet, unsigned, codec, nonce); const postableBytes = codec.bytesToPost(signed); const response = await connection.postTx(postableBytes); @@ -324,12 +333,12 @@ describe("CosmWasmConnection", () => { it("can post and search for a transaction", async () => { pendingWithoutCosmos(); + const codec = new CosmWasmCodec(defaultPrefix, defaultConfig.bankTokens); const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); const profile = new UserProfile(); const wallet = profile.addWallet(Secp256k1HdWallet.fromMnemonic(faucetMnemonic)); const faucet = await profile.createIdentity(wallet.id, defaultChainId, faucetPath); - // tslint:disable-next-line: deprecation - const faucetAddress = cosmWasmCodec.identityToAddress(faucet); + const faucetAddress = codec.identityToAddress(faucet); const unsigned = await connection.withDefaultFee({ kind: "bcp/send", @@ -344,8 +353,6 @@ describe("CosmWasmConnection", () => { }, }); const nonce = await connection.getNonce({ address: faucetAddress }); - // TODO: we need to use custom codecs everywhere - const codec = new CosmWasmCodec(defaultPrefix, defaultConfig.bankTokens); const signed = await profile.signTransaction(faucet, unsigned, codec, nonce); const postableBytes = codec.bytesToPost(signed); const response = await connection.postTx(postableBytes); diff --git a/packages/bcp/types/cosmwasmcodec.d.ts b/packages/bcp/types/cosmwasmcodec.d.ts index c44c38b5..c7f01fb1 100644 --- a/packages/bcp/types/cosmwasmcodec.d.ts +++ b/packages/bcp/types/cosmwasmcodec.d.ts @@ -28,9 +28,3 @@ export declare class CosmWasmCodec implements TxCodec { identityToAddress(identity: Identity): Address; isValidAddress(address: string): boolean; } -/** - * Unconfigured codec is useful for testing only - * - * @deprecated use CosmWasmCodec constructor - */ -export declare const cosmWasmCodec: CosmWasmCodec; From 357fc3577e93ced9b656527996f003272d17458a Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 17:59:32 +0100 Subject: [PATCH 08/15] Increase searchTx limit to 50 --- packages/bcp/src/cosmwasmconnection.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index bb52c528..5640c72f 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -295,8 +295,10 @@ export class CosmWasmConnection implements BlockchainConnection { ): Promise | FailedTransaction)[]> { const queryString = buildQueryString(query); const chainId = this.chainId(); - const { txs: responses } = await this.restClient.txs(queryString); - return Promise.all(responses.map(response => this.parseAndPopulateTxResponse(response, chainId))); + // TODO: we need pagination support + const response = await this.restClient.txs(queryString + "&limit=50"); + const { txs } = response; + return Promise.all(txs.map(tx => this.parseAndPopulateTxResponse(tx, chainId))); } public listenTx( From b7ca6da1bf7084ae1af7b0c921532ce61bbb09a0 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 18:15:49 +0100 Subject: [PATCH 09/15] Faucet.refill: only log when logging is enabled --- packages/faucet/src/faucet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/faucet/src/faucet.ts b/packages/faucet/src/faucet.ts index 66d406b2..6a2cd3fc 100644 --- a/packages/faucet/src/faucet.ts +++ b/packages/faucet/src/faucet.ts @@ -146,7 +146,7 @@ export class Faucet { } if (jobs.length > 0) { for (const job of jobs) { - logSendJob(job); + if (this.logging) logSendJob(job); await this.send(job); await sleep(50); } From f38e07632a60c87c7cbd6b74fdc3bb157b8b98db Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 14:47:22 +0100 Subject: [PATCH 10/15] Add support for ERC20 in faucet --- packages/faucet/src/faucet.spec.ts | 52 ++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/packages/faucet/src/faucet.spec.ts b/packages/faucet/src/faucet.spec.ts index 932ba7a9..51e5abb7 100644 --- a/packages/faucet/src/faucet.spec.ts +++ b/packages/faucet/src/faucet.spec.ts @@ -38,17 +38,17 @@ const defaultConfig: TokenConfiguration = { // ticker: "ASH", // name: "Ash Token", // }, - // { - // contractAddress: "cosmos1hqrdl6wstt8qzshwc6mrumpjk9338k0lr4dqxd", - // fractionalDigits: 0, - // ticker: "BASH", - // name: "Bash Token", - // }, + { + contractAddress: "cosmos1hqrdl6wstt8qzshwc6mrumpjk9338k0lr4dqxd", + fractionalDigits: 0, + ticker: "BASH", + name: "Bash Token", + }, ], }; const defaultPrefix = "cosmos" as CosmosAddressBech32Prefix; const defaultChainId = "cosmos:testing" as ChainId; -const codec = new CosmWasmCodec(defaultPrefix, defaultConfig.bankTokens); +const codec = new CosmWasmCodec(defaultPrefix, defaultConfig.bankTokens, defaultConfig.erc20Tokens); function makeRandomAddress(): Address { return Bech32.encode(defaultPrefix, Random.getBytes(20)) as Address; @@ -81,7 +81,34 @@ describe("Faucet", () => { }); describe("send", () => { - it("can send", async () => { + it("can send bank token", async () => { + pendingWithoutCosmos(); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); + const { profile, holder } = await makeProfile(); + const faucet = new Faucet(defaultConfig, connection, codec, profile); + const recipient = makeRandomAddress(); + await faucet.send({ + amount: { + quantity: "7", + fractionalDigits: 0, + tokenTicker: "BASH" as TokenTicker, + }, + sender: holder, + recipient: recipient, + }); + const account = await connection.getAccount({ address: recipient }); + assert(account); + expect(account.balance).toEqual([ + { + quantity: "7", + fractionalDigits: 0, + tokenTicker: "BASH" as TokenTicker, + }, + ]); + connection.disconnect(); + }); + + it("can send ERC20 token", async () => { pendingWithoutCosmos(); const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); const { profile, holder } = await makeProfile(); @@ -119,6 +146,10 @@ describe("Faucet", () => { const distributorBalance = (await connection.getAccount({ pubkey: distributors[0].pubkey }))?.balance; assert(distributorBalance); expect(distributorBalance).toEqual([ + jasmine.objectContaining({ + tokenTicker: "BASH", + fractionalDigits: 0, + }), jasmine.objectContaining({ tokenTicker: "COSM", fractionalDigits: 6, @@ -128,8 +159,9 @@ describe("Faucet", () => { fractionalDigits: 6, }), ]); - expect(Number.parseInt(distributorBalance[0].quantity, 10)).toBeGreaterThanOrEqual(80_000000); + expect(Number.parseInt(distributorBalance[0].quantity, 10)).toBeGreaterThanOrEqual(80); expect(Number.parseInt(distributorBalance[1].quantity, 10)).toBeGreaterThanOrEqual(80_000000); + expect(Number.parseInt(distributorBalance[2].quantity, 10)).toBeGreaterThanOrEqual(80_000000); connection.disconnect(); }); }); @@ -181,7 +213,7 @@ describe("Faucet", () => { const { profile } = await makeProfile(); const faucet = new Faucet(defaultConfig, connection, codec, profile); const tickers = await faucet.loadTokenTickers(); - expect(tickers).toEqual(["COSM", "STAKE"]); + expect(tickers).toEqual(["BASH", "COSM", "STAKE"]); connection.disconnect(); }); }); From dad4ca4a9682a27569451b89057bd785ff52c4f5 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 18:26:23 +0100 Subject: [PATCH 11/15] Refactor amountToBankCoin --- packages/bcp/src/encode.spec.ts | 6 +++--- packages/bcp/src/encode.ts | 26 ++++++++------------------ packages/bcp/types/encode.d.ts | 4 +--- 3 files changed, 12 insertions(+), 24 deletions(-) diff --git a/packages/bcp/src/encode.spec.ts b/packages/bcp/src/encode.spec.ts index 220d6d2e..9b5138bc 100644 --- a/packages/bcp/src/encode.spec.ts +++ b/packages/bcp/src/encode.spec.ts @@ -14,9 +14,9 @@ import { import { Encoding } from "@iov/encoding"; import { + amountToBankCoin, buildSignedTx, buildUnsignedTx, - encodeAmount, encodeFee, encodeFullSignature, encodePubkey, @@ -75,9 +75,9 @@ describe("encode", () => { }); }); - describe("encodeAmount", () => { + describe("amountToBankCoin", () => { it("encodes an amount", () => { - expect(encodeAmount(defaultAmount, defaultTokens)).toEqual({ + expect(amountToBankCoin(defaultAmount, defaultTokens)).toEqual({ denom: "uatom", amount: "11657995", }); diff --git a/packages/bcp/src/encode.ts b/packages/bcp/src/encode.ts index 52dd0670..e0446735 100644 --- a/packages/bcp/src/encode.ts +++ b/packages/bcp/src/encode.ts @@ -10,7 +10,7 @@ import { SignedTransaction, UnsignedTransaction, } from "@iov/bcp"; -import { Decimal, Encoding } from "@iov/encoding"; +import { Encoding } from "@iov/encoding"; import { BankTokens, Erc20Token } from "./types"; @@ -33,30 +33,20 @@ export function encodePubkey(pubkey: PubkeyBundle): types.PubKey { } } -export function decimalToCoin(lookup: BankTokens, value: Decimal, ticker: string): types.Coin { - const match = lookup.find(token => token.ticker === ticker); - if (!match) { - throw Error(`unknown ticker: ${ticker}`); - } - if (match.fractionalDigits !== value.fractionalDigits) { +export function amountToBankCoin(amount: Amount, tokens: BankTokens): types.Coin { + const match = tokens.find(token => token.ticker === amount.tokenTicker); + if (!match) throw Error(`unknown ticker: ${amount.tokenTicker}`); + if (match.fractionalDigits !== amount.fractionalDigits) { throw new Error( "Mismatch in fractional digits between token and value. If you really want, implement a conversion here. However, this indicates a bug in the caller code.", ); } return { denom: match.denom, - amount: value.atomics, + amount: amount.quantity, }; } -export function encodeAmount(amount: Amount, tokens: BankTokens): types.Coin { - return decimalToCoin( - tokens, - Decimal.fromAtomics(amount.quantity, amount.fractionalDigits), - amount.tokenTicker, - ); -} - export function encodeFee(fee: Fee, tokens: BankTokens): types.StdFee { if (fee.tokens === undefined) { throw new Error("Cannot encode fee without tokens"); @@ -65,7 +55,7 @@ export function encodeFee(fee: Fee, tokens: BankTokens): types.StdFee { throw new Error("Cannot encode fee without gas limit"); } return { - amount: [encodeAmount(fee.tokens, tokens)], + amount: [amountToBankCoin(fee.tokens, tokens)], gas: fee.gasLimit, }; } @@ -101,7 +91,7 @@ export function buildUnsignedTx( value: { from_address: tx.sender, to_address: tx.recipient, - amount: [encodeAmount(tx.amount, bankTokens)], + amount: [amountToBankCoin(tx.amount, bankTokens)], }, }, ], diff --git a/packages/bcp/types/encode.d.ts b/packages/bcp/types/encode.d.ts index 2c84e8e9..8589e217 100644 --- a/packages/bcp/types/encode.d.ts +++ b/packages/bcp/types/encode.d.ts @@ -1,10 +1,8 @@ import { types } from "@cosmwasm/sdk"; import { Amount, Fee, FullSignature, PubkeyBundle, SignedTransaction, UnsignedTransaction } from "@iov/bcp"; -import { Decimal } from "@iov/encoding"; import { BankTokens, Erc20Token } from "./types"; export declare function encodePubkey(pubkey: PubkeyBundle): types.PubKey; -export declare function decimalToCoin(lookup: BankTokens, value: Decimal, ticker: string): types.Coin; -export declare function encodeAmount(amount: Amount, tokens: BankTokens): types.Coin; +export declare function amountToBankCoin(amount: Amount, tokens: BankTokens): types.Coin; export declare function encodeFee(fee: Fee, tokens: BankTokens): types.StdFee; export declare function encodeFullSignature(fullSignature: FullSignature): types.StdSignature; export declare function buildUnsignedTx( From e0db927bca979e2ca13535c5cc0148f3c5a1dc4f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 18:42:20 +0100 Subject: [PATCH 12/15] Add strict toErc20Amount converter --- packages/bcp/src/cosmwasmcodec.spec.ts | 2 +- packages/bcp/src/cosmwasmconnection.spec.ts | 2 +- packages/bcp/src/encode.spec.ts | 34 ++++++++++++++++++++- packages/bcp/src/encode.ts | 10 +++++- packages/bcp/types/encode.d.ts | 1 + 5 files changed, 45 insertions(+), 4 deletions(-) diff --git a/packages/bcp/src/cosmwasmcodec.spec.ts b/packages/bcp/src/cosmwasmcodec.spec.ts index d55a2939..2269887e 100644 --- a/packages/bcp/src/cosmwasmcodec.spec.ts +++ b/packages/bcp/src/cosmwasmcodec.spec.ts @@ -75,7 +75,7 @@ describe("CosmWasmCodec", () => { recipient: "cosmos1dddd" as Address, memo: "My first BASH payment", amount: { - fractionalDigits: 6, + fractionalDigits: 0, quantity: "345", tokenTicker: "BASH" as TokenTicker, }, diff --git a/packages/bcp/src/cosmwasmconnection.spec.ts b/packages/bcp/src/cosmwasmconnection.spec.ts index 769eb07a..6e28d6dc 100644 --- a/packages/bcp/src/cosmwasmconnection.spec.ts +++ b/packages/bcp/src/cosmwasmconnection.spec.ts @@ -477,7 +477,7 @@ describe("CosmWasmConnection", () => { memo: "My first payment", amount: { quantity: "75", - fractionalDigits: 3, // todo: BROKEN! + fractionalDigits: 0, tokenTicker: "BASH" as TokenTicker, }, }); diff --git a/packages/bcp/src/encode.spec.ts b/packages/bcp/src/encode.spec.ts index 9b5138bc..0e239aa4 100644 --- a/packages/bcp/src/encode.spec.ts +++ b/packages/bcp/src/encode.spec.ts @@ -20,6 +20,7 @@ import { encodeFee, encodeFullSignature, encodePubkey, + toErc20Amount, } from "./encode"; import { BankTokens, Erc20Token } from "./types"; @@ -75,6 +76,37 @@ describe("encode", () => { }); }); + describe("toErc20Amount", () => { + const [ash, bash] = defaultErc20Tokens; + + it("encodes an amount", () => { + const amount: Amount = { + quantity: "789", + fractionalDigits: 0, + tokenTicker: "BASH" as TokenTicker, + }; + expect(toErc20Amount(amount, bash)).toEqual("789"); + }); + + it("throws on ticker mismatch", () => { + const amount: Amount = { + quantity: "789", + fractionalDigits: 0, + tokenTicker: "BASH" as TokenTicker, + }; + expect(() => toErc20Amount(amount, ash)).toThrowError(/ticker mismatch/i); + }); + + it("throws on ticker mismatch", () => { + const amount: Amount = { + quantity: "789", + fractionalDigits: 2, + tokenTicker: "BASH" as TokenTicker, + }; + expect(() => toErc20Amount(amount, bash)).toThrowError(/fractional digits mismatch/i); + }); + }); + describe("amountToBankCoin", () => { it("encodes an amount", () => { expect(amountToBankCoin(defaultAmount, defaultTokens)).toEqual({ @@ -288,7 +320,7 @@ describe("encode", () => { recipient: "cosmos1dddd" as Address, memo: defaultMemo, amount: { - fractionalDigits: 6, + fractionalDigits: 0, quantity: "345", tokenTicker: "BASH" as TokenTicker, }, diff --git a/packages/bcp/src/encode.ts b/packages/bcp/src/encode.ts index e0446735..f1a61b65 100644 --- a/packages/bcp/src/encode.ts +++ b/packages/bcp/src/encode.ts @@ -33,6 +33,14 @@ export function encodePubkey(pubkey: PubkeyBundle): types.PubKey { } } +export function toErc20Amount(amount: Amount, erc20Token: Erc20Token): string { + if (amount.tokenTicker !== erc20Token.ticker) throw new Error("Ticker mismatch between amount and token"); + if (amount.fractionalDigits !== erc20Token.fractionalDigits) { + throw new Error("Fractional digits mismatch between amount and token"); + } + return amount.quantity; +} + export function amountToBankCoin(amount: Amount, tokens: BankTokens): types.Coin { const match = tokens.find(token => token.ticker === amount.tokenTicker); if (!match) throw Error(`unknown ticker: ${amount.tokenTicker}`); @@ -117,7 +125,7 @@ export function buildUnsignedTx( contract: matchingErc20Token.contractAddress, msg: { transfer: { - amount: tx.amount.quantity, + amount: toErc20Amount(tx.amount, matchingErc20Token), recipient: tx.recipient, }, }, diff --git a/packages/bcp/types/encode.d.ts b/packages/bcp/types/encode.d.ts index 8589e217..fe974fc0 100644 --- a/packages/bcp/types/encode.d.ts +++ b/packages/bcp/types/encode.d.ts @@ -2,6 +2,7 @@ import { types } from "@cosmwasm/sdk"; import { Amount, Fee, FullSignature, PubkeyBundle, SignedTransaction, UnsignedTransaction } from "@iov/bcp"; import { BankTokens, Erc20Token } from "./types"; export declare function encodePubkey(pubkey: PubkeyBundle): types.PubKey; +export declare function toErc20Amount(amount: Amount, erc20Token: Erc20Token): string; export declare function amountToBankCoin(amount: Amount, tokens: BankTokens): types.Coin; export declare function encodeFee(fee: Fee, tokens: BankTokens): types.StdFee; export declare function encodeFullSignature(fullSignature: FullSignature): types.StdSignature; From 748900f22da9c5971cc161ba25171e9d41f5a15f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 18:43:53 +0100 Subject: [PATCH 13/15] Rename to toBankCoin --- packages/bcp/src/encode.spec.ts | 6 +++--- packages/bcp/src/encode.ts | 6 +++--- packages/bcp/types/encode.d.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/bcp/src/encode.spec.ts b/packages/bcp/src/encode.spec.ts index 0e239aa4..26aac966 100644 --- a/packages/bcp/src/encode.spec.ts +++ b/packages/bcp/src/encode.spec.ts @@ -14,12 +14,12 @@ import { import { Encoding } from "@iov/encoding"; import { - amountToBankCoin, buildSignedTx, buildUnsignedTx, encodeFee, encodeFullSignature, encodePubkey, + toBankCoin, toErc20Amount, } from "./encode"; import { BankTokens, Erc20Token } from "./types"; @@ -107,9 +107,9 @@ describe("encode", () => { }); }); - describe("amountToBankCoin", () => { + describe("toBankCoin", () => { it("encodes an amount", () => { - expect(amountToBankCoin(defaultAmount, defaultTokens)).toEqual({ + expect(toBankCoin(defaultAmount, defaultTokens)).toEqual({ denom: "uatom", amount: "11657995", }); diff --git a/packages/bcp/src/encode.ts b/packages/bcp/src/encode.ts index f1a61b65..1cf8a300 100644 --- a/packages/bcp/src/encode.ts +++ b/packages/bcp/src/encode.ts @@ -41,7 +41,7 @@ export function toErc20Amount(amount: Amount, erc20Token: Erc20Token): string { return amount.quantity; } -export function amountToBankCoin(amount: Amount, tokens: BankTokens): types.Coin { +export function toBankCoin(amount: Amount, tokens: BankTokens): types.Coin { const match = tokens.find(token => token.ticker === amount.tokenTicker); if (!match) throw Error(`unknown ticker: ${amount.tokenTicker}`); if (match.fractionalDigits !== amount.fractionalDigits) { @@ -63,7 +63,7 @@ export function encodeFee(fee: Fee, tokens: BankTokens): types.StdFee { throw new Error("Cannot encode fee without gas limit"); } return { - amount: [amountToBankCoin(fee.tokens, tokens)], + amount: [toBankCoin(fee.tokens, tokens)], gas: fee.gasLimit, }; } @@ -99,7 +99,7 @@ export function buildUnsignedTx( value: { from_address: tx.sender, to_address: tx.recipient, - amount: [amountToBankCoin(tx.amount, bankTokens)], + amount: [toBankCoin(tx.amount, bankTokens)], }, }, ], diff --git a/packages/bcp/types/encode.d.ts b/packages/bcp/types/encode.d.ts index fe974fc0..bf6d0410 100644 --- a/packages/bcp/types/encode.d.ts +++ b/packages/bcp/types/encode.d.ts @@ -3,7 +3,7 @@ import { Amount, Fee, FullSignature, PubkeyBundle, SignedTransaction, UnsignedTr import { BankTokens, Erc20Token } from "./types"; export declare function encodePubkey(pubkey: PubkeyBundle): types.PubKey; export declare function toErc20Amount(amount: Amount, erc20Token: Erc20Token): string; -export declare function amountToBankCoin(amount: Amount, tokens: BankTokens): types.Coin; +export declare function toBankCoin(amount: Amount, tokens: BankTokens): types.Coin; export declare function encodeFee(fee: Fee, tokens: BankTokens): types.StdFee; export declare function encodeFullSignature(fullSignature: FullSignature): types.StdSignature; export declare function buildUnsignedTx( From a83344be94d0108981557d918e3c880a90c111c8 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 18:53:15 +0100 Subject: [PATCH 14/15] Make BASH token usable in local faucet --- packages/bcp/src/cosmwasmconnector.ts | 6 +++--- packages/bcp/types/cosmwasmconnector.d.ts | 2 +- packages/faucet/README.md | 2 +- packages/faucet/src/actions/start/start.ts | 4 ++-- packages/faucet/src/constants.ts | 12 +++++++++++- packages/faucet/src/faucet.spec.ts | 6 ------ 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/packages/bcp/src/cosmwasmconnector.ts b/packages/bcp/src/cosmwasmconnector.ts index cdf1c4b2..648568e9 100644 --- a/packages/bcp/src/cosmwasmconnector.ts +++ b/packages/bcp/src/cosmwasmconnector.ts @@ -10,12 +10,12 @@ import { CosmWasmConnection, TokenConfiguration } from "./cosmwasmconnection"; export function createCosmWasmConnector( url: string, addressPrefix: CosmosAddressBech32Prefix, - tokens: TokenConfiguration, + tokenConfig: TokenConfiguration, expectedChainId?: ChainId, ): ChainConnector { - const codec = new CosmWasmCodec(addressPrefix, tokens.bankTokens); + const codec = new CosmWasmCodec(addressPrefix, tokenConfig.bankTokens, tokenConfig.erc20Tokens); return { - establishConnection: async () => CosmWasmConnection.establish(url, addressPrefix, tokens), + establishConnection: async () => CosmWasmConnection.establish(url, addressPrefix, tokenConfig), codec: codec, expectedChainId: expectedChainId, }; diff --git a/packages/bcp/types/cosmwasmconnector.d.ts b/packages/bcp/types/cosmwasmconnector.d.ts index fbebceea..61ed984f 100644 --- a/packages/bcp/types/cosmwasmconnector.d.ts +++ b/packages/bcp/types/cosmwasmconnector.d.ts @@ -7,6 +7,6 @@ import { CosmWasmConnection, TokenConfiguration } from "./cosmwasmconnection"; export declare function createCosmWasmConnector( url: string, addressPrefix: CosmosAddressBech32Prefix, - tokens: TokenConfiguration, + tokenConfig: TokenConfiguration, expectedChainId?: ChainId, ): ChainConnector; diff --git a/packages/faucet/README.md b/packages/faucet/README.md index b2918908..3919206e 100644 --- a/packages/faucet/README.md +++ b/packages/faucet/README.md @@ -116,7 +116,7 @@ situation is different. ``` curl --header "Content-Type: application/json" \ --request POST \ - --data '{"ticker":"CASH","address":"tiov1k898u78hgs36uqw68dg7va5nfkgstu5z0fhz3f"}' \ + --data '{"ticker":"BASH","address":"cosmos1yre6ac7qfgyfgvh58ph0rgw627rhw766y430qq"}' \ http://localhost:8000/credit ``` diff --git a/packages/faucet/src/actions/start/start.ts b/packages/faucet/src/actions/start/start.ts index 408203f5..9f803cb7 100644 --- a/packages/faucet/src/actions/start/start.ts +++ b/packages/faucet/src/actions/start/start.ts @@ -19,7 +19,7 @@ export async function start(args: ReadonlyArray): Promise { const connector = createCosmWasmConnector( blockchainBaseUrl, constants.addressPrefix, - constants.tokenConfig, + constants.developmentTokenConfig, ); console.info(`Connecting to blockchain ${blockchainBaseUrl} ...`); const connection = await connector.establishConnection(); @@ -35,7 +35,7 @@ export async function start(args: ReadonlyArray): Promise { ); // Faucet - const faucet = new Faucet(constants.tokenConfig, connection, connector.codec, profile, true); + const faucet = new Faucet(constants.developmentTokenConfig, connection, connector.codec, profile, true); const chainTokens = await faucet.loadTokenTickers(); console.info("Chain tokens:", chainTokens); const accounts = await faucet.loadAccounts(); diff --git a/packages/faucet/src/constants.ts b/packages/faucet/src/constants.ts index bea2d710..3e37b1e8 100644 --- a/packages/faucet/src/constants.ts +++ b/packages/faucet/src/constants.ts @@ -6,7 +6,9 @@ export const port: number = Number.parseInt(process.env.FAUCET_PORT || "", 10) | export const mnemonic: string | undefined = process.env.FAUCET_MNEMONIC; export const addressPrefix = "cosmos"; -export const tokenConfig: TokenConfiguration = { + +/** For the local development chain */ +export const developmentTokenConfig: TokenConfiguration = { bankTokens: [ { fractionalDigits: 6, @@ -21,4 +23,12 @@ export const tokenConfig: TokenConfiguration = { denom: "ustake", }, ], + erc20Tokens: [ + { + contractAddress: "cosmos1hqrdl6wstt8qzshwc6mrumpjk9338k0lr4dqxd", + fractionalDigits: 0, + ticker: "BASH", + name: "Bash Token", + }, + ], }; diff --git a/packages/faucet/src/faucet.spec.ts b/packages/faucet/src/faucet.spec.ts index 51e5abb7..f59d6564 100644 --- a/packages/faucet/src/faucet.spec.ts +++ b/packages/faucet/src/faucet.spec.ts @@ -32,12 +32,6 @@ const defaultConfig: TokenConfiguration = { }, ], erc20Tokens: [ - // { - // contractAddress: "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5", - // fractionalDigits: 5, - // ticker: "ASH", - // name: "Ash Token", - // }, { contractAddress: "cosmos1hqrdl6wstt8qzshwc6mrumpjk9338k0lr4dqxd", fractionalDigits: 0, From 0d59011b7c14894aad6bb139ee6310122ad013da Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Mon, 10 Feb 2020 21:39:30 +0100 Subject: [PATCH 15/15] Use correct titles for faucet send tests --- packages/faucet/src/faucet.spec.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/faucet/src/faucet.spec.ts b/packages/faucet/src/faucet.spec.ts index f59d6564..c823f142 100644 --- a/packages/faucet/src/faucet.spec.ts +++ b/packages/faucet/src/faucet.spec.ts @@ -83,9 +83,9 @@ describe("Faucet", () => { const recipient = makeRandomAddress(); await faucet.send({ amount: { - quantity: "7", - fractionalDigits: 0, - tokenTicker: "BASH" as TokenTicker, + quantity: "23456", + fractionalDigits: 6, + tokenTicker: "COSM" as TokenTicker, }, sender: holder, recipient: recipient, @@ -94,9 +94,9 @@ describe("Faucet", () => { assert(account); expect(account.balance).toEqual([ { - quantity: "7", - fractionalDigits: 0, - tokenTicker: "BASH" as TokenTicker, + quantity: "23456", + fractionalDigits: 6, + tokenTicker: "COSM" as TokenTicker, }, ]); connection.disconnect(); @@ -110,9 +110,9 @@ describe("Faucet", () => { const recipient = makeRandomAddress(); await faucet.send({ amount: { - quantity: "23456", - fractionalDigits: 6, - tokenTicker: "COSM" as TokenTicker, + quantity: "7", + fractionalDigits: 0, + tokenTicker: "BASH" as TokenTicker, }, sender: holder, recipient: recipient, @@ -121,9 +121,9 @@ describe("Faucet", () => { assert(account); expect(account.balance).toEqual([ { - quantity: "23456", - fractionalDigits: 6, - tokenTicker: "COSM" as TokenTicker, + quantity: "7", + fractionalDigits: 0, + tokenTicker: "BASH" as TokenTicker, }, ]); connection.disconnect();