Make bank tokens explicit

This commit is contained in:
Simon Warta 2020-02-09 14:55:59 +01:00
parent c59787d6da
commit 669905b11e
10 changed files with 62 additions and 54 deletions

View File

@ -26,13 +26,13 @@ import { pubkeyToAddress } from "./address";
import { Caip5 } from "./caip5";
import { parseTx } from "./decode";
import { buildSignedTx, buildUnsignedTx } from "./encode";
import { nonceToAccountNumber, nonceToSequence, TokenInfos } from "./types";
import { BankTokens, nonceToAccountNumber, nonceToSequence } from "./types";
export class CosmWasmCodec implements TxCodec {
private readonly addressPrefix: CosmosAddressBech32Prefix;
private readonly tokens: TokenInfos;
private readonly tokens: BankTokens;
public constructor(addressPrefix: CosmosAddressBech32Prefix, tokens: TokenInfos) {
public constructor(addressPrefix: CosmosAddressBech32Prefix, tokens: BankTokens) {
this.addressPrefix = addressPrefix;
this.tokens = tokens;
}
@ -93,7 +93,7 @@ export class CosmWasmCodec implements TxCodec {
const defaultPrefix = "cosmos" as CosmosAddressBech32Prefix;
const defaultTokens: TokenInfos = [
const defaultTokens: BankTokens = [
{
fractionalDigits: 6,
ticker: "ATOM",

View File

@ -46,20 +46,22 @@ describe("CosmWasmConnection", () => {
const defaultPrefix = "cosmos" as CosmosAddressBech32Prefix;
// this is for wasmd blockchain
const defaultTokens: TokenConfiguration = [
{
fractionalDigits: 6,
name: "Fee Token",
ticker: "COSM",
denom: "ucosm",
},
{
fractionalDigits: 6,
name: "Staking Token",
ticker: "STAKE",
denom: "ustake",
},
];
const defaultTokens: TokenConfiguration = {
bank: [
{
fractionalDigits: 6,
name: "Fee Token",
ticker: "COSM",
denom: "ucosm",
},
{
fractionalDigits: 6,
name: "Staking Token",
ticker: "STAKE",
denom: "ustake",
},
],
};
describe("establish", () => {
it("can connect to Cosmos via http", async () => {
@ -212,7 +214,7 @@ describe("CosmWasmConnection", () => {
});
const nonce = await connection.getNonce({ address: faucetAddress });
// TODO: we need to use custom codecs everywhere
const codec = new CosmWasmCodec(defaultPrefix, defaultTokens);
const codec = new CosmWasmCodec(defaultPrefix, defaultTokens.bank);
const signed = await profile.signTransaction(faucet, unsigned, codec, nonce);
const postableBytes = codec.bytesToPost(signed);
const response = await connection.postTx(postableBytes);
@ -277,7 +279,7 @@ describe("CosmWasmConnection", () => {
});
const nonce = await connection.getNonce({ address: faucetAddress });
// TODO: we need to use custom codecs everywhere
const codec = new CosmWasmCodec(defaultPrefix, defaultTokens);
const codec = new CosmWasmCodec(defaultPrefix, defaultTokens.bank);
const signed = await profile.signTransaction(faucet, unsigned, codec, nonce);
const postableBytes = codec.bytesToPost(signed);
const response = await connection.postTx(postableBytes);

View File

@ -37,7 +37,7 @@ import { Stream } from "xstream";
import { decodeCosmosPubkey, pubkeyToAddress } from "./address";
import { Caip5 } from "./caip5";
import { decodeAmount, parseTxsResponse } from "./decode";
import { accountToNonce, TokenInfo } from "./types";
import { accountToNonce, BankToken } from "./types";
const { toHex } = Encoding;
@ -69,7 +69,10 @@ function buildQueryString({
return components.filter(Boolean).join("&");
}
export type TokenConfiguration = ReadonlyArray<TokenInfo & { readonly name: string }>;
export interface TokenConfiguration {
/** Supported tokens of the Cosmos SDK bank module */
readonly bank: ReadonlyArray<BankToken & { readonly name: string }>;
}
export class CosmWasmConnection implements BlockchainConnection {
// we must know prefix and tokens a priori to understand the chain
@ -91,7 +94,7 @@ export class CosmWasmConnection implements BlockchainConnection {
private readonly restClient: RestClient;
private readonly chainData: ChainData;
private readonly addressPrefix: CosmosAddressBech32Prefix;
private readonly tokenInfo: readonly TokenInfo[];
private readonly bankTokens: readonly BankToken[];
// these are derived from arguments (cached for use in multiple functions)
private readonly primaryToken: Token;
@ -106,9 +109,9 @@ export class CosmWasmConnection implements BlockchainConnection {
this.restClient = restClient;
this.chainData = chainData;
this.addressPrefix = addressPrefix;
this.tokenInfo = tokens;
this.bankTokens = tokens.bank;
this.supportedTokens = tokens.map(info => ({
this.supportedTokens = tokens.bank.map(info => ({
tokenTicker: info.ticker as TokenTicker,
tokenName: info.name,
fractionalDigits: info.fractionalDigits,
@ -152,13 +155,13 @@ export class CosmWasmConnection implements BlockchainConnection {
return undefined;
}
const supportedCoins = account.coins.filter(({ denom }) =>
this.tokenInfo.find(token => token.denom === denom),
this.bankTokens.find(token => token.denom === denom),
);
const pubkey = !account.public_key ? undefined : decodeCosmosPubkey(account.public_key);
return {
address: address,
balance: supportedCoins.map(coin => decodeAmount(this.tokenInfo, coin)),
balance: supportedCoins.map(coin => decodeAmount(this.bankTokens, coin)),
pubkey: pubkey,
};
}
@ -321,6 +324,6 @@ export class CosmWasmConnection implements BlockchainConnection {
// this is technically not the proper nonce. maybe this causes issues for sig validation?
// leaving for now unless it causes issues
const sequence = (accountForHeight.result.value.sequence - 1) as Nonce;
return parseTxsResponse(chainId, parseInt(response.height, 10), sequence, response, this.tokenInfo);
return parseTxsResponse(chainId, parseInt(response.height, 10), sequence, response, this.bankTokens);
}
}

View File

@ -13,7 +13,7 @@ export function createCosmWasmConnector(
tokens: TokenConfiguration,
expectedChainId?: ChainId,
): ChainConnector<CosmWasmConnection> {
const codec = new CosmWasmCodec(addressPrefix, tokens);
const codec = new CosmWasmCodec(addressPrefix, tokens.bank);
return {
establishConnection: async () => CosmWasmConnection.establish(url, addressPrefix, tokens),
codec: codec,

View File

@ -15,7 +15,7 @@ import {
} from "./decode";
import { chainId, nonce, signedTxJson, txId } from "./testdata.spec";
import data from "./testdata/cosmoshub.json";
import { TokenInfos } from "./types";
import { BankTokens } from "./types";
const { fromBase64, fromHex } = Encoding;
@ -52,7 +52,7 @@ describe("decode", () => {
},
gasLimit: "200000",
};
const defaultTokens: TokenInfos = [
const defaultTokens: BankTokens = [
{
fractionalDigits: 6,
ticker: "ATOM",

View File

@ -19,7 +19,7 @@ import {
} from "@iov/bcp";
import { Decimal, Encoding } from "@iov/encoding";
import { TokenInfos } from "./types";
import { BankTokens } from "./types";
const { fromBase64 } = Encoding;
@ -52,7 +52,7 @@ export function decodeFullSignature(signature: types.StdSignature, nonce: number
};
}
export function coinToDecimal(tokens: TokenInfos, coin: types.Coin): readonly [Decimal, string] {
export function coinToDecimal(tokens: BankTokens, coin: types.Coin): readonly [Decimal, string] {
const match = tokens.find(({ denom }) => denom === coin.denom);
if (!match) {
throw Error(`unknown denom: ${coin.denom}`);
@ -61,7 +61,7 @@ export function coinToDecimal(tokens: TokenInfos, coin: types.Coin): readonly [D
return [value, match.ticker];
}
export function decodeAmount(tokens: TokenInfos, coin: types.Coin): Amount {
export function decodeAmount(tokens: BankTokens, coin: types.Coin): Amount {
const [value, ticker] = coinToDecimal(tokens, coin);
return {
quantity: value.atomics,
@ -70,7 +70,7 @@ export function decodeAmount(tokens: TokenInfos, coin: types.Coin): Amount {
};
}
export function parseMsg(msg: types.Msg, chainId: ChainId, tokens: TokenInfos): UnsignedTransaction {
export function parseMsg(msg: types.Msg, chainId: ChainId, tokens: BankTokens): UnsignedTransaction {
if (types.isMsgSend(msg)) {
if (msg.value.amount.length !== 1) {
throw new Error("Only MsgSend with one amount is supported");
@ -93,7 +93,7 @@ export function parseMsg(msg: types.Msg, chainId: ChainId, tokens: TokenInfos):
}
}
export function parseFee(fee: types.StdFee, tokens: TokenInfos): Fee {
export function parseFee(fee: types.StdFee, tokens: BankTokens): Fee {
if (fee.amount.length !== 1) {
throw new Error("Only fee with one amount is supported");
}
@ -107,7 +107,7 @@ export function parseTx(
txValue: types.StdTx,
chainId: ChainId,
nonce: Nonce,
tokens: TokenInfos,
tokens: BankTokens,
): SignedTransaction {
if (!types.isAminoStdTx(txValue)) {
throw new Error("Only Amino StdTx is supported");
@ -138,7 +138,7 @@ export function parseTxsResponse(
currentHeight: number,
nonce: Nonce,
response: TxsResponse,
tokens: TokenInfos,
tokens: BankTokens,
): ConfirmedAndSignedTransaction<UnsignedTransaction> {
const height = parseInt(response.height, 10);
return {

View File

@ -21,7 +21,7 @@ import {
encodeFullSignature,
encodePubkey,
} from "./encode";
import { TokenInfos } from "./types";
import { BankTokens } from "./types";
const { fromBase64 } = Encoding;
@ -41,7 +41,7 @@ describe("encode", () => {
tokenTicker: atom,
};
const defaultMemo = "hello cosmos hub";
const defaultTokens: TokenInfos = [
const defaultTokens: BankTokens = [
{
fractionalDigits: 6,
ticker: "ATOM",

View File

@ -12,7 +12,7 @@ import {
} from "@iov/bcp";
import { Decimal, Encoding } from "@iov/encoding";
import { TokenInfos } from "./types";
import { BankTokens } from "./types";
const { toBase64 } = Encoding;
@ -33,7 +33,7 @@ export function encodePubkey(pubkey: PubkeyBundle): types.PubKey {
}
}
export function decimalToCoin(lookup: TokenInfos, value: Decimal, ticker: string): types.Coin {
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}`);
@ -49,7 +49,7 @@ export function decimalToCoin(lookup: TokenInfos, value: Decimal, ticker: string
};
}
export function encodeAmount(amount: Amount, tokens: TokenInfos): types.Coin {
export function encodeAmount(amount: Amount, tokens: BankTokens): types.Coin {
return decimalToCoin(
tokens,
Decimal.fromAtomics(amount.quantity, amount.fractionalDigits),
@ -57,7 +57,7 @@ export function encodeAmount(amount: Amount, tokens: TokenInfos): types.Coin {
);
}
export function encodeFee(fee: Fee, tokens: TokenInfos): types.StdFee {
export function encodeFee(fee: Fee, tokens: BankTokens): types.StdFee {
if (fee.tokens === undefined) {
throw new Error("Cannot encode fee without tokens");
}
@ -79,7 +79,7 @@ export function encodeFullSignature(fullSignature: FullSignature): types.StdSign
}
}
export function buildUnsignedTx(tx: UnsignedTransaction, tokens: TokenInfos): types.AminoTx {
export function buildUnsignedTx(tx: UnsignedTransaction, tokens: BankTokens): types.AminoTx {
if (!isSendTransaction(tx)) {
throw new Error("Received transaction of unsupported kind");
}
@ -108,7 +108,7 @@ export function buildUnsignedTx(tx: UnsignedTransaction, tokens: TokenInfos): ty
};
}
export function buildSignedTx(tx: SignedTransaction, tokens: TokenInfos): types.AminoTx {
export function buildSignedTx(tx: SignedTransaction, tokens: BankTokens): types.AminoTx {
const built = buildUnsignedTx(tx.transaction, tokens);
return {
...built,

View File

@ -1,7 +1,7 @@
import { types } from "@cosmwasm/sdk";
import { Nonce } from "@iov/bcp";
export interface TokenInfo {
export interface BankToken {
readonly denom: string;
readonly ticker: string;
/**
@ -16,7 +16,7 @@ export interface TokenInfo {
readonly fractionalDigits: number;
}
export type TokenInfos = ReadonlyArray<TokenInfo>;
export type BankTokens = ReadonlyArray<BankToken>;
// tslint:disable-next-line:no-bitwise
const maxAcct = 1 << 23;

View File

@ -22,11 +22,14 @@ import {
} from "@iov/bcp";
import { Stream } from "xstream";
import { TokenInfo } from "./types";
export declare type TokenConfiguration = ReadonlyArray<
TokenInfo & {
readonly name: string;
}
>;
export interface TokenConfiguration {
/** Supported tokens of the Cosmos SDK bank module */
readonly bank: ReadonlyArray<
TokenInfo & {
readonly name: string;
}
>;
}
export declare class CosmWasmConnection implements BlockchainConnection {
static establish(
url: string,
@ -37,7 +40,7 @@ export declare class CosmWasmConnection implements BlockchainConnection {
private readonly restClient;
private readonly chainData;
private readonly addressPrefix;
private readonly tokenInfo;
private readonly bankTokens;
private readonly primaryToken;
private readonly supportedTokens;
private constructor();