Make bank tokens explicit
This commit is contained in:
parent
c59787d6da
commit
669905b11e
@ -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",
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
15
packages/bcp/types/cosmwasmconnection.d.ts
vendored
15
packages/bcp/types/cosmwasmconnection.d.ts
vendored
@ -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();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user