/* eslint-disable @typescript-eslint/camelcase */ import { Address, Algorithm, Amount, ChainId, Nonce, PubkeyBytes, SendTransaction, SignatureBytes, SignedTransaction, TokenTicker, } from "@iov/bcp"; import { Encoding } from "@iov/encoding"; import { buildSignedTx, buildUnsignedTx, encodeAmount, encodeFee, encodeFullSignature, encodePubkey, } from "./encode"; import { TokenInfos } from "./types"; const { fromBase64 } = Encoding; describe("encode", () => { const atom = "ATOM" as TokenTicker; // https://rpc.cosmos.network:26657/tx?hash=0x2268EB5AB730B45F8426078827BB5BB49819CE2B0D74B2C1D191070BADB379F1&prove=true const defaultPubkey = { algo: Algorithm.Secp256k1, data: fromBase64("AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP") as PubkeyBytes, }; const defaultChainId = "not-used" as ChainId; const defaultSender = "cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r" as Address; const defaultRecipient = "cosmos1z7g5w84ynmjyg0kqpahdjqpj7yq34v3suckp0e" as Address; const defaultAmount: Amount = { fractionalDigits: 6, quantity: "11657995", 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", () => { expect(encodePubkey(defaultPubkey)).toEqual({ type: "tendermint/PubKeySecp256k1", value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP", }); }); }); describe("encodeAmount", () => { it("encodes an amount", () => { expect(encodeAmount(defaultAmount, defaultTokens)).toEqual({ denom: "uatom", amount: "11657995", }); }); }); describe("encodeFee", () => { it("throws without tokens", () => { const fee = { gasLimit: "200000", }; expect(() => encodeFee(fee, defaultTokens)).toThrowError(/cannot encode fee without tokens/i); }); it("throws without gas limit", () => { const fee = { tokens: { fractionalDigits: 6, quantity: "5000", tokenTicker: atom, }, }; expect(() => encodeFee(fee, defaultTokens)).toThrowError(/cannot encode fee without gas limit/i); }); it("encodes a fee", () => { const fee = { tokens: { fractionalDigits: 6, quantity: "5000", tokenTicker: atom, }, gasLimit: "200000", }; expect(encodeFee(fee, defaultTokens)).toEqual({ amount: [{ denom: "uatom", amount: "5000" }], gas: "200000", }); }); }); describe("encodeFullSignature", () => { it("encodes a full signature", () => { const signature = { nonce: 0 as Nonce, pubkey: { algo: Algorithm.Secp256k1, data: fromBase64("AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP") as PubkeyBytes, }, signature: fromBase64( "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", ) as SignatureBytes, }; expect(encodeFullSignature(signature)).toEqual({ pub_key: { type: "tendermint/PubKeySecp256k1", value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP", }, signature: "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", }); }); it("compresses uncompressed public keys", () => { const signature = { nonce: 0 as Nonce, pubkey: { algo: Algorithm.Secp256k1, data: fromBase64( "BE8EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQE7WHpoHoNswYeoFkuYpYSKK4mzFzMV/dB0DVAy4lnNU=", ) as PubkeyBytes, }, signature: fromBase64( "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", ) as SignatureBytes, }; expect(encodeFullSignature(signature)).toEqual({ pub_key: { type: "tendermint/PubKeySecp256k1", value: "A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ", }, signature: "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", }); }); it("removes recovery values from signature data", () => { const signature = { nonce: 0 as Nonce, pubkey: { algo: Algorithm.Secp256k1, data: fromBase64("AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP") as PubkeyBytes, }, signature: Uint8Array.from([ ...fromBase64( "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", ), 99, ]) as SignatureBytes, }; expect(encodeFullSignature(signature)).toEqual({ pub_key: { type: "tendermint/PubKeySecp256k1", value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP", }, signature: "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", }); }); }); describe("buildUnsignedTx", () => { it("throws for unsupported transaction", () => { const tx = { kind: "bns/return_escrow", chainId: defaultChainId, escrowId: "defg", }; expect(() => buildUnsignedTx(tx, defaultTokens)).toThrowError( /received transaction of unsupported kind/i, ); }); it("builds a send transaction without fee", () => { const tx = { kind: "bcp/send", chainId: defaultChainId, amount: defaultAmount, sender: defaultSender, recipient: defaultRecipient, memo: defaultMemo, }; expect(buildUnsignedTx(tx, defaultTokens)).toEqual({ type: "cosmos-sdk/StdTx", value: { msg: [ { type: "cosmos-sdk/MsgSend", value: { from_address: "cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r", to_address: "cosmos1z7g5w84ynmjyg0kqpahdjqpj7yq34v3suckp0e", amount: [ { denom: "uatom", amount: "11657995", }, ], }, }, ], signatures: [], memo: defaultMemo, fee: { amount: [], gas: "", }, }, }); }); it("builds a send transaction with fee", () => { const tx = { kind: "bcp/send", chainId: defaultChainId, amount: defaultAmount, sender: defaultSender, recipient: defaultRecipient, memo: defaultMemo, fee: { tokens: { fractionalDigits: 6, quantity: "5000", tokenTicker: atom, }, gasLimit: "200000", }, }; expect(buildUnsignedTx(tx, defaultTokens)).toEqual({ type: "cosmos-sdk/StdTx", value: { msg: [ { type: "cosmos-sdk/MsgSend", value: { from_address: "cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r", to_address: "cosmos1z7g5w84ynmjyg0kqpahdjqpj7yq34v3suckp0e", amount: [ { denom: "uatom", amount: "11657995", }, ], }, }, ], fee: { amount: [{ denom: "uatom", amount: "5000" }], gas: "200000", }, signatures: [], memo: defaultMemo, }, }); }); }); describe("buildSignedTx", () => { it("builds a send transaction", () => { const tx: SignedTransaction = { transaction: { kind: "bcp/send", chainId: defaultChainId, amount: defaultAmount, sender: defaultSender, recipient: defaultRecipient, memo: defaultMemo, fee: { tokens: { fractionalDigits: 6, quantity: "5000", tokenTicker: atom, }, gasLimit: "200000", }, }, signatures: [ { nonce: 0 as Nonce, pubkey: { algo: Algorithm.Secp256k1, data: fromBase64("AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP") as PubkeyBytes, }, signature: fromBase64( "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", ) as SignatureBytes, }, ], }; expect(buildSignedTx(tx, defaultTokens)).toEqual({ type: "cosmos-sdk/StdTx", value: { msg: [ { type: "cosmos-sdk/MsgSend", value: { from_address: "cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r", to_address: "cosmos1z7g5w84ynmjyg0kqpahdjqpj7yq34v3suckp0e", amount: [ { denom: "uatom", amount: "11657995", }, ], }, }, ], fee: { amount: [{ denom: "uatom", amount: "5000" }], gas: "200000", }, signatures: [ { pub_key: { type: "tendermint/PubKeySecp256k1", value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP", }, signature: "1nUcIH0CLT0/nQ0mBTDrT6kMG20NY/PsH7P2gc4bpYNGLEYjBmdWevXUJouSE/9A/60QG9cYeqyTe5kFDeIPxQ==", }, ], memo: defaultMemo, }, }); }); }); });