From 1bf2d9b287d7d83bbd01198172619052f23e2414 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 31 Jan 2020 17:50:10 +0100 Subject: [PATCH 1/9] Extract RestClient, TxsResponse, AminoTx into @cosmwasm/sdk --- packages/bcp/package.json | 2 +- packages/bcp/src/cosmwasmconnection.ts | 2 +- packages/bcp/src/decode.ts | 2 +- packages/bcp/src/encode.ts | 3 +- packages/bcp/src/types.ts | 2 - packages/bcp/types/decode.d.ts | 2 +- packages/bcp/types/encode.d.ts | 3 +- packages/bcp/types/types.d.ts | 3 -- packages/sdk/.eslintignore | 1 + packages/sdk/.gitignore | 3 ++ .../sdk/jasmine-spec-reporter.config.json | 12 +++++ packages/sdk/jasmine-testrunner.js | 26 +++++++++ packages/sdk/karma.conf.js | 54 +++++++++++++++++++ packages/sdk/nonces/README.txt | 1 + packages/sdk/package.json | 46 ++++++++++++++++ packages/sdk/src/index.ts | 2 + packages/{bcp => sdk}/src/restclient.ts | 7 ++- packages/sdk/src/types.ts | 3 ++ packages/sdk/tsconfig.json | 12 +++++ packages/sdk/tslint.json | 3 ++ packages/sdk/typedoc.js | 14 +++++ packages/sdk/types/index.d.ts | 2 + packages/{bcp => sdk}/types/restclient.d.ts | 7 ++- packages/sdk/types/types.d.ts | 4 ++ packages/sdk/webpack.web.config.js | 19 +++++++ 25 files changed, 216 insertions(+), 19 deletions(-) create mode 120000 packages/sdk/.eslintignore create mode 100644 packages/sdk/.gitignore create mode 100644 packages/sdk/jasmine-spec-reporter.config.json create mode 100755 packages/sdk/jasmine-testrunner.js create mode 100644 packages/sdk/karma.conf.js create mode 100644 packages/sdk/nonces/README.txt create mode 100644 packages/sdk/package.json create mode 100644 packages/sdk/src/index.ts rename packages/{bcp => sdk}/src/restclient.ts (93%) create mode 100644 packages/sdk/src/types.ts create mode 100644 packages/sdk/tsconfig.json create mode 100644 packages/sdk/tslint.json create mode 100644 packages/sdk/typedoc.js create mode 100644 packages/sdk/types/index.d.ts rename packages/{bcp => sdk}/types/restclient.d.ts (87%) create mode 100644 packages/sdk/types/types.d.ts create mode 100644 packages/sdk/webpack.web.config.js diff --git a/packages/bcp/package.json b/packages/bcp/package.json index a21e6deb6e..ae98612da4 100644 --- a/packages/bcp/package.json +++ b/packages/bcp/package.json @@ -38,12 +38,12 @@ "pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js" }, "dependencies": { + "@cosmwasm/sdk": "^0.0.1", "@iov/bcp": "^2.0.0-alpha.7", "@iov/crypto": "^2.0.0-alpha.7", "@iov/encoding": "^2.0.0-alpha.7", "@iov/stream": "^2.0.0-alpha.7", "@tendermint/amino-js": "^0.7.0-alpha.1", - "axios": "^0.19.0", "fast-deep-equal": "^3.1.1", "readonly-date": "^1.0.0", "xstream": "^11.11.0" diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index 839e21d248..05d0c20ab8 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ +import { RestClient, TxsResponse } from "@cosmwasm/sdk"; import { Account, AccountQuery, @@ -37,7 +38,6 @@ import { Stream } from "xstream"; import { CosmosBech32Prefix, decodeCosmosPubkey, pubkeyToAddress } from "./address"; import { Caip5 } from "./caip5"; import { decodeAmount, parseTxsResponse } from "./decode"; -import { RestClient, TxsResponse } from "./restclient"; import { accountToNonce, TokenInfos } from "./types"; interface ChainData { diff --git a/packages/bcp/src/decode.ts b/packages/bcp/src/decode.ts index 4e2f57f305..72eaf9cfad 100644 --- a/packages/bcp/src/decode.ts +++ b/packages/bcp/src/decode.ts @@ -1,3 +1,4 @@ +import { TxsResponse } from "@cosmwasm/sdk"; import { Address, Algorithm, @@ -18,7 +19,6 @@ import { import { Encoding } from "@iov/encoding"; import amino from "@tendermint/amino-js"; -import { TxsResponse } from "./restclient"; import { coinToAmount, isAminoStdTx, TokenInfos } from "./types"; const { fromBase64 } = Encoding; diff --git a/packages/bcp/src/encode.ts b/packages/bcp/src/encode.ts index 42f2624f5b..28843930e5 100644 --- a/packages/bcp/src/encode.ts +++ b/packages/bcp/src/encode.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ +import { AminoTx } from "@cosmwasm/sdk"; import { Algorithm, Amount, @@ -13,7 +14,7 @@ import { Secp256k1 } from "@iov/crypto"; import { Encoding } from "@iov/encoding"; import amino from "@tendermint/amino-js"; -import { AminoTx, amountToCoin, TokenInfos } from "./types"; +import { amountToCoin, TokenInfos } from "./types"; const { toBase64 } = Encoding; diff --git a/packages/bcp/src/types.ts b/packages/bcp/src/types.ts index e929d61ba2..8a77aa71ae 100644 --- a/packages/bcp/src/types.ts +++ b/packages/bcp/src/types.ts @@ -1,8 +1,6 @@ import { Amount, Nonce, Token } from "@iov/bcp"; import amino from "@tendermint/amino-js"; -export type AminoTx = amino.Tx & { readonly value: amino.StdTx }; - export function isAminoStdTx(txValue: amino.TxValue): txValue is amino.StdTx { const { memo, msg, fee, signatures } = txValue as amino.StdTx; return ( diff --git a/packages/bcp/types/decode.d.ts b/packages/bcp/types/decode.d.ts index 8b7332c760..7c4e96f87b 100644 --- a/packages/bcp/types/decode.d.ts +++ b/packages/bcp/types/decode.d.ts @@ -1,3 +1,4 @@ +import { TxsResponse } from "@cosmwasm/sdk"; import { Amount, ChainId, @@ -12,7 +13,6 @@ import { UnsignedTransaction, } from "@iov/bcp"; import amino from "@tendermint/amino-js"; -import { TxsResponse } from "./restclient"; import { TokenInfos } from "./types"; export declare function decodePubkey(pubkey: amino.PubKey): PubkeyBundle; export declare function decodeSignature(signature: string): SignatureBytes; diff --git a/packages/bcp/types/encode.d.ts b/packages/bcp/types/encode.d.ts index 2a0788df5a..1068443a01 100644 --- a/packages/bcp/types/encode.d.ts +++ b/packages/bcp/types/encode.d.ts @@ -1,6 +1,7 @@ +import { AminoTx } from "@cosmwasm/sdk"; import { Amount, Fee, FullSignature, PubkeyBundle, SignedTransaction, UnsignedTransaction } from "@iov/bcp"; import amino from "@tendermint/amino-js"; -import { AminoTx, TokenInfos } from "./types"; +import { TokenInfos } from "./types"; export declare function encodePubkey(pubkey: PubkeyBundle): amino.PubKey; export declare function encodeAmount(amount: Amount, tokens: TokenInfos): amino.Coin; export declare function encodeFee(fee: Fee, tokens: TokenInfos): amino.StdFee; diff --git a/packages/bcp/types/types.d.ts b/packages/bcp/types/types.d.ts index 259be46604..49106266c1 100644 --- a/packages/bcp/types/types.d.ts +++ b/packages/bcp/types/types.d.ts @@ -1,8 +1,5 @@ import { Amount, Nonce, Token } from "@iov/bcp"; import amino from "@tendermint/amino-js"; -export declare type AminoTx = amino.Tx & { - readonly value: amino.StdTx; -}; export declare function isAminoStdTx(txValue: amino.TxValue): txValue is amino.StdTx; export interface TokenInfo extends Token { readonly denom: string; diff --git a/packages/sdk/.eslintignore b/packages/sdk/.eslintignore new file mode 120000 index 0000000000..86039baf54 --- /dev/null +++ b/packages/sdk/.eslintignore @@ -0,0 +1 @@ +../../.eslintignore \ No newline at end of file diff --git a/packages/sdk/.gitignore b/packages/sdk/.gitignore new file mode 100644 index 0000000000..68bf373524 --- /dev/null +++ b/packages/sdk/.gitignore @@ -0,0 +1,3 @@ +build/ +dist/ +docs/ diff --git a/packages/sdk/jasmine-spec-reporter.config.json b/packages/sdk/jasmine-spec-reporter.config.json new file mode 100644 index 0000000000..3a99755608 --- /dev/null +++ b/packages/sdk/jasmine-spec-reporter.config.json @@ -0,0 +1,12 @@ +{ + "suite": { + "displayNumber": true + }, + "spec": { + "displayDuration": true + }, + "summary": { + "displayPending": false, + "displayStacktrace": true + } +} diff --git a/packages/sdk/jasmine-testrunner.js b/packages/sdk/jasmine-testrunner.js new file mode 100755 index 0000000000..55b2df2912 --- /dev/null +++ b/packages/sdk/jasmine-testrunner.js @@ -0,0 +1,26 @@ +#!/usr/bin/env node + +require("source-map-support").install(); +const defaultSpecReporterConfig = require("./jasmine-spec-reporter.config.json"); + +// setup Jasmine +const Jasmine = require("jasmine"); +const jasmine = new Jasmine(); +jasmine.loadConfig({ + spec_dir: "build", + spec_files: ["**/*.spec.js"], + helpers: [], + random: false, + seed: null, + stopSpecOnExpectationFailure: false, +}); +jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 15 * 1000; + +// setup reporter +const { SpecReporter } = require("jasmine-spec-reporter"); +const reporter = new SpecReporter({ ...defaultSpecReporterConfig }); + +// initialize and execute +jasmine.env.clearReporters(); +jasmine.addReporter(reporter); +jasmine.execute(); diff --git a/packages/sdk/karma.conf.js b/packages/sdk/karma.conf.js new file mode 100644 index 0000000000..ff71fce804 --- /dev/null +++ b/packages/sdk/karma.conf.js @@ -0,0 +1,54 @@ +module.exports = function(config) { + config.set({ + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: ".", + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ["jasmine"], + + // list of files / patterns to load in the browser + files: ["dist/web/tests.js"], + + client: { + jasmine: { + random: false, + timeoutInterval: 15000, + }, + }, + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ["progress", "kjhtml"], + + // web server port + port: 9876, + + // enable / disable colors in the output (reporters and logs) + colors: true, + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: false, + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: ["Firefox"], + + browserNoActivityTimeout: 90000, + + // Keep brower open for debugging. This is overridden by yarn scripts + singleRun: false, + + customLaunchers: { + ChromeHeadlessInsecure: { + base: "ChromeHeadless", + flags: ["--disable-web-security"], + }, + }, + }); +}; diff --git a/packages/sdk/nonces/README.txt b/packages/sdk/nonces/README.txt new file mode 100644 index 0000000000..092fe732f1 --- /dev/null +++ b/packages/sdk/nonces/README.txt @@ -0,0 +1 @@ +Directory used to trigger lerna package updates for all packages diff --git a/packages/sdk/package.json b/packages/sdk/package.json new file mode 100644 index 0000000000..d69de11aa3 --- /dev/null +++ b/packages/sdk/package.json @@ -0,0 +1,46 @@ +{ + "name": "@cosmwasm/sdk", + "version": "0.0.1", + "description": "CosmWasm SDK", + "author": "Ethan Frey ", + "license": "Apache-2.0", + "main": "build/index.js", + "types": "types/index.d.ts", + "files": [ + "build/", + "types/", + "*.md", + "!*.spec.*", + "!**/testdata/" + ], + "repository": { + "type": "git", + "url": "https://github.com/confio/cosm-js/tree/master/packages/sdk" + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "docs": "shx rm -rf docs && typedoc --options typedoc.js", + "format": "prettier --write --loglevel warn \"./src/**/*.ts\"", + "lint": "eslint --max-warnings 0 \"**/*.{js,ts}\" && tslint -t verbose --project .", + "lint-fix": "eslint --max-warnings 0 \"**/*.{js,ts}\" --fix", + "move-types": "shx rm -rf ./types/* && shx mv build/types/* ./types && rm -rf ./types/testdata && shx rm -f ./types/*.spec.d.ts", + "format-types": "prettier --write --loglevel warn \"./types/**/*.d.ts\"", + "build": "shx rm -rf ./build && tsc && yarn move-types && yarn format-types", + "build-or-skip": "[ -n \"$SKIP_BUILD\" ] || yarn build", + "test-node": "node jasmine-testrunner.js", + "test-edge": "yarn pack-web && karma start --single-run --browsers Edge", + "test-firefox": "yarn pack-web && karma start --single-run --browsers Firefox", + "test-chrome": "yarn pack-web && karma start --single-run --browsers ChromeHeadlessInsecure", + "test-safari": "yarn pack-web && karma start --single-run --browsers Safari", + "test": "yarn build-or-skip && yarn test-node", + "pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js" + }, + "dependencies": { + "@tendermint/amino-js": "^0.7.0-alpha.1", + "axios": "^0.19.0" + }, + "devDependencies": { + } +} diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts new file mode 100644 index 0000000000..c5976e025f --- /dev/null +++ b/packages/sdk/src/index.ts @@ -0,0 +1,2 @@ +export { RestClient, TxsResponse } from "./restclient"; +export { AminoTx } from "./types"; diff --git a/packages/bcp/src/restclient.ts b/packages/sdk/src/restclient.ts similarity index 93% rename from packages/bcp/src/restclient.ts rename to packages/sdk/src/restclient.ts index 8ef49e48e3..3bd74f709b 100644 --- a/packages/bcp/src/restclient.ts +++ b/packages/sdk/src/restclient.ts @@ -1,4 +1,3 @@ -import { Address, PostableBytes, TransactionId } from "@iov/bcp"; import amino, { unmarshalTx } from "@tendermint/amino-js"; import axios, { AxiosInstance } from "axios"; @@ -132,7 +131,7 @@ export class RestClient { return responseData as BlocksResponse; } - public async authAccounts(address: Address, height?: string): Promise { + public async authAccounts(address: string, height?: string): Promise { const path = height === undefined ? `/auth/accounts/${address}` : `/auth/accounts/${address}?tx.height=${height}`; const responseData = await this.get(path); @@ -150,7 +149,7 @@ export class RestClient { return responseData as SearchTxsResponse; } - public async txsById(id: TransactionId): Promise { + public async txsById(id: string): Promise { const responseData = await this.get(`/txs/${id}`); if (!(responseData as any).tx) { throw new Error("Unexpected response data format"); @@ -158,7 +157,7 @@ export class RestClient { return responseData as TxsResponse; } - public async postTx(tx: PostableBytes): Promise { + public async postTx(tx: Uint8Array): Promise { const unmarshalled = unmarshalTx(tx, true); const params = { tx: unmarshalled.value, diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts new file mode 100644 index 0000000000..7b5d659958 --- /dev/null +++ b/packages/sdk/src/types.ts @@ -0,0 +1,3 @@ +import amino from "@tendermint/amino-js"; + +export type AminoTx = amino.Tx & { readonly value: amino.StdTx }; diff --git a/packages/sdk/tsconfig.json b/packages/sdk/tsconfig.json new file mode 100644 index 0000000000..167e8c0226 --- /dev/null +++ b/packages/sdk/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "build", + "declarationDir": "build/types", + "rootDir": "src" + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/sdk/tslint.json b/packages/sdk/tslint.json new file mode 100644 index 0000000000..0946f20963 --- /dev/null +++ b/packages/sdk/tslint.json @@ -0,0 +1,3 @@ +{ + "extends": "../../tslint.json" +} diff --git a/packages/sdk/typedoc.js b/packages/sdk/typedoc.js new file mode 100644 index 0000000000..e2387c7de4 --- /dev/null +++ b/packages/sdk/typedoc.js @@ -0,0 +1,14 @@ +const packageJson = require("./package.json"); + +module.exports = { + src: ["./src"], + out: "docs", + exclude: "**/*.spec.ts", + target: "es6", + name: `${packageJson.name} Documentation`, + readme: "README.md", + mode: "file", + excludeExternals: true, + excludeNotExported: true, + excludePrivate: true, +}; diff --git a/packages/sdk/types/index.d.ts b/packages/sdk/types/index.d.ts new file mode 100644 index 0000000000..c5976e025f --- /dev/null +++ b/packages/sdk/types/index.d.ts @@ -0,0 +1,2 @@ +export { RestClient, TxsResponse } from "./restclient"; +export { AminoTx } from "./types"; diff --git a/packages/bcp/types/restclient.d.ts b/packages/sdk/types/restclient.d.ts similarity index 87% rename from packages/bcp/types/restclient.d.ts rename to packages/sdk/types/restclient.d.ts index df1d9251b2..ace2f82b1d 100644 --- a/packages/bcp/types/restclient.d.ts +++ b/packages/sdk/types/restclient.d.ts @@ -1,4 +1,3 @@ -import { Address, PostableBytes, TransactionId } from "@iov/bcp"; import amino from "@tendermint/amino-js"; import { AminoTx } from "./types"; interface NodeInfo { @@ -69,9 +68,9 @@ export declare class RestClient { nodeInfo(): Promise; blocksLatest(): Promise; blocks(height: number): Promise; - authAccounts(address: Address, height?: string): Promise; + authAccounts(address: string, height?: string): Promise; txs(query: string): Promise; - txsById(id: TransactionId): Promise; - postTx(tx: PostableBytes): Promise; + txsById(id: string): Promise; + postTx(tx: Uint8Array): Promise; } export {}; diff --git a/packages/sdk/types/types.d.ts b/packages/sdk/types/types.d.ts new file mode 100644 index 0000000000..e0c1176697 --- /dev/null +++ b/packages/sdk/types/types.d.ts @@ -0,0 +1,4 @@ +import amino from "@tendermint/amino-js"; +export declare type AminoTx = amino.Tx & { + readonly value: amino.StdTx; +}; diff --git a/packages/sdk/webpack.web.config.js b/packages/sdk/webpack.web.config.js new file mode 100644 index 0000000000..a26d8e4119 --- /dev/null +++ b/packages/sdk/webpack.web.config.js @@ -0,0 +1,19 @@ +const glob = require("glob"); +const path = require("path"); +const webpack = require("webpack"); + +const target = "web"; +const distdir = path.join(__dirname, "dist", "web"); + +module.exports = [ + { + // bundle used for Karma tests + target: target, + entry: glob.sync("./build/**/*.spec.js"), + output: { + path: distdir, + filename: "tests.js", + }, + plugins: [new webpack.EnvironmentPlugin(["COSMOS_ENABLED"])], + }, +]; From 399a53e99a97005c49ec6f65d0a8e24cefb286cb Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 31 Jan 2020 18:02:08 +0100 Subject: [PATCH 2/9] Add first test in new package --- packages/sdk/src/restclient.spec.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 packages/sdk/src/restclient.spec.ts diff --git a/packages/sdk/src/restclient.spec.ts b/packages/sdk/src/restclient.spec.ts new file mode 100644 index 0000000000..e44812ee3b --- /dev/null +++ b/packages/sdk/src/restclient.spec.ts @@ -0,0 +1,10 @@ +import { RestClient } from "./restclient"; + +const httpUrl = "http://localhost:1317"; + +describe("RestClient", () => { + it("can be constructed", () => { + const client = new RestClient(httpUrl); + expect(client).toBeTruthy(); + }); +}); From 117d12f17dd8adcb795390e224c9c0747ecacba2 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 31 Jan 2020 18:04:11 +0100 Subject: [PATCH 3/9] Add packages/sdk to build container --- faucet.Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/faucet.Dockerfile b/faucet.Dockerfile index 95be7c2c72..558ede9d5b 100644 --- a/faucet.Dockerfile +++ b/faucet.Dockerfile @@ -5,6 +5,7 @@ FROM node:12.14-alpine AS build-env ADD package.json yarn.lock tsconfig.json lerna.json /build_repo_root/ ADD packages/bcp /build_repo_root/packages/bcp ADD packages/faucet /build_repo_root/packages/faucet +ADD packages/sdk /build_repo_root/packages/sdk WORKDIR /build_repo_root RUN yarn install --frozen-lockfile From fb9160f258598259bfe9f3b602abe996ca847b8c Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sat, 1 Feb 2020 15:54:08 +0100 Subject: [PATCH 4/9] Move isAminoStdTx into sdk --- packages/bcp/src/decode.ts | 4 ++-- packages/bcp/src/types.ts | 7 ------- packages/sdk/src/index.ts | 2 +- packages/sdk/src/types.ts | 7 +++++++ packages/sdk/types/index.d.ts | 2 +- packages/sdk/types/types.d.ts | 1 + 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/bcp/src/decode.ts b/packages/bcp/src/decode.ts index 72eaf9cfad..a6c40170b3 100644 --- a/packages/bcp/src/decode.ts +++ b/packages/bcp/src/decode.ts @@ -1,4 +1,4 @@ -import { TxsResponse } from "@cosmwasm/sdk"; +import { isAminoStdTx, TxsResponse } from "@cosmwasm/sdk"; import { Address, Algorithm, @@ -19,7 +19,7 @@ import { import { Encoding } from "@iov/encoding"; import amino from "@tendermint/amino-js"; -import { coinToAmount, isAminoStdTx, TokenInfos } from "./types"; +import { coinToAmount, TokenInfos } from "./types"; const { fromBase64 } = Encoding; diff --git a/packages/bcp/src/types.ts b/packages/bcp/src/types.ts index 8a77aa71ae..8e3bccc169 100644 --- a/packages/bcp/src/types.ts +++ b/packages/bcp/src/types.ts @@ -1,13 +1,6 @@ import { Amount, Nonce, Token } from "@iov/bcp"; import amino from "@tendermint/amino-js"; -export function isAminoStdTx(txValue: amino.TxValue): txValue is amino.StdTx { - const { memo, msg, fee, signatures } = txValue as amino.StdTx; - return ( - typeof memo === "string" && Array.isArray(msg) && typeof fee === "object" && Array.isArray(signatures) - ); -} - export interface TokenInfo extends Token { readonly denom: string; } diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index c5976e025f..f8dbdc9513 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,2 +1,2 @@ export { RestClient, TxsResponse } from "./restclient"; -export { AminoTx } from "./types"; +export { AminoTx, isAminoStdTx } from "./types"; diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 7b5d659958..c8a0ef7242 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -1,3 +1,10 @@ import amino from "@tendermint/amino-js"; export type AminoTx = amino.Tx & { readonly value: amino.StdTx }; + +export function isAminoStdTx(txValue: amino.TxValue): txValue is amino.StdTx { + const { memo, msg, fee, signatures } = txValue as amino.StdTx; + return ( + typeof memo === "string" && Array.isArray(msg) && typeof fee === "object" && Array.isArray(signatures) + ); +} diff --git a/packages/sdk/types/index.d.ts b/packages/sdk/types/index.d.ts index c5976e025f..f8dbdc9513 100644 --- a/packages/sdk/types/index.d.ts +++ b/packages/sdk/types/index.d.ts @@ -1,2 +1,2 @@ export { RestClient, TxsResponse } from "./restclient"; -export { AminoTx } from "./types"; +export { AminoTx, isAminoStdTx } from "./types"; diff --git a/packages/sdk/types/types.d.ts b/packages/sdk/types/types.d.ts index e0c1176697..cb5a44a919 100644 --- a/packages/sdk/types/types.d.ts +++ b/packages/sdk/types/types.d.ts @@ -2,3 +2,4 @@ import amino from "@tendermint/amino-js"; export declare type AminoTx = amino.Tx & { readonly value: amino.StdTx; }; +export declare function isAminoStdTx(txValue: amino.TxValue): txValue is amino.StdTx; From eb72f0e1751effd71aa447b55f64d8801c45dc4f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sat, 1 Feb 2020 16:17:36 +0100 Subject: [PATCH 5/9] Make TokenInfo free from BCP types --- packages/bcp/src/cosmwasmcodec.ts | 4 +--- packages/bcp/src/cosmwasmconnection.spec.ts | 14 +++++++------- packages/bcp/src/cosmwasmconnection.ts | 18 ++++++++++-------- packages/bcp/src/cosmwasmconnector.ts | 9 ++++----- packages/bcp/src/decode.spec.ts | 3 +-- packages/bcp/src/encode.spec.ts | 3 +-- packages/bcp/src/index.ts | 2 +- packages/bcp/src/types.ts | 19 +++++++++++++++---- packages/bcp/types/cosmwasmconnection.d.ts | 7 +++++-- packages/bcp/types/cosmwasmconnector.d.ts | 5 ++--- packages/bcp/types/index.d.ts | 2 +- packages/bcp/types/types.d.ts | 16 +++++++++++++--- packages/faucet/src/codec.ts | 14 +++++++------- 13 files changed, 68 insertions(+), 48 deletions(-) diff --git a/packages/bcp/src/cosmwasmcodec.ts b/packages/bcp/src/cosmwasmcodec.ts index 3d89c5891b..30650ae02d 100644 --- a/packages/bcp/src/cosmwasmcodec.ts +++ b/packages/bcp/src/cosmwasmcodec.ts @@ -9,7 +9,6 @@ import { SignableBytes, SignedTransaction, SigningJob, - TokenTicker, TransactionId, TxCodec, UnsignedTransaction, @@ -107,8 +106,7 @@ const defaultPrefix = "cosmos" as CosmosBech32Prefix; const defaultTokens: TokenInfos = [ { fractionalDigits: 6, - tokenName: "Atom (Cosmos Hub)", - tokenTicker: "ATOM" as TokenTicker, + ticker: "ATOM", denom: "uatom", }, ]; diff --git a/packages/bcp/src/cosmwasmconnection.spec.ts b/packages/bcp/src/cosmwasmconnection.spec.ts index baaa18299d..8ef2a08e3e 100644 --- a/packages/bcp/src/cosmwasmconnection.spec.ts +++ b/packages/bcp/src/cosmwasmconnection.spec.ts @@ -16,8 +16,8 @@ import { HdPaths, Secp256k1HdWallet, UserProfile } from "@iov/keycontrol"; import { CosmosBech32Prefix } from "./address"; import { CosmWasmCodec, cosmWasmCodec } from "./cosmwasmcodec"; -import { CosmWasmConnection } from "./cosmwasmconnection"; -import { nonceToSequence, TokenInfos } from "./types"; +import { CosmWasmConnection, TokenConfiguration } from "./cosmwasmconnection"; +import { nonceToSequence } from "./types"; const { fromBase64, toHex } = Encoding; @@ -45,17 +45,17 @@ describe("CosmWasmConnection", () => { const defaultPrefix = "cosmos" as CosmosBech32Prefix; // this is for wasmd blockchain - const defaultTokens: TokenInfos = [ + const defaultTokens: TokenConfiguration = [ { fractionalDigits: 6, - tokenName: "Fee Token", - tokenTicker: "COSM" as TokenTicker, + name: "Fee Token", + ticker: "COSM", denom: "ucosm", }, { fractionalDigits: 6, - tokenName: "Staking Token", - tokenTicker: "STAKE" as TokenTicker, + name: "Staking Token", + ticker: "STAKE", denom: "ustake", }, ]; diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index 05d0c20ab8..05a87fdf84 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -38,7 +38,7 @@ import { Stream } from "xstream"; import { CosmosBech32Prefix, decodeCosmosPubkey, pubkeyToAddress } from "./address"; import { Caip5 } from "./caip5"; import { decodeAmount, parseTxsResponse } from "./decode"; -import { accountToNonce, TokenInfos } from "./types"; +import { accountToNonce, TokenInfo, TokenInfos } from "./types"; interface ChainData { readonly chainId: ChainId; @@ -68,16 +68,18 @@ function buildQueryString({ return components.filter(Boolean).join("&"); } +export type TokenConfiguration = readonly (TokenInfo & { readonly name: string })[]; + export class CosmWasmConnection implements BlockchainConnection { // we must know prefix and tokens a priori to understand the chain public static async establish( url: string, prefix: CosmosBech32Prefix, - tokenInfo: TokenInfos, + tokens: TokenConfiguration, ): Promise { const restClient = new RestClient(url); const chainData = await this.initialize(restClient); - return new CosmWasmConnection(restClient, chainData, prefix, tokenInfo); + return new CosmWasmConnection(restClient, chainData, prefix, tokens); } private static async initialize(restClient: RestClient): Promise { @@ -102,16 +104,16 @@ export class CosmWasmConnection implements BlockchainConnection { restClient: RestClient, chainData: ChainData, prefix: CosmosBech32Prefix, - tokenInfo: TokenInfos, + tokens: TokenConfiguration, ) { this.restClient = restClient; this.chainData = chainData; this._prefix = prefix; - this.tokenInfo = tokenInfo; + this.tokenInfo = tokens; - this.supportedTokens = this.tokenInfo.map(info => ({ - tokenTicker: info.tokenTicker, - tokenName: info.tokenName, + this.supportedTokens = tokens.map(info => ({ + tokenTicker: info.ticker as TokenTicker, + tokenName: info.name, fractionalDigits: info.fractionalDigits, })); this.primaryToken = this.supportedTokens[0]; diff --git a/packages/bcp/src/cosmwasmconnector.ts b/packages/bcp/src/cosmwasmconnector.ts index 295aee0aa2..af0673a3fa 100644 --- a/packages/bcp/src/cosmwasmconnector.ts +++ b/packages/bcp/src/cosmwasmconnector.ts @@ -2,8 +2,7 @@ import { ChainConnector, ChainId } from "@iov/bcp"; import { CosmosBech32Prefix } from "./address"; import { CosmWasmCodec } from "./cosmwasmcodec"; -import { CosmWasmConnection } from "./cosmwasmconnection"; -import { TokenInfo } from "./types"; +import { CosmWasmConnection, TokenConfiguration } from "./cosmwasmconnection"; /** * A helper to connect to a cosmos-based chain at a given url @@ -11,12 +10,12 @@ import { TokenInfo } from "./types"; export function createCosmWasmConnector( url: string, prefix: CosmosBech32Prefix, - tokenInfo: readonly TokenInfo[], + tokens: TokenConfiguration, expectedChainId?: ChainId, ): ChainConnector { - const codec = new CosmWasmCodec(prefix, tokenInfo); + const codec = new CosmWasmCodec(prefix, tokens); return { - establishConnection: async () => CosmWasmConnection.establish(url, prefix, tokenInfo), + establishConnection: async () => CosmWasmConnection.establish(url, prefix, tokens), codec: codec, expectedChainId: expectedChainId, }; diff --git a/packages/bcp/src/decode.spec.ts b/packages/bcp/src/decode.spec.ts index 64fe28f4e0..cf2b2dd712 100644 --- a/packages/bcp/src/decode.spec.ts +++ b/packages/bcp/src/decode.spec.ts @@ -55,8 +55,7 @@ describe("decode", () => { const defaultTokens: TokenInfos = [ { fractionalDigits: 6, - tokenName: "Atom (Cosmos Hub)", - tokenTicker: "ATOM" as TokenTicker, + ticker: "ATOM", denom: "uatom", }, ]; diff --git a/packages/bcp/src/encode.spec.ts b/packages/bcp/src/encode.spec.ts index 2eaaeb1491..b81e1c37b8 100644 --- a/packages/bcp/src/encode.spec.ts +++ b/packages/bcp/src/encode.spec.ts @@ -44,8 +44,7 @@ describe("encode", () => { const defaultTokens: TokenInfos = [ { fractionalDigits: 6, - tokenName: "Atom (Cosmos Hub)", - tokenTicker: "ATOM" as TokenTicker, + ticker: "ATOM", denom: "uatom", }, ]; diff --git a/packages/bcp/src/index.ts b/packages/bcp/src/index.ts index 81e10d6e4a..000a7e80c5 100644 --- a/packages/bcp/src/index.ts +++ b/packages/bcp/src/index.ts @@ -1,4 +1,4 @@ export { CosmWasmCodec } from "./cosmwasmcodec"; -export { CosmWasmConnection } from "./cosmwasmconnection"; +export { CosmWasmConnection, TokenConfiguration } from "./cosmwasmconnection"; export { createCosmWasmConnector } from "./cosmwasmconnector"; export { TokenInfo } from "./types"; diff --git a/packages/bcp/src/types.ts b/packages/bcp/src/types.ts index 8e3bccc169..f3f4665311 100644 --- a/packages/bcp/src/types.ts +++ b/packages/bcp/src/types.ts @@ -1,15 +1,26 @@ -import { Amount, Nonce, Token } from "@iov/bcp"; +import { Amount, Nonce, TokenTicker } from "@iov/bcp"; import amino from "@tendermint/amino-js"; -export interface TokenInfo extends Token { +export interface TokenInfo { readonly denom: 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 type TokenInfos = ReadonlyArray; // TODO: return null vs throw exception for undefined??? export function amountToCoin(lookup: ReadonlyArray, amount: Amount): amino.Coin { - const match = lookup.find(({ tokenTicker }) => tokenTicker === amount.tokenTicker); + const match = lookup.find(({ ticker }) => ticker === amount.tokenTicker); if (!match) { throw Error(`unknown ticker: ${amount.tokenTicker}`); } @@ -26,7 +37,7 @@ export function coinToAmount(tokens: TokenInfos, coin: amino.Coin): Amount { throw Error(`unknown denom: ${coin.denom}`); } return { - tokenTicker: match.tokenTicker, + tokenTicker: match.ticker as TokenTicker, fractionalDigits: match.fractionalDigits, quantity: coin.amount, }; diff --git a/packages/bcp/types/cosmwasmconnection.d.ts b/packages/bcp/types/cosmwasmconnection.d.ts index 5144df6719..764e9512ae 100644 --- a/packages/bcp/types/cosmwasmconnection.d.ts +++ b/packages/bcp/types/cosmwasmconnection.d.ts @@ -21,12 +21,15 @@ import { } from "@iov/bcp"; import { Stream } from "xstream"; import { CosmosBech32Prefix } from "./address"; -import { TokenInfos } from "./types"; +import { TokenInfo } from "./types"; +export declare type TokenConfiguration = readonly (TokenInfo & { + readonly name: string; +})[]; export declare class CosmWasmConnection implements BlockchainConnection { static establish( url: string, prefix: CosmosBech32Prefix, - tokenInfo: TokenInfos, + tokens: TokenConfiguration, ): Promise; private static initialize; private readonly restClient; diff --git a/packages/bcp/types/cosmwasmconnector.d.ts b/packages/bcp/types/cosmwasmconnector.d.ts index 2a2ba3e702..d9ed98cdcd 100644 --- a/packages/bcp/types/cosmwasmconnector.d.ts +++ b/packages/bcp/types/cosmwasmconnector.d.ts @@ -1,13 +1,12 @@ import { ChainConnector, ChainId } from "@iov/bcp"; import { CosmosBech32Prefix } from "./address"; -import { CosmWasmConnection } from "./cosmwasmconnection"; -import { TokenInfo } from "./types"; +import { CosmWasmConnection, TokenConfiguration } from "./cosmwasmconnection"; /** * A helper to connect to a cosmos-based chain at a given url */ export declare function createCosmWasmConnector( url: string, prefix: CosmosBech32Prefix, - tokenInfo: readonly TokenInfo[], + tokens: TokenConfiguration, expectedChainId?: ChainId, ): ChainConnector; diff --git a/packages/bcp/types/index.d.ts b/packages/bcp/types/index.d.ts index 81e10d6e4a..000a7e80c5 100644 --- a/packages/bcp/types/index.d.ts +++ b/packages/bcp/types/index.d.ts @@ -1,4 +1,4 @@ export { CosmWasmCodec } from "./cosmwasmcodec"; -export { CosmWasmConnection } from "./cosmwasmconnection"; +export { CosmWasmConnection, TokenConfiguration } from "./cosmwasmconnection"; export { createCosmWasmConnector } from "./cosmwasmconnector"; export { TokenInfo } from "./types"; diff --git a/packages/bcp/types/types.d.ts b/packages/bcp/types/types.d.ts index 49106266c1..14cd95cf8e 100644 --- a/packages/bcp/types/types.d.ts +++ b/packages/bcp/types/types.d.ts @@ -1,8 +1,18 @@ -import { Amount, Nonce, Token } from "@iov/bcp"; +import { Amount, Nonce } from "@iov/bcp"; import amino from "@tendermint/amino-js"; -export declare function isAminoStdTx(txValue: amino.TxValue): txValue is amino.StdTx; -export interface TokenInfo extends Token { +export interface TokenInfo { readonly denom: 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 type TokenInfos = ReadonlyArray; export declare function amountToCoin(lookup: ReadonlyArray, amount: Amount): amino.Coin; diff --git a/packages/faucet/src/codec.ts b/packages/faucet/src/codec.ts index 91b76481ec..2b0d895f9e 100644 --- a/packages/faucet/src/codec.ts +++ b/packages/faucet/src/codec.ts @@ -1,18 +1,18 @@ -import { CosmWasmCodec, CosmWasmConnection, TokenInfo } from "@cosmwasm/bcp"; -import { TokenTicker, TxCodec } from "@iov/bcp"; +import { CosmWasmCodec, CosmWasmConnection, TokenConfiguration } from "@cosmwasm/bcp"; +import { TxCodec } from "@iov/bcp"; const prefix = "cosmos"; -const tokens: readonly TokenInfo[] = [ +const tokens: TokenConfiguration = [ { fractionalDigits: 6, - tokenName: "Fee Token", - tokenTicker: "COSM" as TokenTicker, + name: "Fee Token", + ticker: "COSM", denom: "cosm", }, { fractionalDigits: 6, - tokenName: "Staking Token", - tokenTicker: "STAKE" as TokenTicker, + name: "Staking Token", + ticker: "STAKE", denom: "stake", }, ]; From 4377fdd8ab7d04e2d5f1f87d53e96a3064a87fc7 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sat, 1 Feb 2020 16:23:08 +0100 Subject: [PATCH 6/9] Move TokenInfo into sdk --- packages/bcp/src/cosmwasmconnection.ts | 6 +++--- packages/bcp/src/index.ts | 1 - packages/bcp/src/types.ts | 16 +--------------- packages/bcp/types/cosmwasmconnection.d.ts | 2 +- packages/bcp/types/index.d.ts | 1 - packages/bcp/types/types.d.ts | 15 +-------------- packages/sdk/src/index.ts | 2 +- packages/sdk/src/types.ts | 15 +++++++++++++++ packages/sdk/types/index.d.ts | 2 +- packages/sdk/types/types.d.ts | 14 ++++++++++++++ 10 files changed, 37 insertions(+), 37 deletions(-) diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index 05a87fdf84..2cd19fe1ae 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { RestClient, TxsResponse } from "@cosmwasm/sdk"; +import { RestClient, TokenInfo, TxsResponse } from "@cosmwasm/sdk"; import { Account, AccountQuery, @@ -38,7 +38,7 @@ import { Stream } from "xstream"; import { CosmosBech32Prefix, decodeCosmosPubkey, pubkeyToAddress } from "./address"; import { Caip5 } from "./caip5"; import { decodeAmount, parseTxsResponse } from "./decode"; -import { accountToNonce, TokenInfo, TokenInfos } from "./types"; +import { accountToNonce } from "./types"; interface ChainData { readonly chainId: ChainId; @@ -90,7 +90,7 @@ export class CosmWasmConnection implements BlockchainConnection { private readonly restClient: RestClient; private readonly chainData: ChainData; private readonly _prefix: CosmosBech32Prefix; - private readonly tokenInfo: TokenInfos; + private readonly tokenInfo: readonly TokenInfo[]; // these are derived from arguments (cached for use in multiple functions) private readonly primaryToken: Token; diff --git a/packages/bcp/src/index.ts b/packages/bcp/src/index.ts index 000a7e80c5..8402859d0b 100644 --- a/packages/bcp/src/index.ts +++ b/packages/bcp/src/index.ts @@ -1,4 +1,3 @@ export { CosmWasmCodec } from "./cosmwasmcodec"; export { CosmWasmConnection, TokenConfiguration } from "./cosmwasmconnection"; export { createCosmWasmConnector } from "./cosmwasmconnector"; -export { TokenInfo } from "./types"; diff --git a/packages/bcp/src/types.ts b/packages/bcp/src/types.ts index f3f4665311..26b080b7c4 100644 --- a/packages/bcp/src/types.ts +++ b/packages/bcp/src/types.ts @@ -1,21 +1,7 @@ +import { TokenInfo } from "@cosmwasm/sdk"; import { Amount, Nonce, TokenTicker } from "@iov/bcp"; import amino from "@tendermint/amino-js"; -export interface TokenInfo { - readonly denom: 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 type TokenInfos = ReadonlyArray; // TODO: return null vs throw exception for undefined??? diff --git a/packages/bcp/types/cosmwasmconnection.d.ts b/packages/bcp/types/cosmwasmconnection.d.ts index 764e9512ae..4a8a3d83d6 100644 --- a/packages/bcp/types/cosmwasmconnection.d.ts +++ b/packages/bcp/types/cosmwasmconnection.d.ts @@ -1,3 +1,4 @@ +import { TokenInfo } from "@cosmwasm/sdk"; import { Account, AccountQuery, @@ -21,7 +22,6 @@ import { } from "@iov/bcp"; import { Stream } from "xstream"; import { CosmosBech32Prefix } from "./address"; -import { TokenInfo } from "./types"; export declare type TokenConfiguration = readonly (TokenInfo & { readonly name: string; })[]; diff --git a/packages/bcp/types/index.d.ts b/packages/bcp/types/index.d.ts index 000a7e80c5..8402859d0b 100644 --- a/packages/bcp/types/index.d.ts +++ b/packages/bcp/types/index.d.ts @@ -1,4 +1,3 @@ export { CosmWasmCodec } from "./cosmwasmcodec"; export { CosmWasmConnection, TokenConfiguration } from "./cosmwasmconnection"; export { createCosmWasmConnector } from "./cosmwasmconnector"; -export { TokenInfo } from "./types"; diff --git a/packages/bcp/types/types.d.ts b/packages/bcp/types/types.d.ts index 14cd95cf8e..7191f61ce4 100644 --- a/packages/bcp/types/types.d.ts +++ b/packages/bcp/types/types.d.ts @@ -1,19 +1,6 @@ +import { TokenInfo } from "@cosmwasm/sdk"; import { Amount, Nonce } from "@iov/bcp"; import amino from "@tendermint/amino-js"; -export interface TokenInfo { - readonly denom: 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 type TokenInfos = ReadonlyArray; export declare function amountToCoin(lookup: ReadonlyArray, amount: Amount): amino.Coin; export declare function coinToAmount(tokens: TokenInfos, coin: amino.Coin): Amount; diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index f8dbdc9513..d4162d9046 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,2 +1,2 @@ export { RestClient, TxsResponse } from "./restclient"; -export { AminoTx, isAminoStdTx } from "./types"; +export { AminoTx, isAminoStdTx, TokenInfo } from "./types"; diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index c8a0ef7242..83be077d44 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -8,3 +8,18 @@ export function isAminoStdTx(txValue: amino.TxValue): txValue is amino.StdTx { typeof memo === "string" && Array.isArray(msg) && typeof fee === "object" && Array.isArray(signatures) ); } + +export interface TokenInfo { + readonly denom: 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; +} diff --git a/packages/sdk/types/index.d.ts b/packages/sdk/types/index.d.ts index f8dbdc9513..d4162d9046 100644 --- a/packages/sdk/types/index.d.ts +++ b/packages/sdk/types/index.d.ts @@ -1,2 +1,2 @@ export { RestClient, TxsResponse } from "./restclient"; -export { AminoTx, isAminoStdTx } from "./types"; +export { AminoTx, isAminoStdTx, TokenInfo } from "./types"; diff --git a/packages/sdk/types/types.d.ts b/packages/sdk/types/types.d.ts index cb5a44a919..e41597e46a 100644 --- a/packages/sdk/types/types.d.ts +++ b/packages/sdk/types/types.d.ts @@ -3,3 +3,17 @@ export declare type AminoTx = amino.Tx & { readonly value: amino.StdTx; }; export declare function isAminoStdTx(txValue: amino.TxValue): txValue is amino.StdTx; +export interface TokenInfo { + readonly denom: 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; +} From c81c356fe3f1d6144d8602d1f08778b228a97d13 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sat, 1 Feb 2020 16:29:08 +0100 Subject: [PATCH 7/9] Simplify decodeAmount --- packages/bcp/src/cosmwasmconnection.ts | 2 +- packages/bcp/src/decode.spec.ts | 2 +- packages/bcp/src/decode.ts | 9 ++++----- packages/bcp/src/types.ts | 1 - packages/bcp/types/decode.d.ts | 2 +- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index 2cd19fe1ae..68a416b392 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -160,7 +160,7 @@ export class CosmWasmConnection implements BlockchainConnection { }; return { address: address, - balance: supportedCoins.map(decodeAmount(this.tokenInfo)), + balance: supportedCoins.map(coin => decodeAmount(this.tokenInfo, coin)), pubkey: pubkey, }; } diff --git a/packages/bcp/src/decode.spec.ts b/packages/bcp/src/decode.spec.ts index cf2b2dd712..5110328945 100644 --- a/packages/bcp/src/decode.spec.ts +++ b/packages/bcp/src/decode.spec.ts @@ -97,7 +97,7 @@ describe("decode", () => { denom: "uatom", amount: "11657995", }; - expect(decodeAmount(defaultTokens)(amount)).toEqual(defaultAmount); + expect(decodeAmount(defaultTokens, amount)).toEqual(defaultAmount); }); }); diff --git a/packages/bcp/src/decode.ts b/packages/bcp/src/decode.ts index a6c40170b3..9fb0869a4f 100644 --- a/packages/bcp/src/decode.ts +++ b/packages/bcp/src/decode.ts @@ -42,10 +42,9 @@ export function decodeFullSignature(signature: amino.StdSignature, nonce: number }; } -// TODO: return null vs throw exception for undefined??? -export const decodeAmount = (tokens: TokenInfos) => (coin: amino.Coin): Amount => { +export function decodeAmount(tokens: TokenInfos, coin: amino.Coin): Amount { return coinToAmount(tokens, coin); -}; +} export function parseMsg(msg: amino.Msg, chainId: ChainId, tokens: TokenInfos): SendTransaction { if (msg.type !== "cosmos-sdk/MsgSend") { @@ -63,7 +62,7 @@ export function parseMsg(msg: amino.Msg, chainId: ChainId, tokens: TokenInfos): chainId: chainId, sender: msgValue.from_address as Address, recipient: msgValue.to_address as Address, - amount: decodeAmount(tokens)(msgValue.amount[0]), + amount: decodeAmount(tokens, msgValue.amount[0]), }; } @@ -72,7 +71,7 @@ export function parseFee(fee: amino.StdFee, tokens: TokenInfos): Fee { throw new Error("Only fee with one amount is supported"); } return { - tokens: decodeAmount(tokens)(fee.amount[0]), + tokens: decodeAmount(tokens, fee.amount[0]), gasLimit: fee.gas, }; } diff --git a/packages/bcp/src/types.ts b/packages/bcp/src/types.ts index 26b080b7c4..d4035e6caf 100644 --- a/packages/bcp/src/types.ts +++ b/packages/bcp/src/types.ts @@ -16,7 +16,6 @@ export function amountToCoin(lookup: ReadonlyArray, amount: Amount): }; } -// TODO: return null vs throw exception for undefined??? export function coinToAmount(tokens: TokenInfos, coin: amino.Coin): Amount { const match = tokens.find(({ denom }) => denom === coin.denom); if (!match) { diff --git a/packages/bcp/types/decode.d.ts b/packages/bcp/types/decode.d.ts index 7c4e96f87b..68122fd95c 100644 --- a/packages/bcp/types/decode.d.ts +++ b/packages/bcp/types/decode.d.ts @@ -17,7 +17,7 @@ import { TokenInfos } from "./types"; export declare function decodePubkey(pubkey: amino.PubKey): PubkeyBundle; export declare function decodeSignature(signature: string): SignatureBytes; export declare function decodeFullSignature(signature: amino.StdSignature, nonce: number): FullSignature; -export declare const decodeAmount: (tokens: TokenInfos) => (coin: amino.Coin) => Amount; +export declare function decodeAmount(tokens: TokenInfos, coin: amino.Coin): Amount; export declare function parseMsg(msg: amino.Msg, chainId: ChainId, tokens: TokenInfos): SendTransaction; export declare function parseFee(fee: amino.StdFee, tokens: TokenInfos): Fee; export declare function parseTx( From e1270581007d6f993086900542eb27d9d4406856 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sat, 1 Feb 2020 17:05:22 +0100 Subject: [PATCH 8/9] Make coinToDecimal and decimalToCoin BCP free --- packages/bcp/src/decode.ts | 10 ++++++++-- packages/bcp/src/encode.ts | 10 +++++++--- packages/bcp/src/types.ts | 26 ++++++++++++++------------ packages/bcp/types/types.d.ts | 14 +++++++++++--- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/packages/bcp/src/decode.ts b/packages/bcp/src/decode.ts index 9fb0869a4f..002cc5b366 100644 --- a/packages/bcp/src/decode.ts +++ b/packages/bcp/src/decode.ts @@ -13,13 +13,14 @@ import { SendTransaction, SignatureBytes, SignedTransaction, + TokenTicker, TransactionId, UnsignedTransaction, } from "@iov/bcp"; import { Encoding } from "@iov/encoding"; import amino from "@tendermint/amino-js"; -import { coinToAmount, TokenInfos } from "./types"; +import { coinToDecimal, TokenInfos } from "./types"; const { fromBase64 } = Encoding; @@ -43,7 +44,12 @@ export function decodeFullSignature(signature: amino.StdSignature, nonce: number } export function decodeAmount(tokens: TokenInfos, coin: amino.Coin): Amount { - return coinToAmount(tokens, coin); + const [value, ticker] = coinToDecimal(tokens, coin); + return { + quantity: value.atomics, + fractionalDigits: value.fractionalDigits, + tokenTicker: ticker as TokenTicker, + }; } export function parseMsg(msg: amino.Msg, chainId: ChainId, tokens: TokenInfos): SendTransaction { diff --git a/packages/bcp/src/encode.ts b/packages/bcp/src/encode.ts index 28843930e5..3ce2ebcf13 100644 --- a/packages/bcp/src/encode.ts +++ b/packages/bcp/src/encode.ts @@ -11,10 +11,10 @@ import { UnsignedTransaction, } from "@iov/bcp"; import { Secp256k1 } from "@iov/crypto"; -import { Encoding } from "@iov/encoding"; +import { Decimal, Encoding } from "@iov/encoding"; import amino from "@tendermint/amino-js"; -import { amountToCoin, TokenInfos } from "./types"; +import { decimalToCoin, TokenInfos } from "./types"; const { toBase64 } = Encoding; @@ -36,7 +36,11 @@ export function encodePubkey(pubkey: PubkeyBundle): amino.PubKey { } export function encodeAmount(amount: Amount, tokens: TokenInfos): amino.Coin { - return amountToCoin(tokens, amount); + return decimalToCoin( + tokens, + Decimal.fromAtomics(amount.quantity, amount.fractionalDigits), + amount.tokenTicker, + ); } export function encodeFee(fee: Fee, tokens: TokenInfos): amino.StdFee { diff --git a/packages/bcp/src/types.ts b/packages/bcp/src/types.ts index d4035e6caf..c2f5edd175 100644 --- a/packages/bcp/src/types.ts +++ b/packages/bcp/src/types.ts @@ -1,31 +1,33 @@ import { TokenInfo } from "@cosmwasm/sdk"; -import { Amount, Nonce, TokenTicker } from "@iov/bcp"; +import { Nonce } from "@iov/bcp"; +import { Decimal } from "@iov/encoding"; import amino from "@tendermint/amino-js"; export type TokenInfos = ReadonlyArray; -// TODO: return null vs throw exception for undefined??? -export function amountToCoin(lookup: ReadonlyArray, amount: Amount): amino.Coin { - const match = lookup.find(({ ticker }) => ticker === amount.tokenTicker); +export function decimalToCoin(lookup: readonly TokenInfo[], value: Decimal, ticker: string): amino.Coin { + const match = lookup.find(token => token.ticker === ticker); if (!match) { - throw Error(`unknown ticker: ${amount.tokenTicker}`); + throw Error(`unknown ticker: ${ticker}`); + } + if (match.fractionalDigits !== value.fractionalDigits) { + throw new Error( + "Mismatch in fractional digits between token and value. If you really want, implement a conversion here. However, this indicates a bug in the caller code.", + ); } return { denom: match.denom, - amount: amount.quantity, + amount: value.atomics, }; } -export function coinToAmount(tokens: TokenInfos, coin: amino.Coin): Amount { +export function coinToDecimal(tokens: readonly TokenInfo[], coin: amino.Coin): readonly [Decimal, string] { const match = tokens.find(({ denom }) => denom === coin.denom); if (!match) { throw Error(`unknown denom: ${coin.denom}`); } - return { - tokenTicker: match.ticker as TokenTicker, - fractionalDigits: match.fractionalDigits, - quantity: coin.amount, - }; + const value = Decimal.fromAtomics(coin.amount, match.fractionalDigits); + return [value, match.ticker]; } // tslint:disable-next-line:no-bitwise diff --git a/packages/bcp/types/types.d.ts b/packages/bcp/types/types.d.ts index 7191f61ce4..6a8f6f4964 100644 --- a/packages/bcp/types/types.d.ts +++ b/packages/bcp/types/types.d.ts @@ -1,9 +1,17 @@ import { TokenInfo } from "@cosmwasm/sdk"; -import { Amount, Nonce } from "@iov/bcp"; +import { Nonce } from "@iov/bcp"; +import { Decimal } from "@iov/encoding"; import amino from "@tendermint/amino-js"; export declare type TokenInfos = ReadonlyArray; -export declare function amountToCoin(lookup: ReadonlyArray, amount: Amount): amino.Coin; -export declare function coinToAmount(tokens: TokenInfos, coin: amino.Coin): Amount; +export declare function decimalToCoin( + lookup: readonly TokenInfo[], + value: Decimal, + ticker: string, +): amino.Coin; +export declare function coinToDecimal( + tokens: readonly TokenInfo[], + coin: amino.Coin, +): readonly [Decimal, string]; export interface NonceInfo { readonly account_number: string; readonly sequence: string; From 9da9225345db48a176f331ac3a183b61a9c12a8f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Sat, 1 Feb 2020 17:09:27 +0100 Subject: [PATCH 9/9] Move decimalToCoin and coinToDecimal into SDK --- packages/bcp/src/decode.ts | 4 ++-- packages/bcp/src/encode.ts | 4 ++-- packages/bcp/src/types.ts | 27 --------------------------- packages/bcp/types/types.d.ts | 11 ----------- packages/sdk/package.json | 1 + packages/sdk/src/decoding.ts | 13 +++++++++++++ packages/sdk/src/encoding.ts | 20 ++++++++++++++++++++ packages/sdk/src/index.ts | 2 ++ packages/sdk/types/decoding.d.ts | 7 +++++++ packages/sdk/types/encoding.d.ts | 8 ++++++++ packages/sdk/types/index.d.ts | 2 ++ 11 files changed, 57 insertions(+), 42 deletions(-) create mode 100644 packages/sdk/src/decoding.ts create mode 100644 packages/sdk/src/encoding.ts create mode 100644 packages/sdk/types/decoding.d.ts create mode 100644 packages/sdk/types/encoding.d.ts diff --git a/packages/bcp/src/decode.ts b/packages/bcp/src/decode.ts index 002cc5b366..31712fc08c 100644 --- a/packages/bcp/src/decode.ts +++ b/packages/bcp/src/decode.ts @@ -1,4 +1,4 @@ -import { isAminoStdTx, TxsResponse } from "@cosmwasm/sdk"; +import { coinToDecimal, isAminoStdTx, TxsResponse } from "@cosmwasm/sdk"; import { Address, Algorithm, @@ -20,7 +20,7 @@ import { import { Encoding } from "@iov/encoding"; import amino from "@tendermint/amino-js"; -import { coinToDecimal, TokenInfos } from "./types"; +import { TokenInfos } from "./types"; const { fromBase64 } = Encoding; diff --git a/packages/bcp/src/encode.ts b/packages/bcp/src/encode.ts index 3ce2ebcf13..5fb4b84220 100644 --- a/packages/bcp/src/encode.ts +++ b/packages/bcp/src/encode.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { AminoTx } from "@cosmwasm/sdk"; +import { AminoTx, decimalToCoin } from "@cosmwasm/sdk"; import { Algorithm, Amount, @@ -14,7 +14,7 @@ import { Secp256k1 } from "@iov/crypto"; import { Decimal, Encoding } from "@iov/encoding"; import amino from "@tendermint/amino-js"; -import { decimalToCoin, TokenInfos } from "./types"; +import { TokenInfos } from "./types"; const { toBase64 } = Encoding; diff --git a/packages/bcp/src/types.ts b/packages/bcp/src/types.ts index c2f5edd175..07003e9032 100644 --- a/packages/bcp/src/types.ts +++ b/packages/bcp/src/types.ts @@ -1,35 +1,8 @@ import { TokenInfo } from "@cosmwasm/sdk"; import { Nonce } from "@iov/bcp"; -import { Decimal } from "@iov/encoding"; -import amino from "@tendermint/amino-js"; export type TokenInfos = ReadonlyArray; -export function decimalToCoin(lookup: readonly TokenInfo[], value: Decimal, ticker: string): amino.Coin { - const match = lookup.find(token => token.ticker === ticker); - if (!match) { - throw Error(`unknown ticker: ${ticker}`); - } - if (match.fractionalDigits !== value.fractionalDigits) { - throw new Error( - "Mismatch in fractional digits between token and value. If you really want, implement a conversion here. However, this indicates a bug in the caller code.", - ); - } - return { - denom: match.denom, - amount: value.atomics, - }; -} - -export function coinToDecimal(tokens: readonly TokenInfo[], coin: amino.Coin): readonly [Decimal, string] { - const match = tokens.find(({ denom }) => denom === coin.denom); - if (!match) { - throw Error(`unknown denom: ${coin.denom}`); - } - const value = Decimal.fromAtomics(coin.amount, match.fractionalDigits); - return [value, match.ticker]; -} - // tslint:disable-next-line:no-bitwise const maxAcct = 1 << 23; // tslint:disable-next-line:no-bitwise diff --git a/packages/bcp/types/types.d.ts b/packages/bcp/types/types.d.ts index 6a8f6f4964..8bdcbf3b0c 100644 --- a/packages/bcp/types/types.d.ts +++ b/packages/bcp/types/types.d.ts @@ -1,17 +1,6 @@ import { TokenInfo } from "@cosmwasm/sdk"; import { Nonce } from "@iov/bcp"; -import { Decimal } from "@iov/encoding"; -import amino from "@tendermint/amino-js"; export declare type TokenInfos = ReadonlyArray; -export declare function decimalToCoin( - lookup: readonly TokenInfo[], - value: Decimal, - ticker: string, -): amino.Coin; -export declare function coinToDecimal( - tokens: readonly TokenInfo[], - coin: amino.Coin, -): readonly [Decimal, string]; export interface NonceInfo { readonly account_number: string; readonly sequence: string; diff --git a/packages/sdk/package.json b/packages/sdk/package.json index d69de11aa3..e3a7ccf65c 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -38,6 +38,7 @@ "pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js" }, "dependencies": { + "@iov/encoding": "^2.0.0-alpha.7", "@tendermint/amino-js": "^0.7.0-alpha.1", "axios": "^0.19.0" }, diff --git a/packages/sdk/src/decoding.ts b/packages/sdk/src/decoding.ts new file mode 100644 index 0000000000..701e1e69cd --- /dev/null +++ b/packages/sdk/src/decoding.ts @@ -0,0 +1,13 @@ +import { Decimal } from "@iov/encoding"; +import amino from "@tendermint/amino-js"; + +import { TokenInfo } from "./types"; + +export function coinToDecimal(tokens: readonly TokenInfo[], coin: amino.Coin): readonly [Decimal, string] { + const match = tokens.find(({ denom }) => denom === coin.denom); + if (!match) { + throw Error(`unknown denom: ${coin.denom}`); + } + const value = Decimal.fromAtomics(coin.amount, match.fractionalDigits); + return [value, match.ticker]; +} diff --git a/packages/sdk/src/encoding.ts b/packages/sdk/src/encoding.ts new file mode 100644 index 0000000000..c4bed170b3 --- /dev/null +++ b/packages/sdk/src/encoding.ts @@ -0,0 +1,20 @@ +import { Decimal } from "@iov/encoding"; +import amino from "@tendermint/amino-js"; + +import { TokenInfo } from "./types"; + +export function decimalToCoin(lookup: readonly TokenInfo[], value: Decimal, ticker: string): amino.Coin { + const match = lookup.find(token => token.ticker === ticker); + if (!match) { + throw Error(`unknown ticker: ${ticker}`); + } + if (match.fractionalDigits !== value.fractionalDigits) { + throw new Error( + "Mismatch in fractional digits between token and value. If you really want, implement a conversion here. However, this indicates a bug in the caller code.", + ); + } + return { + denom: match.denom, + amount: value.atomics, + }; +} diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index d4162d9046..888fb6c385 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,2 +1,4 @@ +export { coinToDecimal } from "./decoding"; +export { decimalToCoin } from "./encoding"; export { RestClient, TxsResponse } from "./restclient"; export { AminoTx, isAminoStdTx, TokenInfo } from "./types"; diff --git a/packages/sdk/types/decoding.d.ts b/packages/sdk/types/decoding.d.ts new file mode 100644 index 0000000000..6c1552ebf2 --- /dev/null +++ b/packages/sdk/types/decoding.d.ts @@ -0,0 +1,7 @@ +import { Decimal } from "@iov/encoding"; +import amino from "@tendermint/amino-js"; +import { TokenInfo } from "./types"; +export declare function coinToDecimal( + tokens: readonly TokenInfo[], + coin: amino.Coin, +): readonly [Decimal, string]; diff --git a/packages/sdk/types/encoding.d.ts b/packages/sdk/types/encoding.d.ts new file mode 100644 index 0000000000..c8e3334e90 --- /dev/null +++ b/packages/sdk/types/encoding.d.ts @@ -0,0 +1,8 @@ +import { Decimal } from "@iov/encoding"; +import amino from "@tendermint/amino-js"; +import { TokenInfo } from "./types"; +export declare function decimalToCoin( + lookup: readonly TokenInfo[], + value: Decimal, + ticker: string, +): amino.Coin; diff --git a/packages/sdk/types/index.d.ts b/packages/sdk/types/index.d.ts index d4162d9046..888fb6c385 100644 --- a/packages/sdk/types/index.d.ts +++ b/packages/sdk/types/index.d.ts @@ -1,2 +1,4 @@ +export { coinToDecimal } from "./decoding"; +export { decimalToCoin } from "./encoding"; export { RestClient, TxsResponse } from "./restclient"; export { AminoTx, isAminoStdTx, TokenInfo } from "./types";