Merge pull request #28 from confio/add-sdk

Extract RestClient, TxsResponse, AminoTx into @cosmwasm/sdk
This commit is contained in:
Simon Warta 2020-02-02 21:46:31 +01:00 committed by GitHub
commit 8610da30d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 383 additions and 121 deletions

View File

@ -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

View File

@ -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"

View File

@ -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",
},
];

View File

@ -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",
},
];

View File

@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/camelcase */
import { RestClient, TokenInfo, TxsResponse } from "@cosmwasm/sdk";
import {
Account,
AccountQuery,
@ -37,8 +38,7 @@ 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";
import { accountToNonce } 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<CosmWasmConnection> {
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<ChainData> {
@ -88,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;
@ -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];
@ -158,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,
};
}

View File

@ -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<CosmWasmConnection> {
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,
};

View File

@ -55,8 +55,7 @@ describe("decode", () => {
const defaultTokens: TokenInfos = [
{
fractionalDigits: 6,
tokenName: "Atom (Cosmos Hub)",
tokenTicker: "ATOM" as TokenTicker,
ticker: "ATOM",
denom: "uatom",
},
];
@ -118,7 +117,7 @@ describe("decode", () => {
denom: "uatom",
amount: "11657995",
};
expect(decodeAmount(defaultTokens)(amount)).toEqual(defaultAmount);
expect(decodeAmount(defaultTokens, amount)).toEqual(defaultAmount);
});
});

View File

@ -1,3 +1,4 @@
import { coinToDecimal, isAminoStdTx, TxsResponse } from "@cosmwasm/sdk";
import {
Address,
Algorithm,
@ -12,14 +13,14 @@ import {
SendTransaction,
SignatureBytes,
SignedTransaction,
TokenTicker,
TransactionId,
UnsignedTransaction,
} from "@iov/bcp";
import { Encoding } from "@iov/encoding";
import amino from "@tendermint/amino-js";
import { TxsResponse } from "./restclient";
import { coinToAmount, isAminoStdTx, TokenInfos } from "./types";
import { TokenInfos } from "./types";
const { fromBase64 } = Encoding;
@ -54,10 +55,14 @@ 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 => {
return coinToAmount(tokens, coin);
};
export function decodeAmount(tokens: TokenInfos, coin: amino.Coin): Amount {
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 {
if (msg.type !== "cosmos-sdk/MsgSend") {
@ -75,7 +80,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]),
};
}
@ -84,7 +89,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,
};
}

View File

@ -44,8 +44,7 @@ describe("encode", () => {
const defaultTokens: TokenInfos = [
{
fractionalDigits: 6,
tokenName: "Atom (Cosmos Hub)",
tokenTicker: "ATOM" as TokenTicker,
ticker: "ATOM",
denom: "uatom",
},
];

View File

@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/camelcase */
import { AminoTx, decimalToCoin } from "@cosmwasm/sdk";
import {
Algorithm,
Amount,
@ -10,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 { AminoTx, amountToCoin, TokenInfos } from "./types";
import { TokenInfos } from "./types";
const { toBase64 } = Encoding;
@ -35,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 {

View File

@ -1,4 +1,3 @@
export { CosmWasmCodec } from "./cosmwasmcodec";
export { CosmWasmConnection } from "./cosmwasmconnection";
export { CosmWasmConnection, TokenConfiguration } from "./cosmwasmconnection";
export { createCosmWasmConnector } from "./cosmwasmconnector";
export { TokenInfo } from "./types";

View File

@ -1,46 +1,8 @@
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 (
typeof memo === "string" && Array.isArray(msg) && typeof fee === "object" && Array.isArray(signatures)
);
}
export interface TokenInfo extends Token {
readonly denom: string;
}
import { TokenInfo } from "@cosmwasm/sdk";
import { Nonce } from "@iov/bcp";
export type TokenInfos = ReadonlyArray<TokenInfo>;
// TODO: return null vs throw exception for undefined???
export function amountToCoin(lookup: ReadonlyArray<TokenInfo>, amount: Amount): amino.Coin {
const match = lookup.find(({ tokenTicker }) => tokenTicker === amount.tokenTicker);
if (!match) {
throw Error(`unknown ticker: ${amount.tokenTicker}`);
}
return {
denom: match.denom,
amount: amount.quantity,
};
}
// 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) {
throw Error(`unknown denom: ${coin.denom}`);
}
return {
tokenTicker: match.tokenTicker,
fractionalDigits: match.fractionalDigits,
quantity: coin.amount,
};
}
// tslint:disable-next-line:no-bitwise
const maxAcct = 1 << 23;
// tslint:disable-next-line:no-bitwise

View File

@ -1,3 +1,4 @@
import { TokenInfo } from "@cosmwasm/sdk";
import {
Account,
AccountQuery,
@ -21,12 +22,14 @@ import {
} from "@iov/bcp";
import { Stream } from "xstream";
import { CosmosBech32Prefix } from "./address";
import { TokenInfos } 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<CosmWasmConnection>;
private static initialize;
private readonly restClient;

View File

@ -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<CosmWasmConnection>;

View File

@ -1,3 +1,4 @@
import { TxsResponse } from "@cosmwasm/sdk";
import {
Amount,
ChainId,
@ -12,12 +13,11 @@ 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;
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(

View File

@ -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;

View File

@ -1,4 +1,3 @@
export { CosmWasmCodec } from "./cosmwasmcodec";
export { CosmWasmConnection } from "./cosmwasmconnection";
export { CosmWasmConnection, TokenConfiguration } from "./cosmwasmconnection";
export { createCosmWasmConnector } from "./cosmwasmconnector";
export { TokenInfo } from "./types";

View File

@ -1,15 +1,6 @@
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;
}
import { TokenInfo } from "@cosmwasm/sdk";
import { Nonce } from "@iov/bcp";
export declare type TokenInfos = ReadonlyArray<TokenInfo>;
export declare function amountToCoin(lookup: ReadonlyArray<TokenInfo>, amount: Amount): amino.Coin;
export declare function coinToAmount(tokens: TokenInfos, coin: amino.Coin): Amount;
export interface NonceInfo {
readonly account_number: string;
readonly sequence: string;

View File

@ -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",
},
];

1
packages/sdk/.eslintignore Symbolic link
View File

@ -0,0 +1 @@
../../.eslintignore

3
packages/sdk/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
build/
dist/
docs/

View File

@ -0,0 +1,12 @@
{
"suite": {
"displayNumber": true
},
"spec": {
"displayDuration": true
},
"summary": {
"displayPending": false,
"displayStacktrace": true
}
}

View File

@ -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();

View File

@ -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"],
},
},
});
};

