diff --git a/src/cosmoscodec.ts b/src/cosmoscodec.ts index b5497f01..9e122a8f 100644 --- a/src/cosmoscodec.ts +++ b/src/cosmoscodec.ts @@ -56,7 +56,7 @@ export class CosmosCodec implements TxCodec { public bytesToSign(unsigned: UnsignedTransaction, nonce: Nonce): SigningJob { const accountNumber = 0; const memo = (unsigned as any).memo; - const built = buildUnsignedTx(unsigned); + const built = buildUnsignedTx(unsigned, this.tokens); const signMsg = sortJson({ account_number: accountNumber.toString(), @@ -75,7 +75,7 @@ export class CosmosCodec implements TxCodec { } public bytesToPost(signed: SignedTransaction): PostableBytes { - const built = buildSignedTx(signed); + const built = buildSignedTx(signed, this.tokens); const bytes = marshalTx(built, true); return bytes as PostableBytes; } diff --git a/src/cosmosconnection.spec.ts b/src/cosmosconnection.spec.ts index e11ea319..0e5aa64a 100644 --- a/src/cosmosconnection.spec.ts +++ b/src/cosmosconnection.spec.ts @@ -14,7 +14,7 @@ import { Secp256k1 } from "@iov/crypto"; import { Encoding } from "@iov/encoding"; import { HdPaths, Secp256k1HdWallet, UserProfile } from "@iov/keycontrol"; -import { cosmosCodec } from "./cosmoscodec"; +import { cosmosCodec, CosmosCodec } from "./cosmoscodec"; import { CosmosConnection } from "./cosmosconnection"; import { CosmosBech32Prefix } from "./address"; import { TokenInfos } from "./types"; @@ -28,7 +28,7 @@ function pendingWithoutCosmos(): void { } describe("CosmosConnection", () => { - const atom = "ATOM" as TokenTicker; + const cosm = "COSM" as TokenTicker; const httpUrl = "http://localhost:1317"; const defaultChainId = "cosmos:testing" as ChainId; const defaultEmptyAddress = "cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r" as Address; @@ -191,13 +191,17 @@ describe("CosmosConnection", () => { amount: { quantity: "75000", fractionalDigits: 6, - tokenTicker: atom, + tokenTicker: cosm, }, }); const nonce = await connection.getNonce({ address: faucetAddress }); - const signed = await profile.signTransaction(faucet, unsigned, cosmosCodec, nonce); - const postableBytes = cosmosCodec.bytesToPost(signed); + // TODO: we need to use custom codecs everywhere + const codec = new CosmosCodec(defaultPrefix, defaultTokens); + console.log("nonce:", nonce); + const signed = await profile.signTransaction(faucet, unsigned, codec, nonce); + const postableBytes = codec.bytesToPost(signed); const response = await connection.postTx(postableBytes); + console.log(response); const { transactionId } = response; const blockInfo = await response.blockInfo.waitFor(info => !isBlockInfoPending(info)); expect(blockInfo.state).toEqual(TransactionState.Succeeded); @@ -250,13 +254,16 @@ describe("CosmosConnection", () => { amount: { quantity: "75000", fractionalDigits: 6, - tokenTicker: atom, + tokenTicker: cosm, }, }); const nonce = await connection.getNonce({ address: faucetAddress }); - const signed = await profile.signTransaction(faucet, unsigned, cosmosCodec, nonce); - const postableBytes = cosmosCodec.bytesToPost(signed); + // TODO: we need to use custom codecs everywhere + const codec = new CosmosCodec(defaultPrefix, defaultTokens); + const signed = await profile.signTransaction(faucet, unsigned, codec, nonce); + const postableBytes = codec.bytesToPost(signed); const response = await connection.postTx(postableBytes); + console.log(response); const { transactionId } = response; const blockInfo = await response.blockInfo.waitFor(info => !isBlockInfoPending(info)); expect(blockInfo.state).toEqual(TransactionState.Succeeded); diff --git a/src/cosmosconnection.ts b/src/cosmosconnection.ts index aec3825b..76f67f45 100644 --- a/src/cosmosconnection.ts +++ b/src/cosmosconnection.ts @@ -214,6 +214,7 @@ export class CosmosConnection implements BlockchainConnection { } public async postTx(tx: PostableBytes): Promise { + // TODO: we need to check errors here... bad chain-id breaks this const { txhash, raw_log } = await this.restClient.postTx(tx); const transactionId = txhash as TransactionId; const firstEvent: BlockInfo = { state: TransactionState.Pending }; diff --git a/src/decode.spec.ts b/src/decode.spec.ts index 8ca2e5f1..64fe28f4 100644 --- a/src/decode.spec.ts +++ b/src/decode.spec.ts @@ -19,15 +19,6 @@ import { TokenInfos } from "./types"; const { fromBase64 } = Encoding; -const defaultTokens: TokenInfos = [ - { - fractionalDigits: 6, - tokenName: "Atom (Cosmos Hub)", - tokenTicker: "ATOM" as TokenTicker, - denom: "uatom", - }, -]; - describe("decode", () => { const defaultPubkey = { algo: Algorithm.Secp256k1, @@ -61,6 +52,14 @@ describe("decode", () => { }, gasLimit: "200000", }; + const defaultTokens: TokenInfos = [ + { + fractionalDigits: 6, + tokenName: "Atom (Cosmos Hub)", + tokenTicker: "ATOM" as TokenTicker, + denom: "uatom", + }, + ]; describe("decodePubkey", () => { it("works", () => { diff --git a/src/encode.spec.ts b/src/encode.spec.ts index 4b4179e5..2eaaeb14 100644 --- a/src/encode.spec.ts +++ b/src/encode.spec.ts @@ -21,6 +21,7 @@ import { encodeFullSignature, encodePubkey, } from "./encode"; +import { TokenInfos } from "./types"; const { fromBase64 } = Encoding; @@ -40,6 +41,14 @@ describe("encode", () => { tokenTicker: atom, }; const defaultMemo = "hello cosmos hub"; + const defaultTokens: TokenInfos = [ + { + fractionalDigits: 6, + tokenName: "Atom (Cosmos Hub)", + tokenTicker: "ATOM" as TokenTicker, + denom: "uatom", + }, + ]; describe("encodePubKey", () => { it("encodes a Secp256k1 pubkey", () => { @@ -52,7 +61,7 @@ describe("encode", () => { describe("encodeAmount", () => { it("encodes an amount", () => { - expect(encodeAmount(defaultAmount)).toEqual({ + expect(encodeAmount(defaultAmount, defaultTokens)).toEqual({ denom: "uatom", amount: "11657995", }); @@ -64,7 +73,7 @@ describe("encode", () => { const fee = { gasLimit: "200000", }; - expect(() => encodeFee(fee)).toThrowError(/cannot encode fee without tokens/i); + expect(() => encodeFee(fee, defaultTokens)).toThrowError(/cannot encode fee without tokens/i); }); it("throws without gas limit", () => { @@ -75,7 +84,7 @@ describe("encode", () => { tokenTicker: atom, }, }; - expect(() => encodeFee(fee)).toThrowError(/cannot encode fee without gas limit/i); + expect(() => encodeFee(fee, defaultTokens)).toThrowError(/cannot encode fee without gas limit/i); }); it("encodes a fee", () => { @@ -87,7 +96,7 @@ describe("encode", () => { }, gasLimit: "200000", }; - expect(encodeFee(fee)).toEqual({ + expect(encodeFee(fee, defaultTokens)).toEqual({ amount: [{ denom: "uatom", amount: "5000" }], gas: "200000", }); @@ -168,7 +177,9 @@ describe("encode", () => { chainId: defaultChainId, escrowId: "defg", }; - expect(() => buildUnsignedTx(tx)).toThrowError(/received transaction of unsupported kind/i); + expect(() => buildUnsignedTx(tx, defaultTokens)).toThrowError( + /received transaction of unsupported kind/i, + ); }); it("builds a send transaction without fee", () => { @@ -180,7 +191,7 @@ describe("encode", () => { recipient: defaultRecipient, memo: defaultMemo, }; - expect(buildUnsignedTx(tx)).toEqual({ + expect(buildUnsignedTx(tx, defaultTokens)).toEqual({ type: "cosmos-sdk/StdTx", value: { msg: [ @@ -225,7 +236,7 @@ describe("encode", () => { gasLimit: "200000", }, }; - expect(buildUnsignedTx(tx)).toEqual({ + expect(buildUnsignedTx(tx, defaultTokens)).toEqual({ type: "cosmos-sdk/StdTx", value: { msg: [ @@ -286,7 +297,7 @@ describe("encode", () => { }, ], }; - expect(buildSignedTx(tx)).toEqual({ + expect(buildSignedTx(tx, defaultTokens)).toEqual({ type: "cosmos-sdk/StdTx", value: { msg: [ diff --git a/src/encode.ts b/src/encode.ts index 88ddb24d..1c098084 100644 --- a/src/encode.ts +++ b/src/encode.ts @@ -13,7 +13,7 @@ import { Secp256k1 } from "@iov/crypto"; import { Encoding } from "@iov/encoding"; import amino from "@tendermint/amino-js"; -import { AminoTx } from "./types"; +import { AminoTx, TokenInfos, amountToCoin } from "./types"; const { toBase64 } = Encoding; @@ -34,17 +34,11 @@ export function encodePubkey(pubkey: PubkeyBundle): amino.PubKey { } } -export function encodeAmount(amount: Amount): amino.Coin { - if (amount.tokenTicker !== "ATOM") { - throw new Error("Only ATOM amounts are supported"); - } - return { - denom: "uatom", - amount: amount.quantity, - }; +export function encodeAmount(amount: Amount, tokens: TokenInfos): amino.Coin { + return amountToCoin(tokens, amount); } -export function encodeFee(fee: Fee): amino.StdFee { +export function encodeFee(fee: Fee, tokens: TokenInfos): amino.StdFee { if (fee.tokens === undefined) { throw new Error("Cannot encode fee without tokens"); } @@ -52,7 +46,7 @@ export function encodeFee(fee: Fee): amino.StdFee { throw new Error("Cannot encode fee without gas limit"); } return { - amount: [encodeAmount(fee.tokens)], + amount: [encodeAmount(fee.tokens, tokens)], gas: fee.gasLimit, }; } @@ -68,7 +62,7 @@ export function encodeFullSignature(fullSignature: FullSignature): amino.StdSign }; } -export function buildUnsignedTx(tx: UnsignedTransaction): AminoTx { +export function buildUnsignedTx(tx: UnsignedTransaction, tokens: TokenInfos): AminoTx { if (!isSendTransaction(tx)) { throw new Error("Received transaction of unsupported kind"); } @@ -81,14 +75,14 @@ export function buildUnsignedTx(tx: UnsignedTransaction): AminoTx { value: { from_address: tx.sender, to_address: tx.recipient, - amount: [encodeAmount(tx.amount)], + amount: [encodeAmount(tx.amount, tokens)], }, }, ], memo: tx.memo || "", signatures: [], fee: tx.fee - ? encodeFee(tx.fee) + ? encodeFee(tx.fee, tokens) : { amount: [], gas: "", @@ -97,8 +91,8 @@ export function buildUnsignedTx(tx: UnsignedTransaction): AminoTx { }; } -export function buildSignedTx(tx: SignedTransaction): AminoTx { - const built = buildUnsignedTx(tx.transaction); +export function buildSignedTx(tx: SignedTransaction, tokens: TokenInfos): AminoTx { + const built = buildUnsignedTx(tx.transaction, tokens); return { ...built, value: {