diff --git a/packages/bcp/package.json b/packages/bcp/package.json index b91f2ec0..4721a2d6 100644 --- a/packages/bcp/package.json +++ b/packages/bcp/package.json @@ -43,6 +43,7 @@ "@iov/crypto": "^2.0.0-alpha.7", "@iov/encoding": "^2.0.0-alpha.7", "@iov/stream": "^2.0.0-alpha.7", + "@iov/utils": "^2.0.0-alpha.7", "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 cc3a5f57..6c14f0d1 100644 --- a/packages/bcp/src/cosmwasmconnection.spec.ts +++ b/packages/bcp/src/cosmwasmconnection.spec.ts @@ -14,6 +14,7 @@ import { import { Secp256k1 } from "@iov/crypto"; import { Encoding } from "@iov/encoding"; import { HdPaths, Secp256k1HdWallet, UserProfile } from "@iov/keycontrol"; +import { assert } from "@iov/utils"; import { CosmWasmCodec, cosmWasmCodec } from "./cosmwasmcodec"; import { CosmWasmConnection, TokenConfiguration } from "./cosmwasmconnection"; @@ -33,21 +34,30 @@ describe("CosmWasmConnection", () => { const httpUrl = "http://localhost:1317"; const defaultChainId = "cosmos:testing" as ChainId; const defaultEmptyAddress = "cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r" as Address; - const defaultAddress = "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6" as Address; - const defaultPubkey = { - algo: Algorithm.Secp256k1, - data: fromBase64("A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ") as PubkeyBytes, - }; const faucetMnemonic = "economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone"; const faucetPath = HdPaths.cosmos(0); const defaultRecipient = "cosmos1t70qnpr0az8tf7py83m4ue5y89w58lkjmx0yq2" as Address; + const faucetAccount = { + pubkey: { + algo: Algorithm.Secp256k1, + data: fromBase64("A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ") as PubkeyBytes, + }, + address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6" as Address, + }; + const unusedAccount = { + pubkey: { + algo: Algorithm.Secp256k1, + data: fromBase64("ArkCaFUJ/IH+vKBmNRCdUVl3mCAhbopk9jjW4Ko4OfRQ") as PubkeyBytes, + }, + address: "cosmos1cjsxept9rkggzxztslae9ndgpdyt2408lk850u" as Address, + }; const defaultPrefix = "cosmos" as CosmosAddressBech32Prefix; // this is for wasmd blockchain - const defaultTokens: TokenConfiguration = { - bank: [ + const defaultConfig: TokenConfiguration = { + bankTokens: [ { fractionalDigits: 6, name: "Fee Token", @@ -61,12 +71,26 @@ describe("CosmWasmConnection", () => { denom: "ustake", }, ], + erc20Tokens: [ + { + contractAddress: "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5", + fractionalDigits: 5, + ticker: "ASH", + name: "Ash Token", + }, + { + contractAddress: "cosmos1hqrdl6wstt8qzshwc6mrumpjk9338k0lr4dqxd", + fractionalDigits: 0, + ticker: "BASH", + name: "Bash Token", + }, + ], }; describe("establish", () => { it("can connect to Cosmos via http", async () => { pendingWithoutCosmos(); - const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultTokens); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); expect(connection).toBeTruthy(); connection.disconnect(); }); @@ -75,7 +99,7 @@ describe("CosmWasmConnection", () => { describe("chainId", () => { it("displays the chain ID", async () => { pendingWithoutCosmos(); - const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultTokens); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); const chainId = connection.chainId(); expect(chainId).toEqual(defaultChainId); connection.disconnect(); @@ -85,7 +109,7 @@ describe("CosmWasmConnection", () => { describe("height", () => { it("displays the current height", async () => { pendingWithoutCosmos(); - const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultTokens); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); const height = await connection.height(); expect(height).toBeGreaterThan(0); connection.disconnect(); @@ -95,7 +119,7 @@ describe("CosmWasmConnection", () => { describe("getToken", () => { it("displays a given token", async () => { pendingWithoutCosmos(); - const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultTokens); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); const token = await connection.getToken("COSM" as TokenTicker); expect(token).toEqual({ fractionalDigits: 6, @@ -107,7 +131,7 @@ describe("CosmWasmConnection", () => { it("resolves to undefined if the token is not supported", async () => { pendingWithoutCosmos(); - const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultTokens); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); const token = await connection.getToken("whatever" as TokenTicker); expect(token).toBeUndefined(); connection.disconnect(); @@ -117,10 +141,19 @@ describe("CosmWasmConnection", () => { describe("getAllTokens", () => { it("resolves to a list of all supported tokens", async () => { pendingWithoutCosmos(); - const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultTokens); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); const tokens = await connection.getAllTokens(); - // TODO: make this more flexible expect(tokens).toEqual([ + { + fractionalDigits: 5, + tokenName: "Ash Token", + tokenTicker: "ASH" as TokenTicker, + }, + { + fractionalDigits: 0, + tokenName: "Bash Token", + tokenTicker: "BASH" as TokenTicker, + }, { fractionalDigits: 6, tokenName: "Fee Token", @@ -139,7 +172,7 @@ describe("CosmWasmConnection", () => { describe("identifier", () => { it("calculates tx hash from PostableBytes", async () => { pendingWithoutCosmos(); - const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultTokens); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); const postable = cosmWasmCodec.bytesToPost(signedTxJson); const id = await connection.identifier(postable); expect(id).toMatch(/^[0-9A-F]{64}$/); @@ -150,7 +183,7 @@ describe("CosmWasmConnection", () => { describe("getAccount", () => { it("gets an empty account by address", async () => { pendingWithoutCosmos(); - const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultTokens); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); const account = await connection.getAccount({ address: defaultEmptyAddress }); expect(account).toBeUndefined(); connection.disconnect(); @@ -158,35 +191,50 @@ describe("CosmWasmConnection", () => { it("gets an account by address", async () => { pendingWithoutCosmos(); - const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultTokens); - const account = await connection.getAccount({ address: defaultAddress }); - if (account === undefined) { - throw new Error("Expected account not to be undefined"); - } - expect(account.address).toEqual(defaultAddress); - // Undefined until we sign a transaction (on multiple runs against one server this will be set), allow both - if (account.pubkey !== undefined) { - expect(account.pubkey).toEqual(defaultPubkey); - } - // Starts with two tokens - expect(account.balance.length).toEqual(2); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); + const account = await connection.getAccount({ address: unusedAccount.address }); + assert(account, "Account must be defined"); + expect(account.address).toEqual(unusedAccount.address); + expect(account.pubkey).toBeUndefined(); + expect(account.balance).toEqual([ + { + tokenTicker: "ASH" as TokenTicker, + quantity: "12812345", + fractionalDigits: 5, + }, + { + tokenTicker: "BASH" as TokenTicker, + quantity: "42", + fractionalDigits: 0, + }, + { + tokenTicker: "COSM" as TokenTicker, + quantity: "1000000000", + fractionalDigits: 6, + }, + { + tokenTicker: "STAKE" as TokenTicker, + quantity: "1000000000", + fractionalDigits: 6, + }, + ]); connection.disconnect(); }); it("gets an account by pubkey", async () => { pendingWithoutCosmos(); - const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultTokens); - const account = await connection.getAccount({ pubkey: defaultPubkey }); - if (account === undefined) { - throw new Error("Expected account not to be undefined"); - } - expect(account.address).toEqual(defaultAddress); - // Undefined until we sign a transaction (on multiple runs against one server this will be set), allow both - if (account.pubkey !== undefined) { - expect(account.pubkey).toEqual(defaultPubkey); - } - // Starts with two tokens - expect(account.balance.length).toEqual(2); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); + const byAddress = await connection.getAccount({ address: unusedAccount.address }); + const byPubkey = await connection.getAccount({ pubkey: unusedAccount.pubkey }); + expect(byPubkey).toEqual(byAddress); // above we verified that by address works as expected + connection.disconnect(); + }); + + it("has a pubkey when getting account with transactions", async () => { + pendingWithoutCosmos(); + const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultConfig); + const account = await connection.getAccount({ address: faucetAccount.address }); + expect(account?.pubkey).toEqual(faucetAccount.pubkey); connection.disconnect(); }); }); @@ -194,7 +242,7 @@ describe("CosmWasmConnection", () => { describe("integration tests", () => { it("can post and get a transaction", async () => { pendingWithoutCosmos(); - const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultTokens); + 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); @@ -214,7 +262,7 @@ describe("CosmWasmConnection", () => { }); const nonce = await connection.getNonce({ address: faucetAddress }); // TODO: we need to use custom codecs everywhere - const codec = new CosmWasmCodec(defaultPrefix, defaultTokens.bank); + 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); @@ -259,7 +307,7 @@ describe("CosmWasmConnection", () => { it("can post and search for a transaction", async () => { pendingWithoutCosmos(); - const connection = await CosmWasmConnection.establish(httpUrl, defaultPrefix, defaultTokens); + 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); @@ -279,7 +327,7 @@ describe("CosmWasmConnection", () => { }); const nonce = await connection.getNonce({ address: faucetAddress }); // TODO: we need to use custom codecs everywhere - const codec = new CosmWasmCodec(defaultPrefix, defaultTokens.bank); + 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/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index bdd10989..d8e8dbb5 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -4,6 +4,7 @@ import { Account, AccountQuery, AddressQuery, + Amount, BlockchainConnection, BlockHeader, BlockId, @@ -37,7 +38,7 @@ import { Stream } from "xstream"; import { decodeCosmosPubkey, pubkeyToAddress } from "./address"; import { Caip5 } from "./caip5"; import { decodeAmount, parseTxsResponse } from "./decode"; -import { accountToNonce, BankToken } from "./types"; +import { accountToNonce, BankToken, Erc20Token } from "./types"; const { toHex } = Encoding; @@ -71,7 +72,9 @@ function buildQueryString({ export interface TokenConfiguration { /** Supported tokens of the Cosmos SDK bank module */ - readonly bank: ReadonlyArray; + readonly bankTokens: ReadonlyArray; + /** Smart contract based tokens (ERC20 compatible). Unset means empty array. */ + readonly erc20Tokens?: ReadonlyArray; } export class CosmWasmConnection implements BlockchainConnection { @@ -95,9 +98,10 @@ export class CosmWasmConnection implements BlockchainConnection { private readonly chainData: ChainData; private readonly addressPrefix: CosmosAddressBech32Prefix; private readonly bankTokens: readonly BankToken[]; + private readonly erc20Tokens: readonly Erc20Token[]; // these are derived from arguments (cached for use in multiple functions) - private readonly primaryToken: Token; + private readonly feeToken: BankToken | undefined; private readonly supportedTokens: readonly Token[]; private constructor( @@ -109,14 +113,17 @@ export class CosmWasmConnection implements BlockchainConnection { this.restClient = restClient; this.chainData = chainData; this.addressPrefix = addressPrefix; - this.bankTokens = tokens.bank; - - this.supportedTokens = tokens.bank.map(info => ({ - tokenTicker: info.ticker as TokenTicker, - tokenName: info.name, - fractionalDigits: info.fractionalDigits, - })); - this.primaryToken = this.supportedTokens[0]; + this.bankTokens = tokens.bankTokens; + this.feeToken = this.bankTokens.find(() => true); + const erc20Tokens = tokens.erc20Tokens || []; + this.erc20Tokens = erc20Tokens; + this.supportedTokens = [...tokens.bankTokens, ...erc20Tokens] + .map(info => ({ + tokenTicker: info.ticker as TokenTicker, + tokenName: info.name, + fractionalDigits: info.fractionalDigits, + })) + .sort((a, b) => a.tokenTicker.localeCompare(b.tokenTicker)); } public disconnect(): void { @@ -154,14 +161,35 @@ export class CosmWasmConnection implements BlockchainConnection { if (!account.address) { return undefined; } - const supportedCoins = account.coins.filter(({ denom }) => + + const supportedBankCoins = account.coins.filter(({ denom }) => this.bankTokens.find(token => token.denom === denom), ); + const erc20Amounts = await Promise.all( + this.erc20Tokens.map( + async (erc20): Promise => { + const queryMsg = { balance: { address: address } }; + const response = JSON.parse( + await this.restClient.queryContractSmart(erc20.contractAddress, queryMsg), + ); + return { + fractionalDigits: erc20.fractionalDigits, + quantity: response.balance, + tokenTicker: erc20.ticker as TokenTicker, + }; + }, + ), + ); + + const balance = [ + ...supportedBankCoins.map(coin => decodeAmount(this.bankTokens, coin)), + ...erc20Amounts, + ].sort((a, b) => a.tokenTicker.localeCompare(b.tokenTicker)); const pubkey = !account.public_key ? undefined : decodeCosmosPubkey(account.public_key); return { address: address, - balance: supportedCoins.map(coin => decodeAmount(this.bankTokens, coin)), + balance: balance, pubkey: pubkey, }; } @@ -283,11 +311,12 @@ export class CosmWasmConnection implements BlockchainConnection { if (!isSendTransaction(tx)) { throw new Error("Received transaction of unsupported kind."); } + if (!this.feeToken) throw new Error("This connection has no fee token configured."); return { tokens: { - fractionalDigits: this.primaryToken.fractionalDigits, + fractionalDigits: this.feeToken.fractionalDigits, quantity: "5000", - tokenTicker: this.primaryToken.tokenTicker, + tokenTicker: this.feeToken.ticker as TokenTicker, }, gasLimit: "200000", }; diff --git a/packages/bcp/src/cosmwasmconnector.ts b/packages/bcp/src/cosmwasmconnector.ts index 64889ac2..cdf1c4b2 100644 --- a/packages/bcp/src/cosmwasmconnector.ts +++ b/packages/bcp/src/cosmwasmconnector.ts @@ -13,7 +13,7 @@ export function createCosmWasmConnector( tokens: TokenConfiguration, expectedChainId?: ChainId, ): ChainConnector { - const codec = new CosmWasmCodec(addressPrefix, tokens.bank); + const codec = new CosmWasmCodec(addressPrefix, tokens.bankTokens); return { establishConnection: async () => CosmWasmConnection.establish(url, addressPrefix, tokens), codec: codec, diff --git a/packages/bcp/src/types.ts b/packages/bcp/src/types.ts index 6c3fcc22..ae679136 100644 --- a/packages/bcp/src/types.ts +++ b/packages/bcp/src/types.ts @@ -18,6 +18,21 @@ export interface BankToken { export type BankTokens = ReadonlyArray; +export interface Erc20Token { + readonly contractAddress: string; + readonly ticker: string; + /** + * The number of fractional digits the token supports. + * + * A quantity is expressed as atomic units. 10^fractionalDigits of those + * atomic units make up 1 token. + * + * E.g. in Ethereum 10^18 wei are 1 ETH and from the quantity 123000000000000000000 + * the last 18 digits are the fractional part and the rest the wole part. + */ + readonly fractionalDigits: number; +} + // tslint:disable-next-line:no-bitwise const maxAcct = 1 << 23; // tslint:disable-next-line:no-bitwise diff --git a/packages/bcp/types/cosmwasmcodec.d.ts b/packages/bcp/types/cosmwasmcodec.d.ts index ba1353a8..6161b65d 100644 --- a/packages/bcp/types/cosmwasmcodec.d.ts +++ b/packages/bcp/types/cosmwasmcodec.d.ts @@ -11,11 +11,11 @@ import { TxCodec, UnsignedTransaction, } from "@iov/bcp"; -import { TokenInfos } from "./types"; +import { BankTokens } from "./types"; export declare class CosmWasmCodec implements TxCodec { private readonly addressPrefix; private readonly tokens; - constructor(addressPrefix: CosmosAddressBech32Prefix, tokens: TokenInfos); + constructor(addressPrefix: CosmosAddressBech32Prefix, tokens: BankTokens); bytesToSign(unsigned: UnsignedTransaction, nonce: Nonce): SigningJob; bytesToPost(signed: SignedTransaction): PostableBytes; identifier(_signed: SignedTransaction): TransactionId; diff --git a/packages/bcp/types/cosmwasmconnection.d.ts b/packages/bcp/types/cosmwasmconnection.d.ts index 2413e93d..5b98a5fa 100644 --- a/packages/bcp/types/cosmwasmconnection.d.ts +++ b/packages/bcp/types/cosmwasmconnection.d.ts @@ -21,11 +21,17 @@ import { UnsignedTransaction, } from "@iov/bcp"; import { Stream } from "xstream"; -import { TokenInfo } from "./types"; +import { BankToken, Erc20Token } from "./types"; export interface TokenConfiguration { /** Supported tokens of the Cosmos SDK bank module */ - readonly bank: ReadonlyArray< - TokenInfo & { + readonly bankTokens: ReadonlyArray< + BankToken & { + readonly name: string; + } + >; + /** Smart contract based tokens (ERC20 compatible). Unset means empty array. */ + readonly erc20Tokens?: ReadonlyArray< + Erc20Token & { readonly name: string; } >; @@ -41,7 +47,8 @@ export declare class CosmWasmConnection implements BlockchainConnection { private readonly chainData; private readonly addressPrefix; private readonly bankTokens; - private readonly primaryToken; + private readonly erc20Tokens; + private readonly feeToken; private readonly supportedTokens; private constructor(); disconnect(): void; diff --git a/packages/bcp/types/decode.d.ts b/packages/bcp/types/decode.d.ts index 80de6596..4ef7d075 100644 --- a/packages/bcp/types/decode.d.ts +++ b/packages/bcp/types/decode.d.ts @@ -12,24 +12,24 @@ import { UnsignedTransaction, } from "@iov/bcp"; import { Decimal } from "@iov/encoding"; -import { TokenInfos } from "./types"; +import { BankTokens } from "./types"; export declare function decodePubkey(pubkey: types.PubKey): PubkeyBundle; export declare function decodeSignature(signature: string): SignatureBytes; export declare function decodeFullSignature(signature: types.StdSignature, nonce: number): FullSignature; -export declare function coinToDecimal(tokens: TokenInfos, coin: types.Coin): readonly [Decimal, string]; -export declare function decodeAmount(tokens: TokenInfos, coin: types.Coin): Amount; -export declare function parseMsg(msg: types.Msg, chainId: ChainId, tokens: TokenInfos): UnsignedTransaction; -export declare function parseFee(fee: types.StdFee, tokens: TokenInfos): Fee; +export declare function coinToDecimal(tokens: BankTokens, coin: types.Coin): readonly [Decimal, string]; +export declare function decodeAmount(tokens: BankTokens, coin: types.Coin): Amount; +export declare function parseMsg(msg: types.Msg, chainId: ChainId, tokens: BankTokens): UnsignedTransaction; +export declare function parseFee(fee: types.StdFee, tokens: BankTokens): Fee; export declare function parseTx( txValue: types.StdTx, chainId: ChainId, nonce: Nonce, - tokens: TokenInfos, + tokens: BankTokens, ): SignedTransaction; export declare function parseTxsResponse( chainId: ChainId, currentHeight: number, nonce: Nonce, response: TxsResponse, - tokens: TokenInfos, + tokens: BankTokens, ): ConfirmedAndSignedTransaction; diff --git a/packages/bcp/types/encode.d.ts b/packages/bcp/types/encode.d.ts index 7af2c690..c55e4240 100644 --- a/packages/bcp/types/encode.d.ts +++ b/packages/bcp/types/encode.d.ts @@ -1,11 +1,11 @@ import { types } from "@cosmwasm/sdk"; import { Amount, Fee, FullSignature, PubkeyBundle, SignedTransaction, UnsignedTransaction } from "@iov/bcp"; import { Decimal } from "@iov/encoding"; -import { TokenInfos } from "./types"; +import { BankTokens } from "./types"; export declare function encodePubkey(pubkey: PubkeyBundle): types.PubKey; -export declare function decimalToCoin(lookup: TokenInfos, value: Decimal, ticker: string): types.Coin; -export declare function encodeAmount(amount: Amount, tokens: TokenInfos): types.Coin; -export declare function encodeFee(fee: Fee, tokens: TokenInfos): types.StdFee; +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: TokenInfos): types.AminoTx; -export declare function buildSignedTx(tx: SignedTransaction, tokens: TokenInfos): types.AminoTx; +export declare function buildUnsignedTx(tx: UnsignedTransaction, tokens: BankTokens): types.AminoTx; +export declare function buildSignedTx(tx: SignedTransaction, tokens: BankTokens): types.AminoTx; diff --git a/packages/bcp/types/types.d.ts b/packages/bcp/types/types.d.ts index e5a6e631..35863a03 100644 --- a/packages/bcp/types/types.d.ts +++ b/packages/bcp/types/types.d.ts @@ -1,6 +1,6 @@ import { types } from "@cosmwasm/sdk"; import { Nonce } from "@iov/bcp"; -export interface TokenInfo { +export interface BankToken { readonly denom: string; readonly ticker: string; /** @@ -14,7 +14,21 @@ export interface TokenInfo { */ readonly fractionalDigits: number; } -export declare type TokenInfos = ReadonlyArray; +export declare type BankTokens = ReadonlyArray; +export interface Erc20Token { + readonly contractAddress: string; + readonly ticker: string; + /** + * The number of fractional digits the token supports. + * + * A quantity is expressed as atomic units. 10^fractionalDigits of those + * atomic units make up 1 token. + * + * E.g. in Ethereum 10^18 wei are 1 ETH and from the quantity 123000000000000000000 + * the last 18 digits are the fractional part and the rest the wole part. + */ + readonly fractionalDigits: number; +} export declare function accountToNonce({ account_number: account, sequence }: types.NonceInfo): Nonce; export declare function nonceToAccountNumber(nonce: Nonce): number; export declare function nonceToSequence(nonce: Nonce): number; diff --git a/scripts/cosm/README.md b/scripts/cosm/README.md index 07ea4583..09173dfd 100644 --- a/scripts/cosm/README.md +++ b/scripts/cosm/README.md @@ -83,7 +83,9 @@ You should get output matching the following: 1. Faucet
economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone
+ A08EGB7ro1ORuFhjOnZcSgwYlpe0DSFjVNUIkNNQxwKQ
cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6 2. Unused: for testing account state; this account never changes balances or nonces
oyster design unusual machine spread century engine gravity focus cave carry slot
+ ArkCaFUJ/IH+vKBmNRCdUVl3mCAhbopk9jjW4Ko4OfRQ
cosmos1cjsxept9rkggzxztslae9ndgpdyt2408lk850u