View File

@ -0,0 +1 @@
Directory used to trigger lerna package updates for all packages

47
packages/sdk/package.json Normal file
View File

@ -0,0 +1,47 @@
{
"name": "@cosmwasm/sdk",
"version": "0.0.1",
"description": "CosmWasm SDK",
"author": "Ethan Frey <ethanfrey@users.noreply.github.com>",
"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": {
"@iov/encoding": "^2.0.0-alpha.7",
"@tendermint/amino-js": "^0.7.0-alpha.1",
"axios": "^0.19.0"
},
"devDependencies": {
}
}

View File

@ -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];
}

View File

@ -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,
};
}

View File

@ -0,0 +1,4 @@
export { coinToDecimal } from "./decoding";
export { decimalToCoin } from "./encoding";
export { RestClient, TxsResponse } from "./restclient";
export { AminoTx, isAminoStdTx, TokenInfo } from "./types";

View File

@ -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();
});
});

View File

@ -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<AuthAccountsResponse> {
public async authAccounts(address: string, height?: string): Promise<AuthAccountsResponse> {
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<TxsResponse> {
public async txsById(id: string): Promise<TxsResponse> {
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<PostTxsResponse> {
public async postTx(tx: Uint8Array): Promise<PostTxsResponse> {
const unmarshalled = unmarshalTx(tx, true);
const params = {
tx: unmarshalled.value,

25
packages/sdk/src/types.ts Normal file
View File

@ -0,0 +1,25 @@
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)
);
}
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;
}

View File

@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"outDir": "build",
"declarationDir": "build/types",
"rootDir": "src"
},
"include": [
"src/**/*"
]
}

3
packages/sdk/tslint.json Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "../../tslint.json"
}

14
packages/sdk/typedoc.js Normal file
View File

@ -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,
};

7
packages/sdk/types/decoding.d.ts vendored Normal file
View File

@ -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];

8
packages/sdk/types/encoding.d.ts vendored Normal file
View File

@ -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;

4
packages/sdk/types/index.d.ts vendored Normal file
View File

@ -0,0 +1,4 @@
export { coinToDecimal } from "./decoding";
export { decimalToCoin } from "./encoding";
export { RestClient, TxsResponse } from "./restclient";
export { AminoTx, isAminoStdTx, TokenInfo } from "./types";

View File

@ -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<NodeInfoResponse>;
blocksLatest(): Promise<BlocksResponse>;
blocks(height: number): Promise<BlocksResponse>;
authAccounts(address: Address, height?: string): Promise<AuthAccountsResponse>;
authAccounts(address: string, height?: string): Promise<AuthAccountsResponse>;
txs(query: string): Promise<SearchTxsResponse>;
txsById(id: TransactionId): Promise<TxsResponse>;
postTx(tx: PostableBytes): Promise<PostTxsResponse>;
txsById(id: string): Promise<TxsResponse>;
postTx(tx: Uint8Array): Promise<PostTxsResponse>;
}
export {};

19
packages/sdk/types/types.d.ts vendored Normal file
View File

@ -0,0 +1,19 @@
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 {
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;
}

View File

@ -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"])],
},
];