From 433362608c70627108cde72639a882c025ad8057 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 06:39:34 +0200 Subject: [PATCH 01/37] Improve source/builder documentation --- packages/cosmwasm/src/cosmwasmclient.ts | 11 +++++++++++ packages/cosmwasm/src/restclient.ts | 12 +++++++++++- packages/cosmwasm/src/signingcosmwasmclient.ts | 13 +++++++++++-- packages/cosmwasm/types/cosmwasmclient.d.ts | 11 +++++++++++ packages/cosmwasm/types/restclient.d.ts | 11 +++++++++++ packages/cosmwasm/types/signingcosmwasmclient.d.ts | 13 +++++++++++-- 6 files changed, 66 insertions(+), 5 deletions(-) diff --git a/packages/cosmwasm/src/cosmwasmclient.ts b/packages/cosmwasm/src/cosmwasmclient.ts index 4d7d4a4c..1df52b9f 100644 --- a/packages/cosmwasm/src/cosmwasmclient.ts +++ b/packages/cosmwasm/src/cosmwasmclient.ts @@ -104,7 +104,18 @@ export interface Code { readonly creator: string; /** Hex-encoded sha256 hash of the code stored here */ readonly checksum: string; + /** + * An URL to a .tar.gz archive of the source code of the contract, which can be used to reproducibly build the Wasm bytecode. + * + * @see https://github.com/CosmWasm/cosmwasm-verify + */ readonly source?: string; + /** + * A docker image (including version) to reproducibly build the Wasm bytecode from the source code. + * + * @example ```cosmwasm/rust-optimizer:0.8.0``` + * @see https://github.com/CosmWasm/cosmwasm-verify + */ readonly builder?: string; } diff --git a/packages/cosmwasm/src/restclient.ts b/packages/cosmwasm/src/restclient.ts index a915f565..d5406da6 100644 --- a/packages/cosmwasm/src/restclient.ts +++ b/packages/cosmwasm/src/restclient.ts @@ -23,8 +23,18 @@ export interface CodeInfo { readonly creator: string; /** Hex-encoded sha256 hash of the code stored here */ readonly data_hash: string; - // TODO: these are not supported in current wasmd + /** + * An URL to a .tar.gz archive of the source code of the contract, which can be used to reproducibly build the Wasm bytecode. + * + * @see https://github.com/CosmWasm/cosmwasm-verify + */ readonly source?: string; + /** + * A docker image (including version) to reproducibly build the Wasm bytecode from the source code. + * + * @example ```cosmwasm/rust-optimizer:0.8.0``` + * @see https://github.com/CosmWasm/cosmwasm-verify + */ readonly builder?: string; } diff --git a/packages/cosmwasm/src/signingcosmwasmclient.ts b/packages/cosmwasm/src/signingcosmwasmclient.ts index cae59657..e713e82f 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.ts @@ -83,9 +83,18 @@ const defaultFees: FeeTable = { }; export interface UploadMeta { - /** The source URL */ + /** + * An URL to a .tar.gz archive of the source code of the contract, which can be used to reproducibly build the Wasm bytecode. + * + * @see https://github.com/CosmWasm/cosmwasm-verify + */ readonly source?: string; - /** The builder tag */ + /** + * A docker image (including version) to reproducibly build the Wasm bytecode from the source code. + * + * @example ```cosmwasm/rust-optimizer:0.8.0``` + * @see https://github.com/CosmWasm/cosmwasm-verify + */ readonly builder?: string; } diff --git a/packages/cosmwasm/types/cosmwasmclient.d.ts b/packages/cosmwasm/types/cosmwasmclient.d.ts index 87e5360c..82eecb72 100644 --- a/packages/cosmwasm/types/cosmwasmclient.d.ts +++ b/packages/cosmwasm/types/cosmwasmclient.d.ts @@ -64,7 +64,18 @@ export interface Code { readonly creator: string; /** Hex-encoded sha256 hash of the code stored here */ readonly checksum: string; + /** + * An URL to a .tar.gz archive of the source code of the contract, which can be used to reproducibly build the Wasm bytecode. + * + * @see https://github.com/CosmWasm/cosmwasm-verify + */ readonly source?: string; + /** + * A docker image (including version) to reproducibly build the Wasm bytecode from the source code. + * + * @example ```cosmwasm/rust-optimizer:0.8.0``` + * @see https://github.com/CosmWasm/cosmwasm-verify + */ readonly builder?: string; } export interface CodeDetails extends Code { diff --git a/packages/cosmwasm/types/restclient.d.ts b/packages/cosmwasm/types/restclient.d.ts index 2ca6f5e2..43e4be1e 100644 --- a/packages/cosmwasm/types/restclient.d.ts +++ b/packages/cosmwasm/types/restclient.d.ts @@ -6,7 +6,18 @@ export interface CodeInfo { readonly creator: string; /** Hex-encoded sha256 hash of the code stored here */ readonly data_hash: string; + /** + * An URL to a .tar.gz archive of the source code of the contract, which can be used to reproducibly build the Wasm bytecode. + * + * @see https://github.com/CosmWasm/cosmwasm-verify + */ readonly source?: string; + /** + * A docker image (including version) to reproducibly build the Wasm bytecode from the source code. + * + * @example ```cosmwasm/rust-optimizer:0.8.0``` + * @see https://github.com/CosmWasm/cosmwasm-verify + */ readonly builder?: string; } export interface CodeDetails extends CodeInfo { diff --git a/packages/cosmwasm/types/signingcosmwasmclient.d.ts b/packages/cosmwasm/types/signingcosmwasmclient.d.ts index b86bebeb..2a3e17ea 100644 --- a/packages/cosmwasm/types/signingcosmwasmclient.d.ts +++ b/packages/cosmwasm/types/signingcosmwasmclient.d.ts @@ -14,9 +14,18 @@ export interface FeeTable { readonly changeAdmin: StdFee; } export interface UploadMeta { - /** The source URL */ + /** + * An URL to a .tar.gz archive of the source code of the contract, which can be used to reproducibly build the Wasm bytecode. + * + * @see https://github.com/CosmWasm/cosmwasm-verify + */ readonly source?: string; - /** The builder tag */ + /** + * A docker image (including version) to reproducibly build the Wasm bytecode from the source code. + * + * @example ```cosmwasm/rust-optimizer:0.8.0``` + * @see https://github.com/CosmWasm/cosmwasm-verify + */ readonly builder?: string; } export interface UploadResult { From a63ddb40dd6a8551c2afb5cfa42e0b5ffe2a06a5 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 07:04:54 +0200 Subject: [PATCH 02/37] Create initial LcdClient --- packages/sdk38/src/lcdclient.spec.ts | 869 +++++++++++++++++++++++++++ packages/sdk38/src/lcdclient.ts | 217 +++++++ packages/sdk38/types/lcdclient.d.ts | 66 ++ 3 files changed, 1152 insertions(+) create mode 100644 packages/sdk38/src/lcdclient.spec.ts create mode 100644 packages/sdk38/src/lcdclient.ts create mode 100644 packages/sdk38/types/lcdclient.d.ts diff --git a/packages/sdk38/src/lcdclient.spec.ts b/packages/sdk38/src/lcdclient.spec.ts new file mode 100644 index 00000000..c0eee65e --- /dev/null +++ b/packages/sdk38/src/lcdclient.spec.ts @@ -0,0 +1,869 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { assert, sleep } from "@cosmjs/utils"; + +import { rawSecp256k1PubkeyToAddress } from "./address"; +import { Coin } from "./coins"; +import { isPostTxFailure } from "./cosmosclient"; +import { makeSignBytes } from "./encoding"; +import { CosmosSdkArray, LcdClient, normalizeArray } from "./lcdclient"; +import { parseLogs } from "./logs"; +import { Msg, MsgSend } from "./msgs"; +import { makeCosmoshubPath, Secp256k1Pen } from "./pen"; +import { BroadcastMode, TxsResponse } from "./restclient"; +import { SigningCosmosClient } from "./signingcosmosclient"; +import cosmoshub from "./testdata/cosmoshub.json"; +import { + faucet, + makeRandomAddress, + nonNegativeIntegerMatcher, + pendingWithoutWasmd, + tendermintIdMatcher, + wasmd, + wasmdEnabled, +} from "./testutils.spec"; +import { StdFee, StdSignature, StdTx } from "./types"; + +const emptyAddress = "cosmos1ltkhnmdcqemmd2tkhnx7qx66tq7e0wykw2j85k"; + +/** Deployed as part of scripts/wasmd/init.sh */ +export const deployedErc20 = { + codeId: 1, + source: "https://crates.io/api/v1/crates/cw-erc20/0.5.1/download", + builder: "cosmwasm/rust-optimizer:0.8.0", + checksum: "3e97bf88bd960fee5e5959c77b972eb2927690bc10160792741b174f105ec0c5", + instances: [ + "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5", // HASH + "cosmos1hqrdl6wstt8qzshwc6mrumpjk9338k0lr4dqxd", // ISA + "cosmos18r5szma8hm93pvx6lwpjwyxruw27e0k5uw835c", // JADE + ], +}; + +function makeSignedTx(firstMsg: Msg, fee: StdFee, memo: string, firstSignature: StdSignature): StdTx { + return { + msg: [firstMsg], + fee: fee, + memo: memo, + signatures: [firstSignature], + }; +} + +describe("LcdClient", () => { + it("can be constructed", () => { + const client = new LcdClient(wasmd.endpoint); + expect(client).toBeTruthy(); + }); + + describe("withModules", () => { + interface CodeInfo { + readonly id: number; + /** Bech32 account address */ + readonly creator: string; + /** Hex-encoded sha256 hash of the code stored here */ + readonly data_hash: string; + readonly source?: string; + readonly builder?: string; + } + + // Currently all wasm query responses return json-encoded strings... + // later deprecate this and use the specific types for result + // (assuming it is inlined, no second parse needed) + type WasmResponse = WasmSuccess | WasmError; + + interface WasmSuccess { + readonly height: string; + readonly result: T; + } + + interface WasmError { + readonly error: string; + } + + function isWasmError(resp: WasmResponse): resp is WasmError { + return (resp as WasmError).error !== undefined; + } + + function unwrapWasmResponse(response: WasmResponse): T { + if (isWasmError(response)) { + throw new Error(response.error); + } + return response.result; + } + + interface WasmModule extends Record any> { + listCodeInfo: () => Promise; + } + + it("works for no modules", async () => { + const client = LcdClient.withModules(wasmd.endpoint, BroadcastMode.Sync); + expect(client).toBeTruthy(); + }); + + it("works for one module", async () => { + pendingWithoutWasmd(); + function wasmClientRegisterer(base: LcdClient): WasmModule { + return { + listCodeInfo: async (): Promise => { + const path = `/wasm/code`; + const responseData = (await base.get(path)) as WasmResponse>; + return normalizeArray(unwrapWasmResponse(responseData)); + }, + }; + } + + const client = LcdClient.withModules(wasmd.endpoint, BroadcastMode.Sync, wasmClientRegisterer); + const codes = await client.listCodeInfo(); + expect(codes.length).toBeGreaterThanOrEqual(3); + expect(codes[0].id).toEqual(deployedErc20.codeId); + expect(codes[0].data_hash).toEqual(deployedErc20.checksum.toUpperCase()); + expect(codes[0].builder).toEqual(deployedErc20.builder); + expect(codes[0].source).toEqual(deployedErc20.source); + }); + + it("works for two modules", async () => { + pendingWithoutWasmd(); + function registerWasmModule(base: LcdClient): WasmModule { + return { + listCodeInfo: async (): Promise => { + const path = `/wasm/code`; + const responseData = (await base.get(path)) as WasmResponse>; + return normalizeArray(unwrapWasmResponse(responseData)); + }, + }; + } + + interface TotalSupplyReponse { + readonly height: string; + readonly result: CosmosSdkArray; + } + + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + function registerSupplyModule(base: LcdClient) { + return { + totalSupply: async (): Promise => { + const path = `/supply/total`; + return (await base.get(path)) as TotalSupplyReponse; + }, + }; + } + + const client = LcdClient.withModules( + wasmd.endpoint, + BroadcastMode.Sync, + registerWasmModule, + registerSupplyModule, + ); + const codes = await client.listCodeInfo(); + expect(codes.length).toBeGreaterThanOrEqual(3); + expect(codes[0].id).toEqual(deployedErc20.codeId); + expect(codes[0].data_hash).toEqual(deployedErc20.checksum.toUpperCase()); + expect(codes[0].builder).toEqual(deployedErc20.builder); + expect(codes[0].source).toEqual(deployedErc20.source); + const supply = await client.totalSupply(); + expect(supply).toEqual({ + height: jasmine.stringMatching(/^[0-9]+$/), + result: [ + { + amount: jasmine.stringMatching(/^[0-9]+$/), + denom: "ucosm", + }, + { + amount: jasmine.stringMatching(/^[0-9]+$/), + denom: "ustake", + }, + ], + }); + }); + }); + + // The /txs endpoints + + describe("txById", () => { + let successful: + | { + readonly sender: string; + readonly recipient: string; + readonly hash: string; + } + | undefined; + let unsuccessful: + | { + readonly sender: string; + readonly recipient: string; + readonly hash: string; + } + | undefined; + + beforeAll(async () => { + if (wasmdEnabled()) { + const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic); + const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, (signBytes) => + pen.sign(signBytes), + ); + + { + const recipient = makeRandomAddress(); + const transferAmount = { + denom: "ucosm", + amount: "1234567", + }; + const result = await client.sendTokens(recipient, [transferAmount]); + successful = { + sender: faucet.address, + recipient: recipient, + hash: result.transactionHash, + }; + } + + { + const memo = "Sending more than I can afford"; + const recipient = makeRandomAddress(); + const transferAmount = [ + { + denom: "ucosm", + amount: "123456700000000", + }, + ]; + const sendMsg: MsgSend = { + type: "cosmos-sdk/MsgSend", + value: { + // eslint-disable-next-line @typescript-eslint/camelcase + from_address: faucet.address, + // eslint-disable-next-line @typescript-eslint/camelcase + to_address: recipient, + amount: transferAmount, + }, + }; + const fee = { + amount: [ + { + denom: "ucosm", + amount: "2000", + }, + ], + gas: "80000", // 80k + }; + const { accountNumber, sequence } = await client.getNonce(); + const chainId = await client.getChainId(); + const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence); + const signature = await pen.sign(signBytes); + const signedTx = { + msg: [sendMsg], + fee: fee, + memo: memo, + signatures: [signature], + }; + const transactionId = await client.getIdentifier({ type: "cosmos-sdk/StdTx", value: signedTx }); + const result = await client.postTx(signedTx); + assert(isPostTxFailure(result)); + unsuccessful = { + sender: faucet.address, + recipient: recipient, + hash: transactionId, + }; + } + + await sleep(75); // wait until transactions are indexed + } + }); + + it("works for successful transaction", async () => { + pendingWithoutWasmd(); + assert(successful); + const client = new LcdClient(wasmd.endpoint); + const result = await client.txById(successful.hash); + expect(result.height).toBeGreaterThanOrEqual(1); + expect(result.txhash).toEqual(successful.hash); + expect(result.codespace).toBeUndefined(); + expect(result.code).toBeUndefined(); + const logs = parseLogs(result.logs); + expect(logs).toEqual([ + { + msg_index: 0, + log: "", + events: [ + { + type: "message", + attributes: [ + { key: "action", value: "send" }, + { key: "sender", value: successful.sender }, + { key: "module", value: "bank" }, + ], + }, + { + type: "transfer", + attributes: [ + { key: "recipient", value: successful.recipient }, + { key: "sender", value: successful.sender }, + { key: "amount", value: "1234567ucosm" }, + ], + }, + ], + }, + ]); + }); + + it("works for unsuccessful transaction", async () => { + pendingWithoutWasmd(); + assert(unsuccessful); + const client = new LcdClient(wasmd.endpoint); + const result = await client.txById(unsuccessful.hash); + expect(result.height).toBeGreaterThanOrEqual(1); + expect(result.txhash).toEqual(unsuccessful.hash); + expect(result.codespace).toEqual("sdk"); + expect(result.code).toEqual(5); + expect(result.logs).toBeUndefined(); + expect(result.raw_log).toContain("insufficient funds"); + }); + }); + + describe("txsQuery", () => { + let posted: + | { + readonly sender: string; + readonly recipient: string; + readonly hash: string; + readonly height: number; + readonly tx: TxsResponse; + } + | undefined; + + beforeAll(async () => { + if (wasmdEnabled()) { + const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic); + const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, (signBytes) => + pen.sign(signBytes), + ); + + const recipient = makeRandomAddress(); + const transferAmount = [ + { + denom: "ucosm", + amount: "1234567", + }, + ]; + const result = await client.sendTokens(recipient, transferAmount); + + await sleep(75); // wait until tx is indexed + const txDetails = await new LcdClient(wasmd.endpoint).txById(result.transactionHash); + posted = { + sender: faucet.address, + recipient: recipient, + hash: result.transactionHash, + height: Number.parseInt(txDetails.height, 10), + tx: txDetails, + }; + } + }); + + it("can query transactions by height", async () => { + pendingWithoutWasmd(); + assert(posted); + const client = new LcdClient(wasmd.endpoint); + const result = await client.txsQuery(`tx.height=${posted.height}&limit=26`); + expect(result).toEqual({ + count: jasmine.stringMatching(/^(1|2|3|4|5)$/), // 1-5 transactions as string + limit: "26", + page_number: "1", + page_total: "1", + total_count: jasmine.stringMatching(/^(1|2|3|4|5)$/), // 1-5 transactions as string + txs: jasmine.arrayContaining([posted.tx]), + }); + }); + + it("can query transactions by ID", async () => { + pendingWithoutWasmd(); + assert(posted); + const client = new LcdClient(wasmd.endpoint); + const result = await client.txsQuery(`tx.hash=${posted.hash}&limit=26`); + expect(result).toEqual({ + count: "1", + limit: "26", + page_number: "1", + page_total: "1", + total_count: "1", + txs: [posted.tx], + }); + }); + + it("can query transactions by sender", async () => { + pendingWithoutWasmd(); + assert(posted); + const client = new LcdClient(wasmd.endpoint); + const result = await client.txsQuery(`message.sender=${posted.sender}&limit=200`); + expect(parseInt(result.count, 10)).toBeGreaterThanOrEqual(1); + expect(parseInt(result.limit, 10)).toEqual(200); + expect(parseInt(result.page_number, 10)).toEqual(1); + expect(parseInt(result.page_total, 10)).toEqual(1); + expect(parseInt(result.total_count, 10)).toBeGreaterThanOrEqual(1); + expect(result.txs.length).toBeGreaterThanOrEqual(1); + expect(result.txs[result.txs.length - 1]).toEqual(posted.tx); + }); + + it("can query transactions by recipient", async () => { + pendingWithoutWasmd(); + assert(posted); + const client = new LcdClient(wasmd.endpoint); + const result = await client.txsQuery(`transfer.recipient=${posted.recipient}&limit=200`); + expect(parseInt(result.count, 10)).toEqual(1); + expect(parseInt(result.limit, 10)).toEqual(200); + expect(parseInt(result.page_number, 10)).toEqual(1); + expect(parseInt(result.page_total, 10)).toEqual(1); + expect(parseInt(result.total_count, 10)).toEqual(1); + expect(result.txs.length).toBeGreaterThanOrEqual(1); + expect(result.txs[result.txs.length - 1]).toEqual(posted.tx); + }); + + it("can filter by tx.hash and tx.minheight", async () => { + pending("This combination is broken 🤷‍♂️. Handle client-side at higher level."); + pendingWithoutWasmd(); + assert(posted); + const client = new LcdClient(wasmd.endpoint); + const hashQuery = `tx.hash=${posted.hash}`; + + { + const { count } = await client.txsQuery(`${hashQuery}&tx.minheight=0`); + expect(count).toEqual("1"); + } + + { + const { count } = await client.txsQuery(`${hashQuery}&tx.minheight=${posted.height - 1}`); + expect(count).toEqual("1"); + } + + { + const { count } = await client.txsQuery(`${hashQuery}&tx.minheight=${posted.height}`); + expect(count).toEqual("1"); + } + + { + const { count } = await client.txsQuery(`${hashQuery}&tx.minheight=${posted.height + 1}`); + expect(count).toEqual("0"); + } + }); + + it("can filter by recipient and tx.minheight", async () => { + pendingWithoutWasmd(); + assert(posted); + const client = new LcdClient(wasmd.endpoint); + const recipientQuery = `transfer.recipient=${posted.recipient}`; + + { + const { count } = await client.txsQuery(`${recipientQuery}&tx.minheight=0`); + expect(count).toEqual("1"); + } + + { + const { count } = await client.txsQuery(`${recipientQuery}&tx.minheight=${posted.height - 1}`); + expect(count).toEqual("1"); + } + + { + const { count } = await client.txsQuery(`${recipientQuery}&tx.minheight=${posted.height}`); + expect(count).toEqual("1"); + } + + { + const { count } = await client.txsQuery(`${recipientQuery}&tx.minheight=${posted.height + 1}`); + expect(count).toEqual("0"); + } + }); + + it("can filter by recipient and tx.maxheight", async () => { + pendingWithoutWasmd(); + assert(posted); + const client = new LcdClient(wasmd.endpoint); + const recipientQuery = `transfer.recipient=${posted.recipient}`; + + { + const { count } = await client.txsQuery(`${recipientQuery}&tx.maxheight=9999999999999`); + expect(count).toEqual("1"); + } + + { + const { count } = await client.txsQuery(`${recipientQuery}&tx.maxheight=${posted.height + 1}`); + expect(count).toEqual("1"); + } + + { + const { count } = await client.txsQuery(`${recipientQuery}&tx.maxheight=${posted.height}`); + expect(count).toEqual("1"); + } + + { + const { count } = await client.txsQuery(`${recipientQuery}&tx.maxheight=${posted.height - 1}`); + expect(count).toEqual("0"); + } + }); + }); + + describe("encodeTx", () => { + it("works for cosmoshub example", async () => { + pendingWithoutWasmd(); + const client = new LcdClient(wasmd.endpoint); + const response = await client.encodeTx(cosmoshub.tx); + expect(response).toEqual( + jasmine.objectContaining({ + tx: cosmoshub.tx_data, + }), + ); + }); + }); + + describe("postTx", () => { + it("can send tokens", async () => { + pendingWithoutWasmd(); + const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic); + + const memo = "My first contract on chain"; + const theMsg: MsgSend = { + type: "cosmos-sdk/MsgSend", + value: { + from_address: faucet.address, + to_address: emptyAddress, + amount: [ + { + denom: "ucosm", + amount: "1234567", + }, + ], + }, + }; + + const fee: StdFee = { + amount: [ + { + amount: "5000", + denom: "ucosm", + }, + ], + gas: "890000", + }; + + const client = new LcdClient(wasmd.endpoint); + const { account_number, sequence } = (await client.authAccounts(faucet.address)).result.value; + + const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); + const signature = await pen.sign(signBytes); + const signedTx = makeSignedTx(theMsg, fee, memo, signature); + const result = await client.postTx(signedTx); + expect(result.code).toBeUndefined(); + expect(result).toEqual({ + height: jasmine.stringMatching(nonNegativeIntegerMatcher), + txhash: jasmine.stringMatching(tendermintIdMatcher), + // code is not set + raw_log: jasmine.stringMatching(/^\[.+\]$/i), + logs: jasmine.any(Array), + gas_wanted: jasmine.stringMatching(nonNegativeIntegerMatcher), + gas_used: jasmine.stringMatching(nonNegativeIntegerMatcher), + }); + }); + + it("can't send transaction with additional signatures", async () => { + pendingWithoutWasmd(); + const account1 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0)); + const account2 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1)); + const account3 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(2)); + const address1 = rawSecp256k1PubkeyToAddress(account1.pubkey, "cosmos"); + const address2 = rawSecp256k1PubkeyToAddress(account2.pubkey, "cosmos"); + const address3 = rawSecp256k1PubkeyToAddress(account3.pubkey, "cosmos"); + + const memo = "My first contract on chain"; + const theMsg: MsgSend = { + type: "cosmos-sdk/MsgSend", + value: { + from_address: address1, + to_address: emptyAddress, + amount: [ + { + denom: "ucosm", + amount: "1234567", + }, + ], + }, + }; + + const fee: StdFee = { + amount: [ + { + amount: "5000", + denom: "ucosm", + }, + ], + gas: "890000", + }; + + const client = new LcdClient(wasmd.endpoint); + const { account_number: an1, sequence: sequence1 } = (await client.authAccounts(address1)).result.value; + const { account_number: an2, sequence: sequence2 } = (await client.authAccounts(address2)).result.value; + const { account_number: an3, sequence: sequence3 } = (await client.authAccounts(address3)).result.value; + + const signBytes1 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an1, sequence1); + const signBytes2 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an2, sequence2); + const signBytes3 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an3, sequence3); + const signature1 = await account1.sign(signBytes1); + const signature2 = await account2.sign(signBytes2); + const signature3 = await account3.sign(signBytes3); + const signedTx = { + msg: [theMsg], + fee: fee, + memo: memo, + signatures: [signature1, signature2, signature3], + }; + const postResult = await client.postTx(signedTx); + expect(postResult.code).toEqual(4); + expect(postResult.raw_log).toContain("wrong number of signers"); + }); + + it("can send multiple messages with one signature", async () => { + pendingWithoutWasmd(); + const account1 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0)); + const address1 = rawSecp256k1PubkeyToAddress(account1.pubkey, "cosmos"); + + const memo = "My first contract on chain"; + const msg1: MsgSend = { + type: "cosmos-sdk/MsgSend", + value: { + from_address: address1, + to_address: emptyAddress, + amount: [ + { + denom: "ucosm", + amount: "1234567", + }, + ], + }, + }; + const msg2: MsgSend = { + type: "cosmos-sdk/MsgSend", + value: { + from_address: address1, + to_address: emptyAddress, + amount: [ + { + denom: "ucosm", + amount: "7654321", + }, + ], + }, + }; + + const fee: StdFee = { + amount: [ + { + amount: "5000", + denom: "ucosm", + }, + ], + gas: "890000", + }; + + const client = new LcdClient(wasmd.endpoint); + const { account_number, sequence } = (await client.authAccounts(address1)).result.value; + + const signBytes = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, account_number, sequence); + const signature1 = await account1.sign(signBytes); + const signedTx = { + msg: [msg1, msg2], + fee: fee, + memo: memo, + signatures: [signature1], + }; + const postResult = await client.postTx(signedTx); + expect(postResult.code).toBeUndefined(); + }); + + it("can send multiple messages with multiple signatures", async () => { + pendingWithoutWasmd(); + const account1 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0)); + const account2 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1)); + const address1 = rawSecp256k1PubkeyToAddress(account1.pubkey, "cosmos"); + const address2 = rawSecp256k1PubkeyToAddress(account2.pubkey, "cosmos"); + + const memo = "My first contract on chain"; + const msg1: MsgSend = { + type: "cosmos-sdk/MsgSend", + value: { + from_address: address1, + to_address: emptyAddress, + amount: [ + { + denom: "ucosm", + amount: "1234567", + }, + ], + }, + }; + const msg2: MsgSend = { + type: "cosmos-sdk/MsgSend", + value: { + from_address: address2, + to_address: emptyAddress, + amount: [ + { + denom: "ucosm", + amount: "7654321", + }, + ], + }, + }; + + const fee: StdFee = { + amount: [ + { + amount: "5000", + denom: "ucosm", + }, + ], + gas: "890000", + }; + + const client = new LcdClient(wasmd.endpoint); + const { account_number: an1, sequence: sequence1 } = (await client.authAccounts(address1)).result.value; + const { account_number: an2, sequence: sequence2 } = (await client.authAccounts(address2)).result.value; + + const signBytes1 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1); + const signBytes2 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2); + const signature1 = await account1.sign(signBytes1); + const signature2 = await account2.sign(signBytes2); + const signedTx = { + msg: [msg2, msg1], + fee: fee, + memo: memo, + signatures: [signature2, signature1], + }; + const postResult = await client.postTx(signedTx); + expect(postResult.code).toBeUndefined(); + + await sleep(500); + const searched = await client.txsQuery(`tx.hash=${postResult.txhash}`); + expect(searched.txs.length).toEqual(1); + expect(searched.txs[0].tx.value.signatures).toEqual([signature2, signature1]); + }); + + it("can't send transaction with wrong signature order (1)", async () => { + pendingWithoutWasmd(); + const account1 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0)); + const account2 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1)); + const address1 = rawSecp256k1PubkeyToAddress(account1.pubkey, "cosmos"); + const address2 = rawSecp256k1PubkeyToAddress(account2.pubkey, "cosmos"); + + const memo = "My first contract on chain"; + const msg1: MsgSend = { + type: "cosmos-sdk/MsgSend", + value: { + from_address: address1, + to_address: emptyAddress, + amount: [ + { + denom: "ucosm", + amount: "1234567", + }, + ], + }, + }; + const msg2: MsgSend = { + type: "cosmos-sdk/MsgSend", + value: { + from_address: address2, + to_address: emptyAddress, + amount: [ + { + denom: "ucosm", + amount: "7654321", + }, + ], + }, + }; + + const fee: StdFee = { + amount: [ + { + amount: "5000", + denom: "ucosm", + }, + ], + gas: "890000", + }; + + const client = new LcdClient(wasmd.endpoint); + const { account_number: an1, sequence: sequence1 } = (await client.authAccounts(address1)).result.value; + const { account_number: an2, sequence: sequence2 } = (await client.authAccounts(address2)).result.value; + + const signBytes1 = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, an1, sequence1); + const signBytes2 = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, an2, sequence2); + const signature1 = await account1.sign(signBytes1); + const signature2 = await account2.sign(signBytes2); + const signedTx = { + msg: [msg1, msg2], + fee: fee, + memo: memo, + signatures: [signature2, signature1], + }; + const postResult = await client.postTx(signedTx); + expect(postResult.code).toEqual(8); + }); + + it("can't send transaction with wrong signature order (2)", async () => { + pendingWithoutWasmd(); + const account1 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0)); + const account2 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1)); + const address1 = rawSecp256k1PubkeyToAddress(account1.pubkey, "cosmos"); + const address2 = rawSecp256k1PubkeyToAddress(account2.pubkey, "cosmos"); + + const memo = "My first contract on chain"; + const msg1: MsgSend = { + type: "cosmos-sdk/MsgSend", + value: { + from_address: address1, + to_address: emptyAddress, + amount: [ + { + denom: "ucosm", + amount: "1234567", + }, + ], + }, + }; + const msg2: MsgSend = { + type: "cosmos-sdk/MsgSend", + value: { + from_address: address2, + to_address: emptyAddress, + amount: [ + { + denom: "ucosm", + amount: "7654321", + }, + ], + }, + }; + + const fee: StdFee = { + amount: [ + { + amount: "5000", + denom: "ucosm", + }, + ], + gas: "890000", + }; + + const client = new LcdClient(wasmd.endpoint); + const { account_number: an1, sequence: sequence1 } = (await client.authAccounts(address1)).result.value; + const { account_number: an2, sequence: sequence2 } = (await client.authAccounts(address2)).result.value; + + const signBytes1 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1); + const signBytes2 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2); + const signature1 = await account1.sign(signBytes1); + const signature2 = await account2.sign(signBytes2); + const signedTx = { + msg: [msg2, msg1], + fee: fee, + memo: memo, + signatures: [signature1, signature2], + }; + const postResult = await client.postTx(signedTx); + expect(postResult.code).toEqual(8); + }); + }); +}); diff --git a/packages/sdk38/src/lcdclient.ts b/packages/sdk38/src/lcdclient.ts new file mode 100644 index 00000000..6fb8a86f --- /dev/null +++ b/packages/sdk38/src/lcdclient.ts @@ -0,0 +1,217 @@ +/* eslint-disable no-dupe-class-members */ +import { assert, isNonNullObject } from "@cosmjs/utils"; +import axios, { AxiosError, AxiosInstance } from "axios"; + +import { + AuthAccountsResponse, + BlockResponse, + BroadcastMode, + EncodeTxResponse, + NodeInfoResponse, + PostTxsResponse, + SearchTxsResponse, + TxsResponse, +} from "./restclient"; +import { CosmosSdkTx, StdTx } from "./types"; + +/** Unfortunately, Cosmos SDK encodes empty arrays as null */ +export type CosmosSdkArray = ReadonlyArray | null; + +export function normalizeArray(backend: CosmosSdkArray): ReadonlyArray { + return backend || []; +} + +type LcdClientModule = Record any>; + +type LcdClientModuleRegisterer = (base: LcdClient) => M; + +// We want to get message data from 500 errors +// https://stackoverflow.com/questions/56577124/how-to-handle-500-error-message-with-axios +// this should be chained to catch one error and throw a more informative one +function parseAxiosError(err: AxiosError): never { + // use the error message sent from server, not default 500 msg + if (err.response?.data) { + let errorText: string; + const data = err.response.data; + // expect { error: string }, but otherwise dump + if (data.error && typeof data.error === "string") { + errorText = data.error; + } else if (typeof data === "string") { + errorText = data; + } else { + errorText = JSON.stringify(data); + } + throw new Error(`${errorText} (HTTP ${err.response.status})`); + } else { + throw err; + } +} + +export class LcdClient { + /** Constructs an LCD client with 0 modules */ + public static withModules(apiUrl: string, broadcastMode: BroadcastMode): LcdClient; + + /** Constructs an LCD client with 1 modules */ + public static withModules( + apiUrl: string, + broadcastMode: BroadcastMode, + moduleA: LcdClientModuleRegisterer, + ): LcdClient & A; + + /** Constructs an LCD client with 2 modules */ + public static withModules( + apiUrl: string, + broadcastMode: BroadcastMode, + moduleA: LcdClientModuleRegisterer, + moduleB: LcdClientModuleRegisterer, + ): LcdClient & A & B; + + public static withModules( + apiUrl: string, + broadcastMode: BroadcastMode, + moduleA?: LcdClientModuleRegisterer, + moduleB?: LcdClientModuleRegisterer, + ): any { + const client = new LcdClient(apiUrl, broadcastMode); + + const modules = new Array(); + if (moduleA) modules.push(moduleA(client)); + if (moduleB) modules.push(moduleB(client)); + for (const module of modules) { + assert(isNonNullObject(module), `Module must be a non-null object`); + for (const key in module) { + assert(typeof key == "string", `Found non-string module key: ${key}`); + (client as any)[key] = module[key]; + } + } + + return client; + } + + private readonly client: AxiosInstance; + private readonly broadcastMode: BroadcastMode; + + /** + * Creates a new client to interact with a Cosmos SDK light client daemon. + * This class tries to be a direct mapping onto the API. Some basic decoding and normalizatin is done + * but things like caching are done at a higher level. + * + * When building apps, you should not need to use this class directly. If you do, this indicates a missing feature + * in higher level components. Feel free to raise an issue in this case. + * + * @param apiUrl The URL of a Cosmos SDK light client daemon API (sometimes called REST server or REST API) + * @param broadcastMode Defines at which point of the transaction processing the postTx method (i.e. transaction broadcasting) returns + */ + public constructor(apiUrl: string, broadcastMode = BroadcastMode.Block) { + const headers = { + post: { "Content-Type": "application/json" }, + }; + this.client = axios.create({ + baseURL: apiUrl, + headers: headers, + }); + this.broadcastMode = broadcastMode; + } + + public async get(path: string): Promise { + const { data } = await this.client.get(path).catch(parseAxiosError); + if (data === null) { + throw new Error("Received null response from server"); + } + return data; + } + + public async post(path: string, params: any): Promise { + if (!isNonNullObject(params)) throw new Error("Got unexpected type of params. Expected object."); + const { data } = await this.client.post(path, params).catch(parseAxiosError); + if (data === null) { + throw new Error("Received null response from server"); + } + return data; + } + + // The /auth endpoints + + public async authAccounts(address: string): Promise { + const path = `/auth/accounts/${address}`; + const responseData = await this.get(path); + if (responseData.result.type !== "cosmos-sdk/Account") { + throw new Error("Unexpected response data format"); + } + return responseData as AuthAccountsResponse; + } + + // The /blocks endpoints + + public async blocksLatest(): Promise { + const responseData = await this.get("/blocks/latest"); + if (!responseData.block) { + throw new Error("Unexpected response data format"); + } + return responseData as BlockResponse; + } + + public async blocks(height: number): Promise { + const responseData = await this.get(`/blocks/${height}`); + if (!responseData.block) { + throw new Error("Unexpected response data format"); + } + return responseData as BlockResponse; + } + + // The /node_info endpoint + + public async nodeInfo(): Promise { + const responseData = await this.get("/node_info"); + if (!responseData.node_info) { + throw new Error("Unexpected response data format"); + } + return responseData as NodeInfoResponse; + } + + // The /txs endpoints + + public async txById(id: string): Promise { + const responseData = await this.get(`/txs/${id}`); + if (!responseData.tx) { + throw new Error("Unexpected response data format"); + } + return responseData as TxsResponse; + } + + public async txsQuery(query: string): Promise { + const responseData = await this.get(`/txs?${query}`); + if (!responseData.txs) { + throw new Error("Unexpected response data format"); + } + return responseData as SearchTxsResponse; + } + + /** returns the amino-encoding of the transaction performed by the server */ + public async encodeTx(tx: CosmosSdkTx): Promise { + const responseData = await this.post("/txs/encode", tx); + if (!responseData.tx) { + throw new Error("Unexpected response data format"); + } + return responseData as EncodeTxResponse; + } + + /** + * Broadcasts a signed transaction to into the transaction pool. + * Depending on the RestClient's broadcast mode, this might or might + * wait for checkTx or deliverTx to be executed before returning. + * + * @param tx a signed transaction as StdTx (i.e. not wrapped in type/value container) + */ + public async postTx(tx: StdTx): Promise { + const params = { + tx: tx, + mode: this.broadcastMode, + }; + const responseData = await this.post("/txs", params); + if (!responseData.txhash) { + throw new Error("Unexpected response data format"); + } + return responseData as PostTxsResponse; + } +} diff --git a/packages/sdk38/types/lcdclient.d.ts b/packages/sdk38/types/lcdclient.d.ts new file mode 100644 index 00000000..5003d833 --- /dev/null +++ b/packages/sdk38/types/lcdclient.d.ts @@ -0,0 +1,66 @@ +import { + AuthAccountsResponse, + BlockResponse, + BroadcastMode, + EncodeTxResponse, + NodeInfoResponse, + PostTxsResponse, + SearchTxsResponse, + TxsResponse, +} from "./restclient"; +import { CosmosSdkTx, StdTx } from "./types"; +/** Unfortunately, Cosmos SDK encodes empty arrays as null */ +export declare type CosmosSdkArray = ReadonlyArray | null; +export declare function normalizeArray(backend: CosmosSdkArray): ReadonlyArray; +declare type LcdClientModule = Record any>; +declare type LcdClientModuleRegisterer = (base: LcdClient) => M; +export declare class LcdClient { + /** Constructs an LCD client with 0 modules */ + static withModules(apiUrl: string, broadcastMode: BroadcastMode): LcdClient; + /** Constructs an LCD client with 1 modules */ + static withModules( + apiUrl: string, + broadcastMode: BroadcastMode, + moduleA: LcdClientModuleRegisterer, + ): LcdClient & A; + /** Constructs an LCD client with 2 modules */ + static withModules( + apiUrl: string, + broadcastMode: BroadcastMode, + moduleA: LcdClientModuleRegisterer, + moduleB: LcdClientModuleRegisterer, + ): LcdClient & A & B; + private readonly client; + private readonly broadcastMode; + /** + * Creates a new client to interact with a Cosmos SDK light client daemon. + * This class tries to be a direct mapping onto the API. Some basic decoding and normalizatin is done + * but things like caching are done at a higher level. + * + * When building apps, you should not need to use this class directly. If you do, this indicates a missing feature + * in higher level components. Feel free to raise an issue in this case. + * + * @param apiUrl The URL of a Cosmos SDK light client daemon API (sometimes called REST server or REST API) + * @param broadcastMode Defines at which point of the transaction processing the postTx method (i.e. transaction broadcasting) returns + */ + constructor(apiUrl: string, broadcastMode?: BroadcastMode); + get(path: string): Promise; + post(path: string, params: any): Promise; + authAccounts(address: string): Promise; + blocksLatest(): Promise; + blocks(height: number): Promise; + nodeInfo(): Promise; + txById(id: string): Promise; + txsQuery(query: string): Promise; + /** returns the amino-encoding of the transaction performed by the server */ + encodeTx(tx: CosmosSdkTx): Promise; + /** + * Broadcasts a signed transaction to into the transaction pool. + * Depending on the RestClient's broadcast mode, this might or might + * wait for checkTx or deliverTx to be executed before returning. + * + * @param tx a signed transaction as StdTx (i.e. not wrapped in type/value container) + */ + postTx(tx: StdTx): Promise; +} +export {}; From 434f0e6ef15841c93ec999c333500a0484a65ebe Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 07:12:31 +0200 Subject: [PATCH 03/37] Improve readability of defaultRecipientAddress in restclient.spec.ts --- packages/sdk38/src/restclient.spec.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/sdk38/src/restclient.spec.ts b/packages/sdk38/src/restclient.spec.ts index 207c3d98..ad17e4be 100644 --- a/packages/sdk38/src/restclient.spec.ts +++ b/packages/sdk38/src/restclient.spec.ts @@ -28,8 +28,6 @@ import { } from "./testutils.spec"; import { StdFee, StdSignature, StdTx } from "./types"; -const emptyAddress = "cosmos1ltkhnmdcqemmd2tkhnx7qx66tq7e0wykw2j85k"; - function makeSignedTx(firstMsg: Msg, fee: StdFee, memo: string, firstSignature: StdSignature): StdTx { return { msg: [firstMsg], @@ -40,6 +38,8 @@ function makeSignedTx(firstMsg: Msg, fee: StdFee, memo: string, firstSignature: } describe("RestClient", () => { + const defaultRecipientAddress = makeRandomAddress(); + it("can be constructed", () => { const client = new RestClient(wasmd.endpoint); expect(client).toBeTruthy(); @@ -542,7 +542,7 @@ describe("RestClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: faucet.address, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -595,7 +595,7 @@ describe("RestClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address1, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -647,7 +647,7 @@ describe("RestClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address1, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -660,7 +660,7 @@ describe("RestClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address1, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -707,7 +707,7 @@ describe("RestClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address1, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -720,7 +720,7 @@ describe("RestClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address2, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -775,7 +775,7 @@ describe("RestClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address1, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -788,7 +788,7 @@ describe("RestClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address2, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -838,7 +838,7 @@ describe("RestClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address1, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -851,7 +851,7 @@ describe("RestClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address2, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", From 6645bbaad22ec86bae7db054b945b7ece88e83a3 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 07:14:41 +0200 Subject: [PATCH 04/37] Improve readability of defaultRecipientAddress in lcdclient.spec.ts --- packages/sdk38/src/lcdclient.spec.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/sdk38/src/lcdclient.spec.ts b/packages/sdk38/src/lcdclient.spec.ts index c0eee65e..93d7aab0 100644 --- a/packages/sdk38/src/lcdclient.spec.ts +++ b/packages/sdk38/src/lcdclient.spec.ts @@ -23,8 +23,6 @@ import { } from "./testutils.spec"; import { StdFee, StdSignature, StdTx } from "./types"; -const emptyAddress = "cosmos1ltkhnmdcqemmd2tkhnx7qx66tq7e0wykw2j85k"; - /** Deployed as part of scripts/wasmd/init.sh */ export const deployedErc20 = { codeId: 1, @@ -48,6 +46,8 @@ function makeSignedTx(firstMsg: Msg, fee: StdFee, memo: string, firstSignature: } describe("LcdClient", () => { + const defaultRecipientAddress = makeRandomAddress(); + it("can be constructed", () => { const client = new LcdClient(wasmd.endpoint); expect(client).toBeTruthy(); @@ -519,7 +519,7 @@ describe("LcdClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: faucet.address, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -572,7 +572,7 @@ describe("LcdClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address1, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -624,7 +624,7 @@ describe("LcdClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address1, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -637,7 +637,7 @@ describe("LcdClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address1, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -684,7 +684,7 @@ describe("LcdClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address1, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -697,7 +697,7 @@ describe("LcdClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address2, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -752,7 +752,7 @@ describe("LcdClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address1, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -765,7 +765,7 @@ describe("LcdClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address2, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -815,7 +815,7 @@ describe("LcdClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address1, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", @@ -828,7 +828,7 @@ describe("LcdClient", () => { type: "cosmos-sdk/MsgSend", value: { from_address: address2, - to_address: emptyAddress, + to_address: defaultRecipientAddress, amount: [ { denom: "ucosm", From 4d287019301cca5ba4d7838e349ee12eed1a0988 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 07:15:50 +0200 Subject: [PATCH 05/37] Rename type to LcdApiArray --- packages/sdk38/src/lcdclient.spec.ts | 8 ++++---- packages/sdk38/src/lcdclient.ts | 4 ++-- packages/sdk38/types/lcdclient.d.ts | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/sdk38/src/lcdclient.spec.ts b/packages/sdk38/src/lcdclient.spec.ts index 93d7aab0..a5f13cb9 100644 --- a/packages/sdk38/src/lcdclient.spec.ts +++ b/packages/sdk38/src/lcdclient.spec.ts @@ -5,7 +5,7 @@ import { rawSecp256k1PubkeyToAddress } from "./address"; import { Coin } from "./coins"; import { isPostTxFailure } from "./cosmosclient"; import { makeSignBytes } from "./encoding"; -import { CosmosSdkArray, LcdClient, normalizeArray } from "./lcdclient"; +import { LcdApiArray, LcdClient, normalizeArray } from "./lcdclient"; import { parseLogs } from "./logs"; import { Msg, MsgSend } from "./msgs"; import { makeCosmoshubPath, Secp256k1Pen } from "./pen"; @@ -104,7 +104,7 @@ describe("LcdClient", () => { return { listCodeInfo: async (): Promise => { const path = `/wasm/code`; - const responseData = (await base.get(path)) as WasmResponse>; + const responseData = (await base.get(path)) as WasmResponse>; return normalizeArray(unwrapWasmResponse(responseData)); }, }; @@ -125,7 +125,7 @@ describe("LcdClient", () => { return { listCodeInfo: async (): Promise => { const path = `/wasm/code`; - const responseData = (await base.get(path)) as WasmResponse>; + const responseData = (await base.get(path)) as WasmResponse>; return normalizeArray(unwrapWasmResponse(responseData)); }, }; @@ -133,7 +133,7 @@ describe("LcdClient", () => { interface TotalSupplyReponse { readonly height: string; - readonly result: CosmosSdkArray; + readonly result: LcdApiArray; } // eslint-disable-next-line @typescript-eslint/explicit-function-return-type diff --git a/packages/sdk38/src/lcdclient.ts b/packages/sdk38/src/lcdclient.ts index 6fb8a86f..c7a7e9b2 100644 --- a/packages/sdk38/src/lcdclient.ts +++ b/packages/sdk38/src/lcdclient.ts @@ -15,9 +15,9 @@ import { import { CosmosSdkTx, StdTx } from "./types"; /** Unfortunately, Cosmos SDK encodes empty arrays as null */ -export type CosmosSdkArray = ReadonlyArray | null; +export type LcdApiArray = ReadonlyArray | null; -export function normalizeArray(backend: CosmosSdkArray): ReadonlyArray { +export function normalizeArray(backend: LcdApiArray): ReadonlyArray { return backend || []; } diff --git a/packages/sdk38/types/lcdclient.d.ts b/packages/sdk38/types/lcdclient.d.ts index 5003d833..92459f03 100644 --- a/packages/sdk38/types/lcdclient.d.ts +++ b/packages/sdk38/types/lcdclient.d.ts @@ -10,8 +10,8 @@ import { } from "./restclient"; import { CosmosSdkTx, StdTx } from "./types"; /** Unfortunately, Cosmos SDK encodes empty arrays as null */ -export declare type CosmosSdkArray = ReadonlyArray | null; -export declare function normalizeArray(backend: CosmosSdkArray): ReadonlyArray; +export declare type LcdApiArray = ReadonlyArray | null; +export declare function normalizeArray(backend: LcdApiArray): ReadonlyArray; declare type LcdClientModule = Record any>; declare type LcdClientModuleRegisterer = (base: LcdClient) => M; export declare class LcdClient { From 855ce0bb4a3a312d328236494fd94c3237fdb52e Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 07:17:21 +0200 Subject: [PATCH 06/37] Pull out makeSignedTx --- packages/sdk38/src/lcdclient.spec.ts | 14 +++----------- packages/sdk38/src/restclient.spec.ts | 14 +++----------- packages/sdk38/src/testutils.spec.ts | 12 ++++++++++++ 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/packages/sdk38/src/lcdclient.spec.ts b/packages/sdk38/src/lcdclient.spec.ts index a5f13cb9..63b9f859 100644 --- a/packages/sdk38/src/lcdclient.spec.ts +++ b/packages/sdk38/src/lcdclient.spec.ts @@ -7,7 +7,7 @@ import { isPostTxFailure } from "./cosmosclient"; import { makeSignBytes } from "./encoding"; import { LcdApiArray, LcdClient, normalizeArray } from "./lcdclient"; import { parseLogs } from "./logs"; -import { Msg, MsgSend } from "./msgs"; +import { MsgSend } from "./msgs"; import { makeCosmoshubPath, Secp256k1Pen } from "./pen"; import { BroadcastMode, TxsResponse } from "./restclient"; import { SigningCosmosClient } from "./signingcosmosclient"; @@ -15,13 +15,14 @@ import cosmoshub from "./testdata/cosmoshub.json"; import { faucet, makeRandomAddress, + makeSignedTx, nonNegativeIntegerMatcher, pendingWithoutWasmd, tendermintIdMatcher, wasmd, wasmdEnabled, } from "./testutils.spec"; -import { StdFee, StdSignature, StdTx } from "./types"; +import { StdFee } from "./types"; /** Deployed as part of scripts/wasmd/init.sh */ export const deployedErc20 = { @@ -36,15 +37,6 @@ export const deployedErc20 = { ], }; -function makeSignedTx(firstMsg: Msg, fee: StdFee, memo: string, firstSignature: StdSignature): StdTx { - return { - msg: [firstMsg], - fee: fee, - memo: memo, - signatures: [firstSignature], - }; -} - describe("LcdClient", () => { const defaultRecipientAddress = makeRandomAddress(); diff --git a/packages/sdk38/src/restclient.spec.ts b/packages/sdk38/src/restclient.spec.ts index ad17e4be..bc6cffc3 100644 --- a/packages/sdk38/src/restclient.spec.ts +++ b/packages/sdk38/src/restclient.spec.ts @@ -6,7 +6,7 @@ import { rawSecp256k1PubkeyToAddress } from "./address"; import { isPostTxFailure } from "./cosmosclient"; import { makeSignBytes } from "./encoding"; import { parseLogs } from "./logs"; -import { Msg, MsgSend } from "./msgs"; +import { MsgSend } from "./msgs"; import { makeCosmoshubPath, Secp256k1Pen } from "./pen"; import { encodeBech32Pubkey } from "./pubkey"; import { RestClient, TxsResponse } from "./restclient"; @@ -15,6 +15,7 @@ import cosmoshub from "./testdata/cosmoshub.json"; import { faucet, makeRandomAddress, + makeSignedTx, nonNegativeIntegerMatcher, pendingWithoutWasmd, semverMatcher, @@ -26,16 +27,7 @@ import { wasmd, wasmdEnabled, } from "./testutils.spec"; -import { StdFee, StdSignature, StdTx } from "./types"; - -function makeSignedTx(firstMsg: Msg, fee: StdFee, memo: string, firstSignature: StdSignature): StdTx { - return { - msg: [firstMsg], - fee: fee, - memo: memo, - signatures: [firstSignature], - }; -} +import { StdFee } from "./types"; describe("RestClient", () => { const defaultRecipientAddress = makeRandomAddress(); diff --git a/packages/sdk38/src/testutils.spec.ts b/packages/sdk38/src/testutils.spec.ts index b04b1de5..e61c6927 100644 --- a/packages/sdk38/src/testutils.spec.ts +++ b/packages/sdk38/src/testutils.spec.ts @@ -1,6 +1,9 @@ import { Random } from "@cosmjs/crypto"; import { Bech32 } from "@cosmjs/encoding"; +import { Msg } from "./msgs"; +import { StdFee, StdSignature, StdTx } from "./types"; + export function makeRandomAddress(): string { return Bech32.encode("cosmos", Random.getBytes(20)); } @@ -56,3 +59,12 @@ export function fromOneElementArray(elements: ArrayLike): T { if (elements.length !== 1) throw new Error(`Expected exactly one element but got ${elements.length}`); return elements[0]; } + +export function makeSignedTx(firstMsg: Msg, fee: StdFee, memo: string, firstSignature: StdSignature): StdTx { + return { + msg: [firstMsg], + fee: fee, + memo: memo, + signatures: [firstSignature], + }; +} From acb140710c1f1e360d5a128bfa3488e246620f9e Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 07:23:01 +0200 Subject: [PATCH 07/37] Remove generic defaults form WasmResponse/WasmSuccess --- packages/sdk38/src/lcdclient.spec.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/sdk38/src/lcdclient.spec.ts b/packages/sdk38/src/lcdclient.spec.ts index 63b9f859..c9122426 100644 --- a/packages/sdk38/src/lcdclient.spec.ts +++ b/packages/sdk38/src/lcdclient.spec.ts @@ -56,12 +56,9 @@ describe("LcdClient", () => { readonly builder?: string; } - // Currently all wasm query responses return json-encoded strings... - // later deprecate this and use the specific types for result - // (assuming it is inlined, no second parse needed) - type WasmResponse = WasmSuccess | WasmError; + type WasmResponse = WasmSuccess | WasmError; - interface WasmSuccess { + interface WasmSuccess { readonly height: string; readonly result: T; } From dd5ef76a44be2fb033a7f6adb7f36c167ee4d04f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 07:34:35 +0200 Subject: [PATCH 08/37] Add more overloads to withModules --- packages/sdk38/src/lcdclient.ts | 151 +++++++++++++++++++++++++--- packages/sdk38/types/lcdclient.d.ts | 107 ++++++++++++++++++-- 2 files changed, 238 insertions(+), 20 deletions(-) diff --git a/packages/sdk38/src/lcdclient.ts b/packages/sdk38/src/lcdclient.ts index c7a7e9b2..fac9d6fa 100644 --- a/packages/sdk38/src/lcdclient.ts +++ b/packages/sdk38/src/lcdclient.ts @@ -21,9 +21,9 @@ export function normalizeArray(backend: LcdApiArray): ReadonlyArray { return backend || []; } -type LcdClientModule = Record any>; +type LcdModule = Record any>; -type LcdClientModuleRegisterer = (base: LcdClient) => M; +type LcdModuleSetup = (base: LcdClient) => M; // We want to get message data from 500 errors // https://stackoverflow.com/questions/56577124/how-to-handle-500-error-message-with-axios @@ -52,31 +52,156 @@ export class LcdClient { public static withModules(apiUrl: string, broadcastMode: BroadcastMode): LcdClient; /** Constructs an LCD client with 1 modules */ - public static withModules( + public static withModules( apiUrl: string, broadcastMode: BroadcastMode, - moduleA: LcdClientModuleRegisterer, + setupModuleA: LcdModuleSetup, ): LcdClient & A; /** Constructs an LCD client with 2 modules */ - public static withModules( + public static withModules( apiUrl: string, broadcastMode: BroadcastMode, - moduleA: LcdClientModuleRegisterer, - moduleB: LcdClientModuleRegisterer, + setupModuleA: LcdModuleSetup, + setupModuleB: LcdModuleSetup, ): LcdClient & A & B; - public static withModules( + /** Constructs an LCD client with 3 modules */ + public static withModules( apiUrl: string, broadcastMode: BroadcastMode, - moduleA?: LcdClientModuleRegisterer, - moduleB?: LcdClientModuleRegisterer, + setupModuleA: LcdModuleSetup, + setupModuleB: LcdModuleSetup, + setupModuleC: LcdModuleSetup, + ): LcdClient & A & B & C; + + /** Constructs an LCD client with 4 modules */ + public static withModules< + A extends LcdModule, + B extends LcdModule, + C extends LcdModule, + D extends LcdModule + >( + apiUrl: string, + broadcastMode: BroadcastMode, + setupModuleA: LcdModuleSetup, + setupModuleB: LcdModuleSetup, + setupModuleC: LcdModuleSetup, + setupModuleD: LcdModuleSetup, + ): LcdClient & A & B & C & D; + + /** Constructs an LCD client with 5 modules */ + public static withModules< + A extends LcdModule, + B extends LcdModule, + C extends LcdModule, + D extends LcdModule, + E extends LcdModule + >( + apiUrl: string, + broadcastMode: BroadcastMode, + setupModuleA: LcdModuleSetup, + setupModuleB: LcdModuleSetup, + setupModuleC: LcdModuleSetup, + setupModuleD: LcdModuleSetup, + setupModuleE: LcdModuleSetup, + ): LcdClient & A & B & C & D & E; + + /** Constructs an LCD client with 6 modules */ + public static withModules< + A extends LcdModule, + B extends LcdModule, + C extends LcdModule, + D extends LcdModule, + E extends LcdModule, + F extends LcdModule + >( + apiUrl: string, + broadcastMode: BroadcastMode, + setupModuleA: LcdModuleSetup, + setupModuleB: LcdModuleSetup, + setupModuleC: LcdModuleSetup, + setupModuleD: LcdModuleSetup, + setupModuleE: LcdModuleSetup, + setupModuleF: LcdModuleSetup, + ): LcdClient & A & B & C & D & E & F; + + /** Constructs an LCD client with 7 modules */ + public static withModules< + A extends LcdModule, + B extends LcdModule, + C extends LcdModule, + D extends LcdModule, + E extends LcdModule, + F extends LcdModule, + G extends LcdModule + >( + apiUrl: string, + broadcastMode: BroadcastMode, + setupModuleA: LcdModuleSetup, + setupModuleB: LcdModuleSetup, + setupModuleC: LcdModuleSetup, + setupModuleD: LcdModuleSetup, + setupModuleE: LcdModuleSetup, + setupModuleF: LcdModuleSetup, + setupModuleG: LcdModuleSetup, + ): LcdClient & A & B & C & D & E & F & G; + + /** Constructs an LCD client with 8 modules */ + public static withModules< + A extends LcdModule, + B extends LcdModule, + C extends LcdModule, + D extends LcdModule, + E extends LcdModule, + F extends LcdModule, + G extends LcdModule, + H extends LcdModule + >( + apiUrl: string, + broadcastMode: BroadcastMode, + setupModuleA: LcdModuleSetup, + setupModuleB: LcdModuleSetup, + setupModuleC: LcdModuleSetup, + setupModuleD: LcdModuleSetup, + setupModuleE: LcdModuleSetup, + setupModuleF: LcdModuleSetup, + setupModuleG: LcdModuleSetup, + setupModuleH: LcdModuleSetup, + ): LcdClient & A & B & C & D & E & F & G & H; + + public static withModules< + A extends LcdModule, + B extends LcdModule, + C extends LcdModule, + D extends LcdModule, + E extends LcdModule, + F extends LcdModule, + G extends LcdModule, + H extends LcdModule + >( + apiUrl: string, + broadcastMode: BroadcastMode, + setupModuleA?: LcdModuleSetup, + setupModuleB?: LcdModuleSetup, + setupModuleC?: LcdModuleSetup, + setupModuleD?: LcdModuleSetup, + setupModuleE?: LcdModuleSetup, + setupModuleF?: LcdModuleSetup, + setupModuleG?: LcdModuleSetup, + setupModuleH?: LcdModuleSetup, ): any { const client = new LcdClient(apiUrl, broadcastMode); - const modules = new Array(); - if (moduleA) modules.push(moduleA(client)); - if (moduleB) modules.push(moduleB(client)); + const modules = new Array(); + if (setupModuleA) modules.push(setupModuleA(client)); + if (setupModuleB) modules.push(setupModuleB(client)); + if (setupModuleC) modules.push(setupModuleC(client)); + if (setupModuleD) modules.push(setupModuleD(client)); + if (setupModuleE) modules.push(setupModuleE(client)); + if (setupModuleF) modules.push(setupModuleF(client)); + if (setupModuleG) modules.push(setupModuleG(client)); + if (setupModuleH) modules.push(setupModuleH(client)); for (const module of modules) { assert(isNonNullObject(module), `Module must be a non-null object`); for (const key in module) { diff --git a/packages/sdk38/types/lcdclient.d.ts b/packages/sdk38/types/lcdclient.d.ts index 92459f03..8f15abb2 100644 --- a/packages/sdk38/types/lcdclient.d.ts +++ b/packages/sdk38/types/lcdclient.d.ts @@ -12,24 +12,117 @@ import { CosmosSdkTx, StdTx } from "./types"; /** Unfortunately, Cosmos SDK encodes empty arrays as null */ export declare type LcdApiArray = ReadonlyArray | null; export declare function normalizeArray(backend: LcdApiArray): ReadonlyArray; -declare type LcdClientModule = Record any>; -declare type LcdClientModuleRegisterer = (base: LcdClient) => M; +declare type LcdModule = Record any>; +declare type LcdModuleSetup = (base: LcdClient) => M; export declare class LcdClient { /** Constructs an LCD client with 0 modules */ static withModules(apiUrl: string, broadcastMode: BroadcastMode): LcdClient; /** Constructs an LCD client with 1 modules */ - static withModules( + static withModules( apiUrl: string, broadcastMode: BroadcastMode, - moduleA: LcdClientModuleRegisterer, + setupModuleA: LcdModuleSetup, ): LcdClient & A; /** Constructs an LCD client with 2 modules */ - static withModules( + static withModules( apiUrl: string, broadcastMode: BroadcastMode, - moduleA: LcdClientModuleRegisterer, - moduleB: LcdClientModuleRegisterer, + setupModuleA: LcdModuleSetup, + setupModuleB: LcdModuleSetup, ): LcdClient & A & B; + /** Constructs an LCD client with 3 modules */ + static withModules( + apiUrl: string, + broadcastMode: BroadcastMode, + setupModuleA: LcdModuleSetup, + setupModuleB: LcdModuleSetup, + setupModuleC: LcdModuleSetup, + ): LcdClient & A & B & C; + /** Constructs an LCD client with 4 modules */ + static withModules( + apiUrl: string, + broadcastMode: BroadcastMode, + setupModuleA: LcdModuleSetup, + setupModuleB: LcdModuleSetup, + setupModuleC: LcdModuleSetup, + setupModuleD: LcdModuleSetup, + ): LcdClient & A & B & C & D; + /** Constructs an LCD client with 5 modules */ + static withModules< + A extends LcdModule, + B extends LcdModule, + C extends LcdModule, + D extends LcdModule, + E extends LcdModule + >( + apiUrl: string, + broadcastMode: BroadcastMode, + setupModuleA: LcdModuleSetup, + setupModuleB: LcdModuleSetup, + setupModuleC: LcdModuleSetup, + setupModuleD: LcdModuleSetup, + setupModuleE: LcdModuleSetup, + ): LcdClient & A & B & C & D & E; + /** Constructs an LCD client with 6 modules */ + static withModules< + A extends LcdModule, + B extends LcdModule, + C extends LcdModule, + D extends LcdModule, + E extends LcdModule, + F extends LcdModule + >( + apiUrl: string, + broadcastMode: BroadcastMode, + setupModuleA: LcdModuleSetup, + setupModuleB: LcdModuleSetup, + setupModuleC: LcdModuleSetup, + setupModuleD: LcdModuleSetup, + setupModuleE: LcdModuleSetup, + setupModuleF: LcdModuleSetup, + ): LcdClient & A & B & C & D & E & F; + /** Constructs an LCD client with 7 modules */ + static withModules< + A extends LcdModule, + B extends LcdModule, + C extends LcdModule, + D extends LcdModule, + E extends LcdModule, + F extends LcdModule, + G extends LcdModule + >( + apiUrl: string, + broadcastMode: BroadcastMode, + setupModuleA: LcdModuleSetup, + setupModuleB: LcdModuleSetup, + setupModuleC: LcdModuleSetup, + setupModuleD: LcdModuleSetup, + setupModuleE: LcdModuleSetup, + setupModuleF: LcdModuleSetup, + setupModuleG: LcdModuleSetup, + ): LcdClient & A & B & C & D & E & F & G; + /** Constructs an LCD client with 8 modules */ + static withModules< + A extends LcdModule, + B extends LcdModule, + C extends LcdModule, + D extends LcdModule, + E extends LcdModule, + F extends LcdModule, + G extends LcdModule, + H extends LcdModule + >( + apiUrl: string, + broadcastMode: BroadcastMode, + setupModuleA: LcdModuleSetup, + setupModuleB: LcdModuleSetup, + setupModuleC: LcdModuleSetup, + setupModuleD: LcdModuleSetup, + setupModuleE: LcdModuleSetup, + setupModuleF: LcdModuleSetup, + setupModuleG: LcdModuleSetup, + setupModuleH: LcdModuleSetup, + ): LcdClient & A & B & C & D & E & F & G & H; private readonly client; private readonly broadcastMode; /** From e6ac24a3018d7047e121232c4a51ac0e912bb11e Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 07:39:59 +0200 Subject: [PATCH 09/37] Create LcdClientBaseOptions --- packages/sdk38/src/lcdclient.spec.ts | 9 +++---- packages/sdk38/src/lcdclient.ts | 38 +++++++++++++--------------- packages/sdk38/types/lcdclient.d.ts | 32 ++++++++++------------- 3 files changed, 35 insertions(+), 44 deletions(-) diff --git a/packages/sdk38/src/lcdclient.spec.ts b/packages/sdk38/src/lcdclient.spec.ts index c9122426..bf6f5ebe 100644 --- a/packages/sdk38/src/lcdclient.spec.ts +++ b/packages/sdk38/src/lcdclient.spec.ts @@ -9,7 +9,7 @@ import { LcdApiArray, LcdClient, normalizeArray } from "./lcdclient"; import { parseLogs } from "./logs"; import { MsgSend } from "./msgs"; import { makeCosmoshubPath, Secp256k1Pen } from "./pen"; -import { BroadcastMode, TxsResponse } from "./restclient"; +import { TxsResponse } from "./restclient"; import { SigningCosmosClient } from "./signingcosmosclient"; import cosmoshub from "./testdata/cosmoshub.json"; import { @@ -83,7 +83,7 @@ describe("LcdClient", () => { } it("works for no modules", async () => { - const client = LcdClient.withModules(wasmd.endpoint, BroadcastMode.Sync); + const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }); expect(client).toBeTruthy(); }); @@ -99,7 +99,7 @@ describe("LcdClient", () => { }; } - const client = LcdClient.withModules(wasmd.endpoint, BroadcastMode.Sync, wasmClientRegisterer); + const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, wasmClientRegisterer); const codes = await client.listCodeInfo(); expect(codes.length).toBeGreaterThanOrEqual(3); expect(codes[0].id).toEqual(deployedErc20.codeId); @@ -136,8 +136,7 @@ describe("LcdClient", () => { } const client = LcdClient.withModules( - wasmd.endpoint, - BroadcastMode.Sync, + { apiUrl: wasmd.endpoint }, registerWasmModule, registerSupplyModule, ); diff --git a/packages/sdk38/src/lcdclient.ts b/packages/sdk38/src/lcdclient.ts index fac9d6fa..384e35ea 100644 --- a/packages/sdk38/src/lcdclient.ts +++ b/packages/sdk38/src/lcdclient.ts @@ -25,6 +25,11 @@ type LcdModule = Record any>; type LcdModuleSetup = (base: LcdClient) => M; +export interface LcdClientBaseOptions { + readonly apiUrl: string; + readonly broadcastMode?: BroadcastMode; +} + // We want to get message data from 500 errors // https://stackoverflow.com/questions/56577124/how-to-handle-500-error-message-with-axios // this should be chained to catch one error and throw a more informative one @@ -49,27 +54,24 @@ function parseAxiosError(err: AxiosError): never { export class LcdClient { /** Constructs an LCD client with 0 modules */ - public static withModules(apiUrl: string, broadcastMode: BroadcastMode): LcdClient; + public static withModules(options: LcdClientBaseOptions): LcdClient; - /** Constructs an LCD client with 1 modules */ + /** Constructs an LCD client with 1 module */ public static withModules( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA: LcdModuleSetup, ): LcdClient & A; /** Constructs an LCD client with 2 modules */ public static withModules( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA: LcdModuleSetup, setupModuleB: LcdModuleSetup, ): LcdClient & A & B; /** Constructs an LCD client with 3 modules */ public static withModules( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA: LcdModuleSetup, setupModuleB: LcdModuleSetup, setupModuleC: LcdModuleSetup, @@ -82,8 +84,7 @@ export class LcdClient { C extends LcdModule, D extends LcdModule >( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA: LcdModuleSetup, setupModuleB: LcdModuleSetup, setupModuleC: LcdModuleSetup, @@ -98,8 +99,7 @@ export class LcdClient { D extends LcdModule, E extends LcdModule >( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA: LcdModuleSetup, setupModuleB: LcdModuleSetup, setupModuleC: LcdModuleSetup, @@ -116,8 +116,7 @@ export class LcdClient { E extends LcdModule, F extends LcdModule >( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA: LcdModuleSetup, setupModuleB: LcdModuleSetup, setupModuleC: LcdModuleSetup, @@ -136,8 +135,7 @@ export class LcdClient { F extends LcdModule, G extends LcdModule >( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA: LcdModuleSetup, setupModuleB: LcdModuleSetup, setupModuleC: LcdModuleSetup, @@ -158,8 +156,7 @@ export class LcdClient { G extends LcdModule, H extends LcdModule >( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA: LcdModuleSetup, setupModuleB: LcdModuleSetup, setupModuleC: LcdModuleSetup, @@ -180,8 +177,7 @@ export class LcdClient { G extends LcdModule, H extends LcdModule >( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA?: LcdModuleSetup, setupModuleB?: LcdModuleSetup, setupModuleC?: LcdModuleSetup, @@ -191,7 +187,7 @@ export class LcdClient { setupModuleG?: LcdModuleSetup, setupModuleH?: LcdModuleSetup, ): any { - const client = new LcdClient(apiUrl, broadcastMode); + const client = new LcdClient(options.apiUrl, options.broadcastMode); const modules = new Array(); if (setupModuleA) modules.push(setupModuleA(client)); diff --git a/packages/sdk38/types/lcdclient.d.ts b/packages/sdk38/types/lcdclient.d.ts index 8f15abb2..6850ddd2 100644 --- a/packages/sdk38/types/lcdclient.d.ts +++ b/packages/sdk38/types/lcdclient.d.ts @@ -14,34 +14,34 @@ export declare type LcdApiArray = ReadonlyArray | null; export declare function normalizeArray(backend: LcdApiArray): ReadonlyArray; declare type LcdModule = Record any>; declare type LcdModuleSetup = (base: LcdClient) => M; +export interface LcdClientBaseOptions { + readonly apiUrl: string; + readonly broadcastMode?: BroadcastMode; +} export declare class LcdClient { /** Constructs an LCD client with 0 modules */ - static withModules(apiUrl: string, broadcastMode: BroadcastMode): LcdClient; - /** Constructs an LCD client with 1 modules */ + static withModules(options: LcdClientBaseOptions): LcdClient; + /** Constructs an LCD client with 1 module */ static withModules( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA: LcdModuleSetup, ): LcdClient & A; /** Constructs an LCD client with 2 modules */ static withModules( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA: LcdModuleSetup, setupModuleB: LcdModuleSetup, ): LcdClient & A & B; /** Constructs an LCD client with 3 modules */ static withModules( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA: LcdModuleSetup, setupModuleB: LcdModuleSetup, setupModuleC: LcdModuleSetup, ): LcdClient & A & B & C; /** Constructs an LCD client with 4 modules */ static withModules( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA: LcdModuleSetup, setupModuleB: LcdModuleSetup, setupModuleC: LcdModuleSetup, @@ -55,8 +55,7 @@ export declare class LcdClient { D extends LcdModule, E extends LcdModule >( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA: LcdModuleSetup, setupModuleB: LcdModuleSetup, setupModuleC: LcdModuleSetup, @@ -72,8 +71,7 @@ export declare class LcdClient { E extends LcdModule, F extends LcdModule >( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA: LcdModuleSetup, setupModuleB: LcdModuleSetup, setupModuleC: LcdModuleSetup, @@ -91,8 +89,7 @@ export declare class LcdClient { F extends LcdModule, G extends LcdModule >( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA: LcdModuleSetup, setupModuleB: LcdModuleSetup, setupModuleC: LcdModuleSetup, @@ -112,8 +109,7 @@ export declare class LcdClient { G extends LcdModule, H extends LcdModule >( - apiUrl: string, - broadcastMode: BroadcastMode, + options: LcdClientBaseOptions, setupModuleA: LcdModuleSetup, setupModuleB: LcdModuleSetup, setupModuleC: LcdModuleSetup, From 375d30ee5240c6714f153fa70b55a2e0bf4f2a27 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 07:52:16 +0200 Subject: [PATCH 10/37] Add doc comment to LcdClient --- packages/sdk38/src/lcdclient.ts | 12 ++++++++++++ packages/sdk38/types/lcdclient.d.ts | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/packages/sdk38/src/lcdclient.ts b/packages/sdk38/src/lcdclient.ts index 384e35ea..92613533 100644 --- a/packages/sdk38/src/lcdclient.ts +++ b/packages/sdk38/src/lcdclient.ts @@ -52,6 +52,18 @@ function parseAxiosError(err: AxiosError): never { } } +/** + * A client to the LCD's (light client daemon) API. + * This light client connects to Tendermint (i.e. the chain), encodes/decodes Amino data for us and provides a convenient JSON interface. + * + * This _JSON over HTTP_ API is sometimes referred to as "REST" or "RPC", which are both misleading terms + * for the same thing. + * + * Please note that the client to the LCD can not verify light client proofs. When using this, + * you need to trust the API provider as well as the network connection between client and API. + * + * @see https://cosmos.network/rpc + */ export class LcdClient { /** Constructs an LCD client with 0 modules */ public static withModules(options: LcdClientBaseOptions): LcdClient; diff --git a/packages/sdk38/types/lcdclient.d.ts b/packages/sdk38/types/lcdclient.d.ts index 6850ddd2..f044d509 100644 --- a/packages/sdk38/types/lcdclient.d.ts +++ b/packages/sdk38/types/lcdclient.d.ts @@ -18,6 +18,18 @@ export interface LcdClientBaseOptions { readonly apiUrl: string; readonly broadcastMode?: BroadcastMode; } +/** + * A client to the LCD's (light client daemon) API. + * This light client connects to Tendermint (i.e. the chain), encodes/decodes Amino data for us and provides a convenient JSON interface. + * + * This _JSON over HTTP_ API is sometimes referred to as "REST" or "RPC", which are both misleading terms + * for the same thing. + * + * Please note that the client to the LCD can not verify light client proofs. When using this, + * you need to trust the API provider as well as the network connection between client and API. + * + * @see https://cosmos.network/rpc + */ export declare class LcdClient { /** Constructs an LCD client with 0 modules */ static withModules(options: LcdClientBaseOptions): LcdClient; From 6d767cf5b1839ca00360008b91f1e050be32497e Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 09:09:01 +0200 Subject: [PATCH 11/37] Improve setupSupplyModule --- packages/sdk38/src/lcdclient.spec.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/sdk38/src/lcdclient.spec.ts b/packages/sdk38/src/lcdclient.spec.ts index bf6f5ebe..acaf33d0 100644 --- a/packages/sdk38/src/lcdclient.spec.ts +++ b/packages/sdk38/src/lcdclient.spec.ts @@ -120,33 +120,29 @@ describe("LcdClient", () => { }; } - interface TotalSupplyReponse { + interface TotalSupplyAllReponse { readonly height: string; readonly result: LcdApiArray; } // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - function registerSupplyModule(base: LcdClient) { + function setupSupplyModule(base: LcdClient) { return { - totalSupply: async (): Promise => { + totalSupplyAll: async (): Promise => { const path = `/supply/total`; - return (await base.get(path)) as TotalSupplyReponse; + return (await base.get(path)) as TotalSupplyAllReponse; }, }; } - const client = LcdClient.withModules( - { apiUrl: wasmd.endpoint }, - registerWasmModule, - registerSupplyModule, - ); + const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, registerWasmModule, setupSupplyModule); const codes = await client.listCodeInfo(); expect(codes.length).toBeGreaterThanOrEqual(3); expect(codes[0].id).toEqual(deployedErc20.codeId); expect(codes[0].data_hash).toEqual(deployedErc20.checksum.toUpperCase()); expect(codes[0].builder).toEqual(deployedErc20.builder); expect(codes[0].source).toEqual(deployedErc20.source); - const supply = await client.totalSupply(); + const supply = await client.totalSupplyAll(); expect(supply).toEqual({ height: jasmine.stringMatching(/^[0-9]+$/), result: [ From 467b29e45189afadcc83bc71c40431e7905e3652 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 09:15:18 +0200 Subject: [PATCH 12/37] Create proper supply module --- packages/sdk38/src/lcdapi/index.ts | 3 ++ packages/sdk38/src/lcdapi/supply.spec.ts | 40 ++++++++++++++++++++ packages/sdk38/src/lcdapi/supply.ts | 29 ++++++++++++++ packages/sdk38/src/lcdclient.ts | 2 +- packages/sdk38/types/lcdapi/index.d.ts | 1 + packages/sdk38/types/lcdapi/supply.d.ts | 16 ++++++++ packages/sdk38/types/lcdapi/supply.spec.d.ts | 1 + packages/sdk38/types/lcdclient.d.ts | 2 +- 8 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 packages/sdk38/src/lcdapi/index.ts create mode 100644 packages/sdk38/src/lcdapi/supply.spec.ts create mode 100644 packages/sdk38/src/lcdapi/supply.ts create mode 100644 packages/sdk38/types/lcdapi/index.d.ts create mode 100644 packages/sdk38/types/lcdapi/supply.d.ts create mode 100644 packages/sdk38/types/lcdapi/supply.spec.d.ts diff --git a/packages/sdk38/src/lcdapi/index.ts b/packages/sdk38/src/lcdapi/index.ts new file mode 100644 index 00000000..353d2fb3 --- /dev/null +++ b/packages/sdk38/src/lcdapi/index.ts @@ -0,0 +1,3 @@ +// See tracking issue https://github.com/CosmWasm/cosmjs/issues/276 for module support + +export { SupplyModule, TotalSupplyAllReponse, TotalSupplyReponse } from "./supply"; diff --git a/packages/sdk38/src/lcdapi/supply.spec.ts b/packages/sdk38/src/lcdapi/supply.spec.ts new file mode 100644 index 00000000..6ae295e7 --- /dev/null +++ b/packages/sdk38/src/lcdapi/supply.spec.ts @@ -0,0 +1,40 @@ +import { LcdClient } from "../lcdclient"; +import { pendingWithoutWasmd, wasmd } from "../testutils.spec"; +import { setupSupplyModule } from "./supply"; + +describe("supply", () => { + describe("totalSupplyAll", () => { + it("works", async () => { + pendingWithoutWasmd(); + + const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupSupplyModule); + const supply = await client.totalSupplyAll(); + expect(supply).toEqual({ + height: jasmine.stringMatching(/^[0-9]+$/), + result: [ + { + amount: jasmine.stringMatching(/^[0-9]+$/), + denom: "ucosm", + }, + { + amount: jasmine.stringMatching(/^[0-9]+$/), + denom: "ustake", + }, + ], + }); + }); + }); + + describe("totalSupply", () => { + it("works", async () => { + pendingWithoutWasmd(); + + const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupSupplyModule); + const supply = await client.totalSupply("ucosm"); + expect(supply).toEqual({ + height: jasmine.stringMatching(/^[0-9]+$/), + result: jasmine.stringMatching(/^[0-9]+$/), + }); + }); + }); +}); diff --git a/packages/sdk38/src/lcdapi/supply.ts b/packages/sdk38/src/lcdapi/supply.ts new file mode 100644 index 00000000..2063954f --- /dev/null +++ b/packages/sdk38/src/lcdapi/supply.ts @@ -0,0 +1,29 @@ +import { Coin } from "../coins"; +import { LcdApiArray, LcdClient, LcdModule } from "../lcdclient"; + +export interface TotalSupplyAllReponse { + readonly height: string; + readonly result: LcdApiArray; +} + +export interface TotalSupplyReponse { + readonly height: string; + /** The amount */ + readonly result: string; +} + +export interface SupplyModule extends LcdModule { + readonly totalSupplyAll: () => Promise; + readonly totalSupply: (denom: string) => Promise; +} + +export function setupSupplyModule(base: LcdClient): SupplyModule { + return { + totalSupplyAll: async () => { + return base.get(`/supply/total`); + }, + totalSupply: async (denom: string) => { + return base.get(`/supply/total/${denom}`); + }, + }; +} diff --git a/packages/sdk38/src/lcdclient.ts b/packages/sdk38/src/lcdclient.ts index 92613533..2a1581ba 100644 --- a/packages/sdk38/src/lcdclient.ts +++ b/packages/sdk38/src/lcdclient.ts @@ -21,7 +21,7 @@ export function normalizeArray(backend: LcdApiArray): ReadonlyArray { return backend || []; } -type LcdModule = Record any>; +export type LcdModule = Record any>; type LcdModuleSetup = (base: LcdClient) => M; diff --git a/packages/sdk38/types/lcdapi/index.d.ts b/packages/sdk38/types/lcdapi/index.d.ts new file mode 100644 index 00000000..0e8ba9fe --- /dev/null +++ b/packages/sdk38/types/lcdapi/index.d.ts @@ -0,0 +1 @@ +export { SupplyModule, TotalSupplyAllReponse, TotalSupplyReponse } from "./supply"; diff --git a/packages/sdk38/types/lcdapi/supply.d.ts b/packages/sdk38/types/lcdapi/supply.d.ts new file mode 100644 index 00000000..321869a9 --- /dev/null +++ b/packages/sdk38/types/lcdapi/supply.d.ts @@ -0,0 +1,16 @@ +import { Coin } from "../coins"; +import { LcdApiArray, LcdClient, LcdModule } from "../lcdclient"; +export interface TotalSupplyAllReponse { + readonly height: string; + readonly result: LcdApiArray; +} +export interface TotalSupplyReponse { + readonly height: string; + /** The amount */ + readonly result: string; +} +export interface SupplyModule extends LcdModule { + readonly totalSupplyAll: () => Promise; + readonly totalSupply: (denom: string) => Promise; +} +export declare function setupSupplyModule(base: LcdClient): SupplyModule; diff --git a/packages/sdk38/types/lcdapi/supply.spec.d.ts b/packages/sdk38/types/lcdapi/supply.spec.d.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/packages/sdk38/types/lcdapi/supply.spec.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/sdk38/types/lcdclient.d.ts b/packages/sdk38/types/lcdclient.d.ts index f044d509..f8c2519a 100644 --- a/packages/sdk38/types/lcdclient.d.ts +++ b/packages/sdk38/types/lcdclient.d.ts @@ -12,7 +12,7 @@ import { CosmosSdkTx, StdTx } from "./types"; /** Unfortunately, Cosmos SDK encodes empty arrays as null */ export declare type LcdApiArray = ReadonlyArray | null; export declare function normalizeArray(backend: LcdApiArray): ReadonlyArray; -declare type LcdModule = Record any>; +export declare type LcdModule = Record any>; declare type LcdModuleSetup = (base: LcdClient) => M; export interface LcdClientBaseOptions { readonly apiUrl: string; From ff7188b3b5849948037508decc2ebd601c9bc61f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 23:10:09 +0200 Subject: [PATCH 13/37] Move API types into lcdapi --- packages/sdk38/src/cosmosclient.ts | 3 +- packages/sdk38/src/index.ts | 4 +- packages/sdk38/src/lcdapi/index.ts | 173 +++++++++++++++++- packages/sdk38/src/lcdclient.spec.ts | 2 +- packages/sdk38/src/lcdclient.ts | 6 +- packages/sdk38/src/restclient.spec.ts | 3 +- packages/sdk38/src/restclient.ts | 173 +----------------- packages/sdk38/src/signingcosmosclient.ts | 2 +- packages/sdk38/types/cosmosclient.d.ts | 3 +- packages/sdk38/types/index.d.ts | 4 +- packages/sdk38/types/lcdapi/index.d.ts | 145 +++++++++++++++ packages/sdk38/types/lcdclient.d.ts | 2 +- packages/sdk38/types/restclient.d.ts | 155 +--------------- packages/sdk38/types/signingcosmosclient.d.ts | 2 +- 14 files changed, 354 insertions(+), 323 deletions(-) diff --git a/packages/sdk38/src/cosmosclient.ts b/packages/sdk38/src/cosmosclient.ts index 077e354c..890d2d8a 100644 --- a/packages/sdk38/src/cosmosclient.ts +++ b/packages/sdk38/src/cosmosclient.ts @@ -3,9 +3,10 @@ import { fromBase64, fromHex, toHex } from "@cosmjs/encoding"; import { Uint53 } from "@cosmjs/math"; import { Coin } from "./coins"; +import { BroadcastMode } from "./lcdapi"; import { Log, parseLogs } from "./logs"; import { decodeBech32Pubkey } from "./pubkey"; -import { BroadcastMode, RestClient } from "./restclient"; +import { RestClient } from "./restclient"; import { CosmosSdkTx, PubKey, StdTx } from "./types"; export interface GetNonceResult { diff --git a/packages/sdk38/src/index.ts b/packages/sdk38/src/index.ts index 7735fa63..b7bed9a0 100644 --- a/packages/sdk38/src/index.ts +++ b/packages/sdk38/src/index.ts @@ -27,10 +27,10 @@ export { EncodeTxResponse, PostTxsResponse, NodeInfoResponse, - RestClient, SearchTxsResponse, TxsResponse, -} from "./restclient"; +} from "./lcdapi"; +export { RestClient } from "./restclient"; export { isMsgDelegate, isMsgSend, Msg, MsgDelegate, MsgSend } from "./msgs"; export { Pen, Secp256k1Pen, makeCosmoshubPath } from "./pen"; export { decodeBech32Pubkey, encodeBech32Pubkey, encodeSecp256k1Pubkey } from "./pubkey"; diff --git a/packages/sdk38/src/lcdapi/index.ts b/packages/sdk38/src/lcdapi/index.ts index 353d2fb3..567e1517 100644 --- a/packages/sdk38/src/lcdapi/index.ts +++ b/packages/sdk38/src/lcdapi/index.ts @@ -1,3 +1,174 @@ -// See tracking issue https://github.com/CosmWasm/cosmjs/issues/276 for module support +import { Coin } from "../coins"; +import { CosmosSdkTx } from "../types"; + +// +// Standard modules (see tracking issue https://github.com/CosmWasm/cosmjs/issues/276) +// export { SupplyModule, TotalSupplyAllReponse, TotalSupplyReponse } from "./supply"; + +// +// Base types +// + +/** + * The mode used to send transaction + * + * @see https://cosmos.network/rpc/#/Transactions/post_txs + */ +export enum BroadcastMode { + /** Return after tx commit */ + Block = "block", + /** Return afer CheckTx */ + Sync = "sync", + /** Return right away */ + Async = "async", +} + +/** A reponse from the /txs/encode endpoint */ +export interface EncodeTxResponse { + /** base64-encoded amino-binary encoded representation */ + readonly tx: string; +} + +interface NodeInfo { + readonly protocol_version: { + readonly p2p: string; + readonly block: string; + readonly app: string; + }; + readonly id: string; + readonly listen_addr: string; + readonly network: string; + readonly version: string; + readonly channels: string; + readonly moniker: string; + readonly other: { + readonly tx_index: string; + readonly rpc_address: string; + }; +} + +interface ApplicationVersion { + readonly name: string; + readonly server_name: string; + readonly client_name: string; + readonly version: string; + readonly commit: string; + readonly build_tags: string; + readonly go: string; +} + +export interface NodeInfoResponse { + readonly node_info: NodeInfo; + readonly application_version: ApplicationVersion; +} + +interface BlockId { + readonly hash: string; + // TODO: here we also have this + // parts: { + // total: '1', + // hash: '7AF200C78FBF9236944E1AB270F4045CD60972B7C265E3A9DA42973397572931' + // } +} + +interface BlockHeader { + readonly version: { + readonly block: string; + readonly app: string; + }; + readonly height: string; + readonly chain_id: string; + /** An RFC 3339 time string like e.g. '2020-02-15T10:39:10.4696305Z' */ + readonly time: string; + readonly last_commit_hash: string; + readonly last_block_id: BlockId; + /** Can be empty */ + readonly data_hash: string; + readonly validators_hash: string; + readonly next_validators_hash: string; + readonly consensus_hash: string; + readonly app_hash: string; + /** Can be empty */ + readonly last_results_hash: string; + /** Can be empty */ + readonly evidence_hash: string; + readonly proposer_address: string; +} + +interface Block { + readonly header: BlockHeader; + readonly data: { + /** Array of base64 encoded transactions */ + readonly txs: readonly string[] | null; + }; +} + +export interface BlockResponse { + readonly block_id: BlockId; + readonly block: Block; +} + +export interface CosmosSdkAccount { + /** Bech32 account address */ + readonly address: string; + readonly coins: readonly Coin[]; + /** Bech32 encoded pubkey */ + readonly public_key: string; + readonly account_number: number; + readonly sequence: number; +} + +export interface AuthAccountsResponse { + readonly height: string; + readonly result: { + readonly type: "cosmos-sdk/Account"; + readonly value: CosmosSdkAccount; + }; +} + +export interface TxsResponse { + readonly height: string; + readonly txhash: string; + /** 🤷‍♂️ */ + readonly codespace?: string; + /** Falsy when transaction execution succeeded. Contains error code on error. */ + readonly code?: number; + readonly raw_log: string; + readonly logs?: object; + readonly tx: CosmosSdkTx; + /** The gas limit as set by the user */ + readonly gas_wanted?: string; + /** The gas used by the execution */ + readonly gas_used?: string; + readonly timestamp: string; +} + +export interface SearchTxsResponse { + readonly total_count: string; + readonly count: string; + readonly page_number: string; + readonly page_total: string; + readonly limit: string; + readonly txs: readonly TxsResponse[]; +} + +export interface PostTxsResponse { + readonly height: string; + readonly txhash: string; + readonly code?: number; + /** + * The result data of the execution (hex encoded). + * + * @see https://github.com/cosmos/cosmos-sdk/blob/v0.38.4/types/result.go#L101 + */ + readonly data?: string; + readonly raw_log?: string; + /** The same as `raw_log` but deserialized? */ + readonly logs?: object; + /** The gas limit as set by the user */ + readonly gas_wanted?: string; + /** The gas used by the execution */ + readonly gas_used?: string; +} diff --git a/packages/sdk38/src/lcdclient.spec.ts b/packages/sdk38/src/lcdclient.spec.ts index acaf33d0..e6b43eb8 100644 --- a/packages/sdk38/src/lcdclient.spec.ts +++ b/packages/sdk38/src/lcdclient.spec.ts @@ -5,11 +5,11 @@ import { rawSecp256k1PubkeyToAddress } from "./address"; import { Coin } from "./coins"; import { isPostTxFailure } from "./cosmosclient"; import { makeSignBytes } from "./encoding"; +import { TxsResponse } from "./lcdapi"; import { LcdApiArray, LcdClient, normalizeArray } from "./lcdclient"; import { parseLogs } from "./logs"; import { MsgSend } from "./msgs"; import { makeCosmoshubPath, Secp256k1Pen } from "./pen"; -import { TxsResponse } from "./restclient"; import { SigningCosmosClient } from "./signingcosmosclient"; import cosmoshub from "./testdata/cosmoshub.json"; import { diff --git a/packages/sdk38/src/lcdclient.ts b/packages/sdk38/src/lcdclient.ts index 2a1581ba..c15d2d8f 100644 --- a/packages/sdk38/src/lcdclient.ts +++ b/packages/sdk38/src/lcdclient.ts @@ -11,13 +11,13 @@ import { PostTxsResponse, SearchTxsResponse, TxsResponse, -} from "./restclient"; +} from "./lcdapi"; import { CosmosSdkTx, StdTx } from "./types"; /** Unfortunately, Cosmos SDK encodes empty arrays as null */ -export type LcdApiArray = ReadonlyArray | null; +export type LcdApiArray = readonly T[] | null; -export function normalizeArray(backend: LcdApiArray): ReadonlyArray { +export function normalizeArray(backend: LcdApiArray): readonly T[] { return backend || []; } diff --git a/packages/sdk38/src/restclient.spec.ts b/packages/sdk38/src/restclient.spec.ts index bc6cffc3..14d18f61 100644 --- a/packages/sdk38/src/restclient.spec.ts +++ b/packages/sdk38/src/restclient.spec.ts @@ -5,11 +5,12 @@ import { ReadonlyDate } from "readonly-date"; import { rawSecp256k1PubkeyToAddress } from "./address"; import { isPostTxFailure } from "./cosmosclient"; import { makeSignBytes } from "./encoding"; +import { TxsResponse } from "./lcdapi"; import { parseLogs } from "./logs"; import { MsgSend } from "./msgs"; import { makeCosmoshubPath, Secp256k1Pen } from "./pen"; import { encodeBech32Pubkey } from "./pubkey"; -import { RestClient, TxsResponse } from "./restclient"; +import { RestClient } from "./restclient"; import { SigningCosmosClient } from "./signingcosmosclient"; import cosmoshub from "./testdata/cosmoshub.json"; import { diff --git a/packages/sdk38/src/restclient.ts b/packages/sdk38/src/restclient.ts index 67edfb99..fdc572e6 100644 --- a/packages/sdk38/src/restclient.ts +++ b/packages/sdk38/src/restclient.ts @@ -1,171 +1,18 @@ import { isNonNullObject } from "@cosmjs/utils"; import axios, { AxiosError, AxiosInstance } from "axios"; -import { Coin } from "./coins"; +import { + AuthAccountsResponse, + BlockResponse, + BroadcastMode, + EncodeTxResponse, + NodeInfoResponse, + PostTxsResponse, + SearchTxsResponse, + TxsResponse, +} from "./lcdapi"; import { CosmosSdkTx, StdTx } from "./types"; -export interface CosmosSdkAccount { - /** Bech32 account address */ - readonly address: string; - readonly coins: readonly Coin[]; - /** Bech32 encoded pubkey */ - readonly public_key: string; - readonly account_number: number; - readonly sequence: number; -} - -interface NodeInfo { - readonly protocol_version: { - readonly p2p: string; - readonly block: string; - readonly app: string; - }; - readonly id: string; - readonly listen_addr: string; - readonly network: string; - readonly version: string; - readonly channels: string; - readonly moniker: string; - readonly other: { - readonly tx_index: string; - readonly rpc_address: string; - }; -} - -interface ApplicationVersion { - readonly name: string; - readonly server_name: string; - readonly client_name: string; - readonly version: string; - readonly commit: string; - readonly build_tags: string; - readonly go: string; -} - -export interface NodeInfoResponse { - readonly node_info: NodeInfo; - readonly application_version: ApplicationVersion; -} - -interface BlockId { - readonly hash: string; - // TODO: here we also have this - // parts: { - // total: '1', - // hash: '7AF200C78FBF9236944E1AB270F4045CD60972B7C265E3A9DA42973397572931' - // } -} - -interface BlockHeader { - readonly version: { - readonly block: string; - readonly app: string; - }; - readonly height: string; - readonly chain_id: string; - /** An RFC 3339 time string like e.g. '2020-02-15T10:39:10.4696305Z' */ - readonly time: string; - readonly last_commit_hash: string; - readonly last_block_id: BlockId; - /** Can be empty */ - readonly data_hash: string; - readonly validators_hash: string; - readonly next_validators_hash: string; - readonly consensus_hash: string; - readonly app_hash: string; - /** Can be empty */ - readonly last_results_hash: string; - /** Can be empty */ - readonly evidence_hash: string; - readonly proposer_address: string; -} - -interface Block { - readonly header: BlockHeader; - readonly data: { - /** Array of base64 encoded transactions */ - readonly txs: readonly string[] | null; - }; -} - -export interface BlockResponse { - readonly block_id: BlockId; - readonly block: Block; -} - -export interface AuthAccountsResponse { - readonly height: string; - readonly result: { - readonly type: "cosmos-sdk/Account"; - readonly value: CosmosSdkAccount; - }; -} - -export interface TxsResponse { - readonly height: string; - readonly txhash: string; - /** 🤷‍♂️ */ - readonly codespace?: string; - /** Falsy when transaction execution succeeded. Contains error code on error. */ - readonly code?: number; - readonly raw_log: string; - readonly logs?: object; - readonly tx: CosmosSdkTx; - /** The gas limit as set by the user */ - readonly gas_wanted?: string; - /** The gas used by the execution */ - readonly gas_used?: string; - readonly timestamp: string; -} - -export interface SearchTxsResponse { - readonly total_count: string; - readonly count: string; - readonly page_number: string; - readonly page_total: string; - readonly limit: string; - readonly txs: readonly TxsResponse[]; -} - -export interface PostTxsResponse { - readonly height: string; - readonly txhash: string; - readonly code?: number; - /** - * The result data of the execution (hex encoded). - * - * @see https://github.com/cosmos/cosmos-sdk/blob/v0.38.4/types/result.go#L101 - */ - readonly data?: string; - readonly raw_log?: string; - /** The same as `raw_log` but deserialized? */ - readonly logs?: object; - /** The gas limit as set by the user */ - readonly gas_wanted?: string; - /** The gas used by the execution */ - readonly gas_used?: string; -} - -/** A reponse from the /txs/encode endpoint */ -export interface EncodeTxResponse { - /** base64-encoded amino-binary encoded representation */ - readonly tx: string; -} - -/** - * The mode used to send transaction - * - * @see https://cosmos.network/rpc/#/Transactions/post_txs - */ -export enum BroadcastMode { - /** Return after tx commit */ - Block = "block", - /** Return afer CheckTx */ - Sync = "sync", - /** Return right away */ - Async = "async", -} - // We want to get message data from 500 errors // https://stackoverflow.com/questions/56577124/how-to-handle-500-error-message-with-axios // this should be chained to catch one error and throw a more informative one diff --git a/packages/sdk38/src/signingcosmosclient.ts b/packages/sdk38/src/signingcosmosclient.ts index 4768bf60..74f23fef 100644 --- a/packages/sdk38/src/signingcosmosclient.ts +++ b/packages/sdk38/src/signingcosmosclient.ts @@ -1,8 +1,8 @@ import { Coin, coins } from "./coins"; import { Account, CosmosClient, GetNonceResult, PostTxResult } from "./cosmosclient"; import { makeSignBytes } from "./encoding"; +import { BroadcastMode } from "./lcdapi"; import { MsgSend } from "./msgs"; -import { BroadcastMode } from "./restclient"; import { StdFee, StdSignature, StdTx } from "./types"; export interface SigningCallback { diff --git a/packages/sdk38/types/cosmosclient.d.ts b/packages/sdk38/types/cosmosclient.d.ts index b62fb2c6..e83f6059 100644 --- a/packages/sdk38/types/cosmosclient.d.ts +++ b/packages/sdk38/types/cosmosclient.d.ts @@ -1,6 +1,7 @@ import { Coin } from "./coins"; +import { BroadcastMode } from "./lcdapi"; import { Log } from "./logs"; -import { BroadcastMode, RestClient } from "./restclient"; +import { RestClient } from "./restclient"; import { CosmosSdkTx, PubKey, StdTx } from "./types"; export interface GetNonceResult { readonly accountNumber: number; diff --git a/packages/sdk38/types/index.d.ts b/packages/sdk38/types/index.d.ts index 633dc24b..c6bcbb3c 100644 --- a/packages/sdk38/types/index.d.ts +++ b/packages/sdk38/types/index.d.ts @@ -25,10 +25,10 @@ export { EncodeTxResponse, PostTxsResponse, NodeInfoResponse, - RestClient, SearchTxsResponse, TxsResponse, -} from "./restclient"; +} from "./lcdapi"; +export { RestClient } from "./restclient"; export { isMsgDelegate, isMsgSend, Msg, MsgDelegate, MsgSend } from "./msgs"; export { Pen, Secp256k1Pen, makeCosmoshubPath } from "./pen"; export { decodeBech32Pubkey, encodeBech32Pubkey, encodeSecp256k1Pubkey } from "./pubkey"; diff --git a/packages/sdk38/types/lcdapi/index.d.ts b/packages/sdk38/types/lcdapi/index.d.ts index 0e8ba9fe..7341b9bf 100644 --- a/packages/sdk38/types/lcdapi/index.d.ts +++ b/packages/sdk38/types/lcdapi/index.d.ts @@ -1 +1,146 @@ +import { Coin } from "../coins"; +import { CosmosSdkTx } from "../types"; export { SupplyModule, TotalSupplyAllReponse, TotalSupplyReponse } from "./supply"; +/** + * The mode used to send transaction + * + * @see https://cosmos.network/rpc/#/Transactions/post_txs + */ +export declare enum BroadcastMode { + /** Return after tx commit */ + Block = "block", + /** Return afer CheckTx */ + Sync = "sync", + /** Return right away */ + Async = "async", +} +/** A reponse from the /txs/encode endpoint */ +export interface EncodeTxResponse { + /** base64-encoded amino-binary encoded representation */ + readonly tx: string; +} +interface NodeInfo { + readonly protocol_version: { + readonly p2p: string; + readonly block: string; + readonly app: string; + }; + readonly id: string; + readonly listen_addr: string; + readonly network: string; + readonly version: string; + readonly channels: string; + readonly moniker: string; + readonly other: { + readonly tx_index: string; + readonly rpc_address: string; + }; +} +interface ApplicationVersion { + readonly name: string; + readonly server_name: string; + readonly client_name: string; + readonly version: string; + readonly commit: string; + readonly build_tags: string; + readonly go: string; +} +export interface NodeInfoResponse { + readonly node_info: NodeInfo; + readonly application_version: ApplicationVersion; +} +interface BlockId { + readonly hash: string; +} +interface BlockHeader { + readonly version: { + readonly block: string; + readonly app: string; + }; + readonly height: string; + readonly chain_id: string; + /** An RFC 3339 time string like e.g. '2020-02-15T10:39:10.4696305Z' */ + readonly time: string; + readonly last_commit_hash: string; + readonly last_block_id: BlockId; + /** Can be empty */ + readonly data_hash: string; + readonly validators_hash: string; + readonly next_validators_hash: string; + readonly consensus_hash: string; + readonly app_hash: string; + /** Can be empty */ + readonly last_results_hash: string; + /** Can be empty */ + readonly evidence_hash: string; + readonly proposer_address: string; +} +interface Block { + readonly header: BlockHeader; + readonly data: { + /** Array of base64 encoded transactions */ + readonly txs: ReadonlyArray | null; + }; +} +export interface BlockResponse { + readonly block_id: BlockId; + readonly block: Block; +} +export interface CosmosSdkAccount { + /** Bech32 account address */ + readonly address: string; + readonly coins: ReadonlyArray; + /** Bech32 encoded pubkey */ + readonly public_key: string; + readonly account_number: number; + readonly sequence: number; +} +export interface AuthAccountsResponse { + readonly height: string; + readonly result: { + readonly type: "cosmos-sdk/Account"; + readonly value: CosmosSdkAccount; + }; +} +export interface TxsResponse { + readonly height: string; + readonly txhash: string; + /** 🤷‍♂️ */ + readonly codespace?: string; + /** Falsy when transaction execution succeeded. Contains error code on error. */ + readonly code?: number; + readonly raw_log: string; + readonly logs?: object; + readonly tx: CosmosSdkTx; + /** The gas limit as set by the user */ + readonly gas_wanted?: string; + /** The gas used by the execution */ + readonly gas_used?: string; + readonly timestamp: string; +} +export interface SearchTxsResponse { + readonly total_count: string; + readonly count: string; + readonly page_number: string; + readonly page_total: string; + readonly limit: string; + readonly txs: readonly TxsResponse[]; +} +export interface PostTxsResponse { + readonly height: string; + readonly txhash: string; + readonly code?: number; + /** + * The result data of the execution (hex encoded). + * + * @see https://github.com/cosmos/cosmos-sdk/blob/v0.38.4/types/result.go#L101 + */ + readonly data?: string; + readonly raw_log?: string; + /** The same as `raw_log` but deserialized? */ + readonly logs?: object; + /** The gas limit as set by the user */ + readonly gas_wanted?: string; + /** The gas used by the execution */ + readonly gas_used?: string; +} diff --git a/packages/sdk38/types/lcdclient.d.ts b/packages/sdk38/types/lcdclient.d.ts index f8c2519a..349373ea 100644 --- a/packages/sdk38/types/lcdclient.d.ts +++ b/packages/sdk38/types/lcdclient.d.ts @@ -7,7 +7,7 @@ import { PostTxsResponse, SearchTxsResponse, TxsResponse, -} from "./restclient"; +} from "./lcdapi"; import { CosmosSdkTx, StdTx } from "./types"; /** Unfortunately, Cosmos SDK encodes empty arrays as null */ export declare type LcdApiArray = ReadonlyArray | null; diff --git a/packages/sdk38/types/restclient.d.ts b/packages/sdk38/types/restclient.d.ts index b1134d30..6ee4890c 100644 --- a/packages/sdk38/types/restclient.d.ts +++ b/packages/sdk38/types/restclient.d.ts @@ -1,148 +1,14 @@ -import { Coin } from "./coins"; +import { + AuthAccountsResponse, + BlockResponse, + BroadcastMode, + EncodeTxResponse, + NodeInfoResponse, + PostTxsResponse, + SearchTxsResponse, + TxsResponse, +} from "./lcdapi"; import { CosmosSdkTx, StdTx } from "./types"; -export interface CosmosSdkAccount { - /** Bech32 account address */ - readonly address: string; - readonly coins: readonly Coin[]; - /** Bech32 encoded pubkey */ - readonly public_key: string; - readonly account_number: number; - readonly sequence: number; -} -interface NodeInfo { - readonly protocol_version: { - readonly p2p: string; - readonly block: string; - readonly app: string; - }; - readonly id: string; - readonly listen_addr: string; - readonly network: string; - readonly version: string; - readonly channels: string; - readonly moniker: string; - readonly other: { - readonly tx_index: string; - readonly rpc_address: string; - }; -} -interface ApplicationVersion { - readonly name: string; - readonly server_name: string; - readonly client_name: string; - readonly version: string; - readonly commit: string; - readonly build_tags: string; - readonly go: string; -} -export interface NodeInfoResponse { - readonly node_info: NodeInfo; - readonly application_version: ApplicationVersion; -} -interface BlockId { - readonly hash: string; -} -interface BlockHeader { - readonly version: { - readonly block: string; - readonly app: string; - }; - readonly height: string; - readonly chain_id: string; - /** An RFC 3339 time string like e.g. '2020-02-15T10:39:10.4696305Z' */ - readonly time: string; - readonly last_commit_hash: string; - readonly last_block_id: BlockId; - /** Can be empty */ - readonly data_hash: string; - readonly validators_hash: string; - readonly next_validators_hash: string; - readonly consensus_hash: string; - readonly app_hash: string; - /** Can be empty */ - readonly last_results_hash: string; - /** Can be empty */ - readonly evidence_hash: string; - readonly proposer_address: string; -} -interface Block { - readonly header: BlockHeader; - readonly data: { - /** Array of base64 encoded transactions */ - readonly txs: readonly string[] | null; - }; -} -export interface BlockResponse { - readonly block_id: BlockId; - readonly block: Block; -} -export interface AuthAccountsResponse { - readonly height: string; - readonly result: { - readonly type: "cosmos-sdk/Account"; - readonly value: CosmosSdkAccount; - }; -} -export interface TxsResponse { - readonly height: string; - readonly txhash: string; - /** 🤷‍♂️ */ - readonly codespace?: string; - /** Falsy when transaction execution succeeded. Contains error code on error. */ - readonly code?: number; - readonly raw_log: string; - readonly logs?: object; - readonly tx: CosmosSdkTx; - /** The gas limit as set by the user */ - readonly gas_wanted?: string; - /** The gas used by the execution */ - readonly gas_used?: string; - readonly timestamp: string; -} -export interface SearchTxsResponse { - readonly total_count: string; - readonly count: string; - readonly page_number: string; - readonly page_total: string; - readonly limit: string; - readonly txs: readonly TxsResponse[]; -} -export interface PostTxsResponse { - readonly height: string; - readonly txhash: string; - readonly code?: number; - /** - * The result data of the execution (hex encoded). - * - * @see https://github.com/cosmos/cosmos-sdk/blob/v0.38.4/types/result.go#L101 - */ - readonly data?: string; - readonly raw_log?: string; - /** The same as `raw_log` but deserialized? */ - readonly logs?: object; - /** The gas limit as set by the user */ - readonly gas_wanted?: string; - /** The gas used by the execution */ - readonly gas_used?: string; -} -/** A reponse from the /txs/encode endpoint */ -export interface EncodeTxResponse { - /** base64-encoded amino-binary encoded representation */ - readonly tx: string; -} -/** - * The mode used to send transaction - * - * @see https://cosmos.network/rpc/#/Transactions/post_txs - */ -export declare enum BroadcastMode { - /** Return after tx commit */ - Block = "block", - /** Return afer CheckTx */ - Sync = "sync", - /** Return right away */ - Async = "async", -} export declare class RestClient { private readonly client; private readonly broadcastMode; @@ -177,4 +43,3 @@ export declare class RestClient { */ postTx(tx: StdTx): Promise; } -export {}; diff --git a/packages/sdk38/types/signingcosmosclient.d.ts b/packages/sdk38/types/signingcosmosclient.d.ts index d9ac523a..a733b8f6 100644 --- a/packages/sdk38/types/signingcosmosclient.d.ts +++ b/packages/sdk38/types/signingcosmosclient.d.ts @@ -1,6 +1,6 @@ import { Coin } from "./coins"; import { Account, CosmosClient, GetNonceResult, PostTxResult } from "./cosmosclient"; -import { BroadcastMode } from "./restclient"; +import { BroadcastMode } from "./lcdapi"; import { StdFee, StdSignature } from "./types"; export interface SigningCallback { (signBytes: Uint8Array): Promise; From a42dbd2a46d45db4400cc5b3ab588e6d5ab1e32a Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 23:16:21 +0200 Subject: [PATCH 14/37] Move all LCD API types to ./lcdapi --- packages/sdk38/src/lcdapi/base.ts | 164 +++++++++++++++++ packages/sdk38/src/lcdapi/index.ts | 174 +----------------- .../sdk38/src/{ => lcdapi}/lcdclient.spec.ts | 26 +-- packages/sdk38/src/{ => lcdapi}/lcdclient.ts | 4 +- packages/sdk38/src/lcdapi/supply.spec.ts | 2 +- packages/sdk38/src/lcdapi/supply.ts | 2 +- packages/sdk38/types/lcdapi/base.d.ts | 146 +++++++++++++++ packages/sdk38/types/lcdapi/index.d.ts | 155 +--------------- .../sdk38/types/{ => lcdapi}/lcdclient.d.ts | 8 +- .../sdk38/types/lcdapi/lcdclient.spec.d.ts | 8 + packages/sdk38/types/lcdapi/supply.d.ts | 2 +- 11 files changed, 360 insertions(+), 331 deletions(-) create mode 100644 packages/sdk38/src/lcdapi/base.ts rename packages/sdk38/src/{ => lcdapi}/lcdclient.spec.ts (98%) rename packages/sdk38/src/{ => lcdapi}/lcdclient.ts (99%) create mode 100644 packages/sdk38/types/lcdapi/base.d.ts rename packages/sdk38/types/{ => lcdapi}/lcdclient.d.ts (97%) create mode 100644 packages/sdk38/types/lcdapi/lcdclient.spec.d.ts diff --git a/packages/sdk38/src/lcdapi/base.ts b/packages/sdk38/src/lcdapi/base.ts new file mode 100644 index 00000000..bcf457cf --- /dev/null +++ b/packages/sdk38/src/lcdapi/base.ts @@ -0,0 +1,164 @@ +import { Coin } from "../coins"; +import { CosmosSdkTx } from "../types"; + +/** + * The mode used to send transaction + * + * @see https://cosmos.network/rpc/#/Transactions/post_txs + */ +export enum BroadcastMode { + /** Return after tx commit */ + Block = "block", + /** Return afer CheckTx */ + Sync = "sync", + /** Return right away */ + Async = "async", +} + +/** A reponse from the /txs/encode endpoint */ +export interface EncodeTxResponse { + /** base64-encoded amino-binary encoded representation */ + readonly tx: string; +} + +interface NodeInfo { + readonly protocol_version: { + readonly p2p: string; + readonly block: string; + readonly app: string; + }; + readonly id: string; + readonly listen_addr: string; + readonly network: string; + readonly version: string; + readonly channels: string; + readonly moniker: string; + readonly other: { + readonly tx_index: string; + readonly rpc_address: string; + }; +} + +interface ApplicationVersion { + readonly name: string; + readonly server_name: string; + readonly client_name: string; + readonly version: string; + readonly commit: string; + readonly build_tags: string; + readonly go: string; +} + +export interface NodeInfoResponse { + readonly node_info: NodeInfo; + readonly application_version: ApplicationVersion; +} + +interface BlockId { + readonly hash: string; + // TODO: here we also have this + // parts: { + // total: '1', + // hash: '7AF200C78FBF9236944E1AB270F4045CD60972B7C265E3A9DA42973397572931' + // } +} + +interface BlockHeader { + readonly version: { + readonly block: string; + readonly app: string; + }; + readonly height: string; + readonly chain_id: string; + /** An RFC 3339 time string like e.g. '2020-02-15T10:39:10.4696305Z' */ + readonly time: string; + readonly last_commit_hash: string; + readonly last_block_id: BlockId; + /** Can be empty */ + readonly data_hash: string; + readonly validators_hash: string; + readonly next_validators_hash: string; + readonly consensus_hash: string; + readonly app_hash: string; + /** Can be empty */ + readonly last_results_hash: string; + /** Can be empty */ + readonly evidence_hash: string; + readonly proposer_address: string; +} + +interface Block { + readonly header: BlockHeader; + readonly data: { + /** Array of base64 encoded transactions */ + readonly txs: readonly string[] | null; + }; +} + +export interface BlockResponse { + readonly block_id: BlockId; + readonly block: Block; +} + +export interface CosmosSdkAccount { + /** Bech32 account address */ + readonly address: string; + readonly coins: readonly Coin[]; + /** Bech32 encoded pubkey */ + readonly public_key: string; + readonly account_number: number; + readonly sequence: number; +} + +export interface AuthAccountsResponse { + readonly height: string; + readonly result: { + readonly type: "cosmos-sdk/Account"; + readonly value: CosmosSdkAccount; + }; +} + +export interface TxsResponse { + readonly height: string; + readonly txhash: string; + /** 🤷‍♂️ */ + readonly codespace?: string; + /** Falsy when transaction execution succeeded. Contains error code on error. */ + readonly code?: number; + readonly raw_log: string; + readonly logs?: object; + readonly tx: CosmosSdkTx; + /** The gas limit as set by the user */ + readonly gas_wanted?: string; + /** The gas used by the execution */ + readonly gas_used?: string; + readonly timestamp: string; +} + +export interface SearchTxsResponse { + readonly total_count: string; + readonly count: string; + readonly page_number: string; + readonly page_total: string; + readonly limit: string; + readonly txs: readonly TxsResponse[]; +} + +export interface PostTxsResponse { + readonly height: string; + readonly txhash: string; + readonly code?: number; + /** + * The result data of the execution (hex encoded). + * + * @see https://github.com/cosmos/cosmos-sdk/blob/v0.38.4/types/result.go#L101 + */ + readonly data?: string; + readonly raw_log?: string; + /** The same as `raw_log` but deserialized? */ + readonly logs?: object; + /** The gas limit as set by the user */ + readonly gas_wanted?: string; + /** The gas used by the execution */ + readonly gas_used?: string; +} diff --git a/packages/sdk38/src/lcdapi/index.ts b/packages/sdk38/src/lcdapi/index.ts index 567e1517..59479bf3 100644 --- a/packages/sdk38/src/lcdapi/index.ts +++ b/packages/sdk38/src/lcdapi/index.ts @@ -1,6 +1,3 @@ -import { Coin } from "../coins"; -import { CosmosSdkTx } from "../types"; - // // Standard modules (see tracking issue https://github.com/CosmWasm/cosmjs/issues/276) // @@ -11,164 +8,13 @@ export { SupplyModule, TotalSupplyAllReponse, TotalSupplyReponse } from "./suppl // Base types // -/** - * The mode used to send transaction - * - * @see https://cosmos.network/rpc/#/Transactions/post_txs - */ -export enum BroadcastMode { - /** Return after tx commit */ - Block = "block", - /** Return afer CheckTx */ - Sync = "sync", - /** Return right away */ - Async = "async", -} - -/** A reponse from the /txs/encode endpoint */ -export interface EncodeTxResponse { - /** base64-encoded amino-binary encoded representation */ - readonly tx: string; -} - -interface NodeInfo { - readonly protocol_version: { - readonly p2p: string; - readonly block: string; - readonly app: string; - }; - readonly id: string; - readonly listen_addr: string; - readonly network: string; - readonly version: string; - readonly channels: string; - readonly moniker: string; - readonly other: { - readonly tx_index: string; - readonly rpc_address: string; - }; -} - -interface ApplicationVersion { - readonly name: string; - readonly server_name: string; - readonly client_name: string; - readonly version: string; - readonly commit: string; - readonly build_tags: string; - readonly go: string; -} - -export interface NodeInfoResponse { - readonly node_info: NodeInfo; - readonly application_version: ApplicationVersion; -} - -interface BlockId { - readonly hash: string; - // TODO: here we also have this - // parts: { - // total: '1', - // hash: '7AF200C78FBF9236944E1AB270F4045CD60972B7C265E3A9DA42973397572931' - // } -} - -interface BlockHeader { - readonly version: { - readonly block: string; - readonly app: string; - }; - readonly height: string; - readonly chain_id: string; - /** An RFC 3339 time string like e.g. '2020-02-15T10:39:10.4696305Z' */ - readonly time: string; - readonly last_commit_hash: string; - readonly last_block_id: BlockId; - /** Can be empty */ - readonly data_hash: string; - readonly validators_hash: string; - readonly next_validators_hash: string; - readonly consensus_hash: string; - readonly app_hash: string; - /** Can be empty */ - readonly last_results_hash: string; - /** Can be empty */ - readonly evidence_hash: string; - readonly proposer_address: string; -} - -interface Block { - readonly header: BlockHeader; - readonly data: { - /** Array of base64 encoded transactions */ - readonly txs: readonly string[] | null; - }; -} - -export interface BlockResponse { - readonly block_id: BlockId; - readonly block: Block; -} - -export interface CosmosSdkAccount { - /** Bech32 account address */ - readonly address: string; - readonly coins: readonly Coin[]; - /** Bech32 encoded pubkey */ - readonly public_key: string; - readonly account_number: number; - readonly sequence: number; -} - -export interface AuthAccountsResponse { - readonly height: string; - readonly result: { - readonly type: "cosmos-sdk/Account"; - readonly value: CosmosSdkAccount; - }; -} - -export interface TxsResponse { - readonly height: string; - readonly txhash: string; - /** 🤷‍♂️ */ - readonly codespace?: string; - /** Falsy when transaction execution succeeded. Contains error code on error. */ - readonly code?: number; - readonly raw_log: string; - readonly logs?: object; - readonly tx: CosmosSdkTx; - /** The gas limit as set by the user */ - readonly gas_wanted?: string; - /** The gas used by the execution */ - readonly gas_used?: string; - readonly timestamp: string; -} - -export interface SearchTxsResponse { - readonly total_count: string; - readonly count: string; - readonly page_number: string; - readonly page_total: string; - readonly limit: string; - readonly txs: readonly TxsResponse[]; -} - -export interface PostTxsResponse { - readonly height: string; - readonly txhash: string; - readonly code?: number; - /** - * The result data of the execution (hex encoded). - * - * @see https://github.com/cosmos/cosmos-sdk/blob/v0.38.4/types/result.go#L101 - */ - readonly data?: string; - readonly raw_log?: string; - /** The same as `raw_log` but deserialized? */ - readonly logs?: object; - /** The gas limit as set by the user */ - readonly gas_wanted?: string; - /** The gas used by the execution */ - readonly gas_used?: string; -} +export { + AuthAccountsResponse, + BlockResponse, + BroadcastMode, + EncodeTxResponse, + PostTxsResponse, + NodeInfoResponse, + SearchTxsResponse, + TxsResponse, +} from "./base"; diff --git a/packages/sdk38/src/lcdclient.spec.ts b/packages/sdk38/src/lcdapi/lcdclient.spec.ts similarity index 98% rename from packages/sdk38/src/lcdclient.spec.ts rename to packages/sdk38/src/lcdapi/lcdclient.spec.ts index e6b43eb8..e80ee2c0 100644 --- a/packages/sdk38/src/lcdclient.spec.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.spec.ts @@ -1,17 +1,15 @@ /* eslint-disable @typescript-eslint/camelcase */ import { assert, sleep } from "@cosmjs/utils"; -import { rawSecp256k1PubkeyToAddress } from "./address"; -import { Coin } from "./coins"; -import { isPostTxFailure } from "./cosmosclient"; -import { makeSignBytes } from "./encoding"; -import { TxsResponse } from "./lcdapi"; -import { LcdApiArray, LcdClient, normalizeArray } from "./lcdclient"; -import { parseLogs } from "./logs"; -import { MsgSend } from "./msgs"; -import { makeCosmoshubPath, Secp256k1Pen } from "./pen"; -import { SigningCosmosClient } from "./signingcosmosclient"; -import cosmoshub from "./testdata/cosmoshub.json"; +import { rawSecp256k1PubkeyToAddress } from "../address"; +import { Coin } from "../coins"; +import { isPostTxFailure } from "../cosmosclient"; +import { makeSignBytes } from "../encoding"; +import { parseLogs } from "../logs"; +import { MsgSend } from "../msgs"; +import { makeCosmoshubPath, Secp256k1Pen } from "../pen"; +import { SigningCosmosClient } from "../signingcosmosclient"; +import cosmoshub from "../testdata/cosmoshub.json"; import { faucet, makeRandomAddress, @@ -21,8 +19,10 @@ import { tendermintIdMatcher, wasmd, wasmdEnabled, -} from "./testutils.spec"; -import { StdFee } from "./types"; +} from "../testutils.spec"; +import { StdFee } from "../types"; +import { TxsResponse } from "./base"; +import { LcdApiArray, LcdClient, normalizeArray } from "./lcdclient"; /** Deployed as part of scripts/wasmd/init.sh */ export const deployedErc20 = { diff --git a/packages/sdk38/src/lcdclient.ts b/packages/sdk38/src/lcdapi/lcdclient.ts similarity index 99% rename from packages/sdk38/src/lcdclient.ts rename to packages/sdk38/src/lcdapi/lcdclient.ts index c15d2d8f..a2bb655e 100644 --- a/packages/sdk38/src/lcdclient.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.ts @@ -2,6 +2,7 @@ import { assert, isNonNullObject } from "@cosmjs/utils"; import axios, { AxiosError, AxiosInstance } from "axios"; +import { CosmosSdkTx, StdTx } from "../types"; import { AuthAccountsResponse, BlockResponse, @@ -11,8 +12,7 @@ import { PostTxsResponse, SearchTxsResponse, TxsResponse, -} from "./lcdapi"; -import { CosmosSdkTx, StdTx } from "./types"; +} from "./base"; /** Unfortunately, Cosmos SDK encodes empty arrays as null */ export type LcdApiArray = readonly T[] | null; diff --git a/packages/sdk38/src/lcdapi/supply.spec.ts b/packages/sdk38/src/lcdapi/supply.spec.ts index 6ae295e7..3d905fc9 100644 --- a/packages/sdk38/src/lcdapi/supply.spec.ts +++ b/packages/sdk38/src/lcdapi/supply.spec.ts @@ -1,5 +1,5 @@ -import { LcdClient } from "../lcdclient"; import { pendingWithoutWasmd, wasmd } from "../testutils.spec"; +import { LcdClient } from "./lcdclient"; import { setupSupplyModule } from "./supply"; describe("supply", () => { diff --git a/packages/sdk38/src/lcdapi/supply.ts b/packages/sdk38/src/lcdapi/supply.ts index 2063954f..081310ad 100644 --- a/packages/sdk38/src/lcdapi/supply.ts +++ b/packages/sdk38/src/lcdapi/supply.ts @@ -1,5 +1,5 @@ import { Coin } from "../coins"; -import { LcdApiArray, LcdClient, LcdModule } from "../lcdclient"; +import { LcdApiArray, LcdClient, LcdModule } from "./lcdclient"; export interface TotalSupplyAllReponse { readonly height: string; diff --git a/packages/sdk38/types/lcdapi/base.d.ts b/packages/sdk38/types/lcdapi/base.d.ts new file mode 100644 index 00000000..3f520738 --- /dev/null +++ b/packages/sdk38/types/lcdapi/base.d.ts @@ -0,0 +1,146 @@ +import { Coin } from "../coins"; +import { CosmosSdkTx } from "../types"; +/** + * The mode used to send transaction + * + * @see https://cosmos.network/rpc/#/Transactions/post_txs + */ +export declare enum BroadcastMode { + /** Return after tx commit */ + Block = "block", + /** Return afer CheckTx */ + Sync = "sync", + /** Return right away */ + Async = "async", +} +/** A reponse from the /txs/encode endpoint */ +export interface EncodeTxResponse { + /** base64-encoded amino-binary encoded representation */ + readonly tx: string; +} +interface NodeInfo { + readonly protocol_version: { + readonly p2p: string; + readonly block: string; + readonly app: string; + }; + readonly id: string; + readonly listen_addr: string; + readonly network: string; + readonly version: string; + readonly channels: string; + readonly moniker: string; + readonly other: { + readonly tx_index: string; + readonly rpc_address: string; + }; +} +interface ApplicationVersion { + readonly name: string; + readonly server_name: string; + readonly client_name: string; + readonly version: string; + readonly commit: string; + readonly build_tags: string; + readonly go: string; +} +export interface NodeInfoResponse { + readonly node_info: NodeInfo; + readonly application_version: ApplicationVersion; +} +interface BlockId { + readonly hash: string; +} +interface BlockHeader { + readonly version: { + readonly block: string; + readonly app: string; + }; + readonly height: string; + readonly chain_id: string; + /** An RFC 3339 time string like e.g. '2020-02-15T10:39:10.4696305Z' */ + readonly time: string; + readonly last_commit_hash: string; + readonly last_block_id: BlockId; + /** Can be empty */ + readonly data_hash: string; + readonly validators_hash: string; + readonly next_validators_hash: string; + readonly consensus_hash: string; + readonly app_hash: string; + /** Can be empty */ + readonly last_results_hash: string; + /** Can be empty */ + readonly evidence_hash: string; + readonly proposer_address: string; +} +interface Block { + readonly header: BlockHeader; + readonly data: { + /** Array of base64 encoded transactions */ + readonly txs: readonly string[] | null; + }; +} +export interface BlockResponse { + readonly block_id: BlockId; + readonly block: Block; +} +export interface CosmosSdkAccount { + /** Bech32 account address */ + readonly address: string; + readonly coins: readonly Coin[]; + /** Bech32 encoded pubkey */ + readonly public_key: string; + readonly account_number: number; + readonly sequence: number; +} +export interface AuthAccountsResponse { + readonly height: string; + readonly result: { + readonly type: "cosmos-sdk/Account"; + readonly value: CosmosSdkAccount; + }; +} +export interface TxsResponse { + readonly height: string; + readonly txhash: string; + /** 🤷‍♂️ */ + readonly codespace?: string; + /** Falsy when transaction execution succeeded. Contains error code on error. */ + readonly code?: number; + readonly raw_log: string; + readonly logs?: object; + readonly tx: CosmosSdkTx; + /** The gas limit as set by the user */ + readonly gas_wanted?: string; + /** The gas used by the execution */ + readonly gas_used?: string; + readonly timestamp: string; +} +export interface SearchTxsResponse { + readonly total_count: string; + readonly count: string; + readonly page_number: string; + readonly page_total: string; + readonly limit: string; + readonly txs: readonly TxsResponse[]; +} +export interface PostTxsResponse { + readonly height: string; + readonly txhash: string; + readonly code?: number; + /** + * The result data of the execution (hex encoded). + * + * @see https://github.com/cosmos/cosmos-sdk/blob/v0.38.4/types/result.go#L101 + */ + readonly data?: string; + readonly raw_log?: string; + /** The same as `raw_log` but deserialized? */ + readonly logs?: object; + /** The gas limit as set by the user */ + readonly gas_wanted?: string; + /** The gas used by the execution */ + readonly gas_used?: string; +} +export {}; diff --git a/packages/sdk38/types/lcdapi/index.d.ts b/packages/sdk38/types/lcdapi/index.d.ts index 7341b9bf..5c36adce 100644 --- a/packages/sdk38/types/lcdapi/index.d.ts +++ b/packages/sdk38/types/lcdapi/index.d.ts @@ -1,146 +1,11 @@ -import { Coin } from "../coins"; -import { CosmosSdkTx } from "../types"; export { SupplyModule, TotalSupplyAllReponse, TotalSupplyReponse } from "./supply"; -/** - * The mode used to send transaction - * - * @see https://cosmos.network/rpc/#/Transactions/post_txs - */ -export declare enum BroadcastMode { - /** Return after tx commit */ - Block = "block", - /** Return afer CheckTx */ - Sync = "sync", - /** Return right away */ - Async = "async", -} -/** A reponse from the /txs/encode endpoint */ -export interface EncodeTxResponse { - /** base64-encoded amino-binary encoded representation */ - readonly tx: string; -} -interface NodeInfo { - readonly protocol_version: { - readonly p2p: string; - readonly block: string; - readonly app: string; - }; - readonly id: string; - readonly listen_addr: string; - readonly network: string; - readonly version: string; - readonly channels: string; - readonly moniker: string; - readonly other: { - readonly tx_index: string; - readonly rpc_address: string; - }; -} -interface ApplicationVersion { - readonly name: string; - readonly server_name: string; - readonly client_name: string; - readonly version: string; - readonly commit: string; - readonly build_tags: string; - readonly go: string; -} -export interface NodeInfoResponse { - readonly node_info: NodeInfo; - readonly application_version: ApplicationVersion; -} -interface BlockId { - readonly hash: string; -} -interface BlockHeader { - readonly version: { - readonly block: string; - readonly app: string; - }; - readonly height: string; - readonly chain_id: string; - /** An RFC 3339 time string like e.g. '2020-02-15T10:39:10.4696305Z' */ - readonly time: string; - readonly last_commit_hash: string; - readonly last_block_id: BlockId; - /** Can be empty */ - readonly data_hash: string; - readonly validators_hash: string; - readonly next_validators_hash: string; - readonly consensus_hash: string; - readonly app_hash: string; - /** Can be empty */ - readonly last_results_hash: string; - /** Can be empty */ - readonly evidence_hash: string; - readonly proposer_address: string; -} -interface Block { - readonly header: BlockHeader; - readonly data: { - /** Array of base64 encoded transactions */ - readonly txs: ReadonlyArray | null; - }; -} -export interface BlockResponse { - readonly block_id: BlockId; - readonly block: Block; -} -export interface CosmosSdkAccount { - /** Bech32 account address */ - readonly address: string; - readonly coins: ReadonlyArray; - /** Bech32 encoded pubkey */ - readonly public_key: string; - readonly account_number: number; - readonly sequence: number; -} -export interface AuthAccountsResponse { - readonly height: string; - readonly result: { - readonly type: "cosmos-sdk/Account"; - readonly value: CosmosSdkAccount; - }; -} -export interface TxsResponse { - readonly height: string; - readonly txhash: string; - /** 🤷‍♂️ */ - readonly codespace?: string; - /** Falsy when transaction execution succeeded. Contains error code on error. */ - readonly code?: number; - readonly raw_log: string; - readonly logs?: object; - readonly tx: CosmosSdkTx; - /** The gas limit as set by the user */ - readonly gas_wanted?: string; - /** The gas used by the execution */ - readonly gas_used?: string; - readonly timestamp: string; -} -export interface SearchTxsResponse { - readonly total_count: string; - readonly count: string; - readonly page_number: string; - readonly page_total: string; - readonly limit: string; - readonly txs: readonly TxsResponse[]; -} -export interface PostTxsResponse { - readonly height: string; - readonly txhash: string; - readonly code?: number; - /** - * The result data of the execution (hex encoded). - * - * @see https://github.com/cosmos/cosmos-sdk/blob/v0.38.4/types/result.go#L101 - */ - readonly data?: string; - readonly raw_log?: string; - /** The same as `raw_log` but deserialized? */ - readonly logs?: object; - /** The gas limit as set by the user */ - readonly gas_wanted?: string; - /** The gas used by the execution */ - readonly gas_used?: string; -} +export { + AuthAccountsResponse, + BlockResponse, + BroadcastMode, + EncodeTxResponse, + PostTxsResponse, + NodeInfoResponse, + SearchTxsResponse, + TxsResponse, +} from "./base"; diff --git a/packages/sdk38/types/lcdclient.d.ts b/packages/sdk38/types/lcdapi/lcdclient.d.ts similarity index 97% rename from packages/sdk38/types/lcdclient.d.ts rename to packages/sdk38/types/lcdapi/lcdclient.d.ts index 349373ea..b0e6fe67 100644 --- a/packages/sdk38/types/lcdclient.d.ts +++ b/packages/sdk38/types/lcdapi/lcdclient.d.ts @@ -1,3 +1,4 @@ +import { CosmosSdkTx, StdTx } from "../types"; import { AuthAccountsResponse, BlockResponse, @@ -7,11 +8,10 @@ import { PostTxsResponse, SearchTxsResponse, TxsResponse, -} from "./lcdapi"; -import { CosmosSdkTx, StdTx } from "./types"; +} from "./base"; /** Unfortunately, Cosmos SDK encodes empty arrays as null */ -export declare type LcdApiArray = ReadonlyArray | null; -export declare function normalizeArray(backend: LcdApiArray): ReadonlyArray; +export declare type LcdApiArray = readonly T[] | null; +export declare function normalizeArray(backend: LcdApiArray): readonly T[]; export declare type LcdModule = Record any>; declare type LcdModuleSetup = (base: LcdClient) => M; export interface LcdClientBaseOptions { diff --git a/packages/sdk38/types/lcdapi/lcdclient.spec.d.ts b/packages/sdk38/types/lcdapi/lcdclient.spec.d.ts new file mode 100644 index 00000000..42096dd0 --- /dev/null +++ b/packages/sdk38/types/lcdapi/lcdclient.spec.d.ts @@ -0,0 +1,8 @@ +/** Deployed as part of scripts/wasmd/init.sh */ +export declare const deployedErc20: { + codeId: number; + source: string; + builder: string; + checksum: string; + instances: string[]; +}; diff --git a/packages/sdk38/types/lcdapi/supply.d.ts b/packages/sdk38/types/lcdapi/supply.d.ts index 321869a9..90186538 100644 --- a/packages/sdk38/types/lcdapi/supply.d.ts +++ b/packages/sdk38/types/lcdapi/supply.d.ts @@ -1,5 +1,5 @@ import { Coin } from "../coins"; -import { LcdApiArray, LcdClient, LcdModule } from "../lcdclient"; +import { LcdApiArray, LcdClient, LcdModule } from "./lcdclient"; export interface TotalSupplyAllReponse { readonly height: string; readonly result: LcdApiArray; From 1675f94f14c7f2229d39c002e06135501c8c7242 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 10:02:42 +0200 Subject: [PATCH 15/37] Deprecate RestClient --- packages/sdk38/src/restclient.ts | 3 +++ packages/sdk38/types/restclient.d.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/packages/sdk38/src/restclient.ts b/packages/sdk38/src/restclient.ts index fdc572e6..81d1d85f 100644 --- a/packages/sdk38/src/restclient.ts +++ b/packages/sdk38/src/restclient.ts @@ -35,6 +35,9 @@ function parseAxiosError(err: AxiosError): never { } } +/** + * @deprecated use LcdClient. + */ export class RestClient { private readonly client: AxiosInstance; private readonly broadcastMode: BroadcastMode; diff --git a/packages/sdk38/types/restclient.d.ts b/packages/sdk38/types/restclient.d.ts index 6ee4890c..641b04c7 100644 --- a/packages/sdk38/types/restclient.d.ts +++ b/packages/sdk38/types/restclient.d.ts @@ -9,6 +9,9 @@ import { TxsResponse, } from "./lcdapi"; import { CosmosSdkTx, StdTx } from "./types"; +/** + * @deprecated use LcdClient. + */ export declare class RestClient { private readonly client; private readonly broadcastMode; From 4c6b2cce3b5f9e6ea2bbf5b07c3e0c499efb9583 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 10:13:14 +0200 Subject: [PATCH 16/37] Use LcdClient in CosmosClient --- .../sdk38/src/cosmosclient.searchtx.spec.ts | 4 +-- packages/sdk38/src/cosmosclient.spec.ts | 8 +++--- packages/sdk38/src/cosmosclient.ts | 25 +++++++++---------- packages/sdk38/src/lcdapi/index.ts | 1 + .../sdk38/src/signingcosmosclient.spec.ts | 4 +-- packages/sdk38/types/cosmosclient.d.ts | 7 +++--- packages/sdk38/types/lcdapi/index.d.ts | 1 + 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/packages/sdk38/src/cosmosclient.searchtx.spec.ts b/packages/sdk38/src/cosmosclient.searchtx.spec.ts index 9c42f8fb..cb6ca4de 100644 --- a/packages/sdk38/src/cosmosclient.searchtx.spec.ts +++ b/packages/sdk38/src/cosmosclient.searchtx.spec.ts @@ -4,9 +4,9 @@ import { assert, sleep } from "@cosmjs/utils"; import { coins } from "./coins"; import { CosmosClient, isPostTxFailure } from "./cosmosclient"; import { makeSignBytes } from "./encoding"; +import { LcdClient } from "./lcdapi"; import { isMsgSend, MsgSend } from "./msgs"; import { Secp256k1Pen } from "./pen"; -import { RestClient } from "./restclient"; import { SigningCosmosClient } from "./signingcosmosclient"; import { faucet, @@ -86,7 +86,7 @@ describe("CosmosClient.searchTx", () => { const transferAmount = coins(1234567, "ucosm"); const result = await client.sendTokens(recipient, transferAmount); await sleep(75); // wait until tx is indexed - const txDetails = await new RestClient(wasmd.endpoint).txById(result.transactionHash); + const txDetails = await new LcdClient(wasmd.endpoint).txById(result.transactionHash); sendSuccessful = { sender: faucet.address, recipient: recipient, diff --git a/packages/sdk38/src/cosmosclient.spec.ts b/packages/sdk38/src/cosmosclient.spec.ts index cdea2316..d05ddd94 100644 --- a/packages/sdk38/src/cosmosclient.spec.ts +++ b/packages/sdk38/src/cosmosclient.spec.ts @@ -43,7 +43,7 @@ describe("CosmosClient", () => { pendingWithoutWasmd(); const client = new CosmosClient(wasmd.endpoint); const openedClient = (client as unknown) as PrivateCosmWasmClient; - const getCodeSpy = spyOn(openedClient.restClient, "nodeInfo").and.callThrough(); + const getCodeSpy = spyOn(openedClient.lcdClient, "nodeInfo").and.callThrough(); expect(await client.getChainId()).toEqual(wasmd.chainId); // from network expect(await client.getChainId()).toEqual(wasmd.chainId); // from cache @@ -57,7 +57,7 @@ describe("CosmosClient", () => { pendingWithoutWasmd(); const client = new CosmosClient(wasmd.endpoint); const openedClient = (client as unknown) as PrivateCosmWasmClient; - const blockLatestSpy = spyOn(openedClient.restClient, "blocksLatest").and.callThrough(); + const blockLatestSpy = spyOn(openedClient.lcdClient, "blocksLatest").and.callThrough(); const height1 = await client.getHeight(); expect(height1).toBeGreaterThan(0); @@ -74,8 +74,8 @@ describe("CosmosClient", () => { const client = new CosmosClient(wasmd.endpoint); const openedClient = (client as unknown) as PrivateCosmWasmClient; - const blockLatestSpy = spyOn(openedClient.restClient, "blocksLatest").and.callThrough(); - const authAccountsSpy = spyOn(openedClient.restClient, "authAccounts").and.callThrough(); + const blockLatestSpy = spyOn(openedClient.lcdClient, "blocksLatest").and.callThrough(); + const authAccountsSpy = spyOn(openedClient.lcdClient, "authAccounts").and.callThrough(); const height1 = await client.getHeight(); expect(height1).toBeGreaterThan(0); diff --git a/packages/sdk38/src/cosmosclient.ts b/packages/sdk38/src/cosmosclient.ts index 890d2d8a..5ab8b2b2 100644 --- a/packages/sdk38/src/cosmosclient.ts +++ b/packages/sdk38/src/cosmosclient.ts @@ -3,10 +3,9 @@ import { fromBase64, fromHex, toHex } from "@cosmjs/encoding"; import { Uint53 } from "@cosmjs/math"; import { Coin } from "./coins"; -import { BroadcastMode } from "./lcdapi"; +import { BroadcastMode, LcdClient } from "./lcdapi"; import { Log, parseLogs } from "./logs"; import { decodeBech32Pubkey } from "./pubkey"; -import { RestClient } from "./restclient"; import { CosmosSdkTx, PubKey, StdTx } from "./types"; export interface GetNonceResult { @@ -131,11 +130,11 @@ export interface Block { /** Use for testing only */ export interface PrivateCosmWasmClient { - readonly restClient: RestClient; + readonly lcdClient: LcdClient; } export class CosmosClient { - protected readonly restClient: RestClient; + protected readonly lcdClient: LcdClient; /** Any address the chain considers valid (valid bech32 with proper prefix) */ protected anyValidAddress: string | undefined; @@ -151,12 +150,12 @@ export class CosmosClient { * @param broadcastMode Defines at which point of the transaction processing the postTx method (i.e. transaction broadcasting) returns */ public constructor(apiUrl: string, broadcastMode = BroadcastMode.Block) { - this.restClient = new RestClient(apiUrl, broadcastMode); + this.lcdClient = new LcdClient(apiUrl, broadcastMode); } public async getChainId(): Promise { if (!this.chainId) { - const response = await this.restClient.nodeInfo(); + const response = await this.lcdClient.nodeInfo(); const chainId = response.node_info.network; if (!chainId) throw new Error("Chain ID must not be empty"); this.chainId = chainId; @@ -167,12 +166,12 @@ export class CosmosClient { public async getHeight(): Promise { if (this.anyValidAddress) { - const { height } = await this.restClient.authAccounts(this.anyValidAddress); + const { height } = await this.lcdClient.authAccounts(this.anyValidAddress); return parseInt(height, 10); } else { // Note: this gets inefficient when blocks contain a lot of transactions since it // requires downloading and deserializing all transactions in the block. - const latest = await this.restClient.blocksLatest(); + const latest = await this.lcdClient.blocksLatest(); return parseInt(latest.block.header.height, 10); } } @@ -182,7 +181,7 @@ export class CosmosClient { */ public async getIdentifier(tx: CosmosSdkTx): Promise { // We consult the REST API because we don't have a local amino encoder - const response = await this.restClient.encodeTx(tx); + const response = await this.lcdClient.encodeTx(tx); const hash = new Sha256(fromBase64(response.tx)).digest(); return toHex(hash).toUpperCase(); } @@ -208,7 +207,7 @@ export class CosmosClient { } public async getAccount(address: string): Promise { - const account = await this.restClient.authAccounts(address); + const account = await this.lcdClient.authAccounts(address); const value = account.result.value; if (value.address === "") { return undefined; @@ -231,7 +230,7 @@ export class CosmosClient { */ public async getBlock(height?: number): Promise { const response = - height !== undefined ? await this.restClient.blocks(height) : await this.restClient.blocksLatest(); + height !== undefined ? await this.lcdClient.blocks(height) : await this.lcdClient.blocksLatest(); return { id: response.block_id.hash, @@ -288,7 +287,7 @@ export class CosmosClient { } public async postTx(tx: StdTx): Promise { - const result = await this.restClient.postTx(tx); + const result = await this.lcdClient.postTx(tx); if (!result.txhash.match(/^([0-9A-F][0-9A-F])+$/)) { throw new Error("Received ill-formatted txhash. Must be non-empty upper-case hex"); } @@ -311,7 +310,7 @@ export class CosmosClient { private async txsQuery(query: string): Promise { // TODO: we need proper pagination support const limit = 100; - const result = await this.restClient.txsQuery(`${query}&limit=${limit}`); + const result = await this.lcdClient.txsQuery(`${query}&limit=${limit}`); const pages = parseInt(result.page_total, 10); if (pages > 1) { throw new Error( diff --git a/packages/sdk38/src/lcdapi/index.ts b/packages/sdk38/src/lcdapi/index.ts index 59479bf3..04eeb8d9 100644 --- a/packages/sdk38/src/lcdapi/index.ts +++ b/packages/sdk38/src/lcdapi/index.ts @@ -18,3 +18,4 @@ export { SearchTxsResponse, TxsResponse, } from "./base"; +export { LcdApiArray, LcdClient, LcdModule } from "./lcdclient"; diff --git a/packages/sdk38/src/signingcosmosclient.spec.ts b/packages/sdk38/src/signingcosmosclient.spec.ts index 258c4f76..5ad36ce6 100644 --- a/packages/sdk38/src/signingcosmosclient.spec.ts +++ b/packages/sdk38/src/signingcosmosclient.spec.ts @@ -34,8 +34,8 @@ describe("SigningCosmosClient", () => { const client = new SigningCosmosClient(httpUrl, faucet.address, (signBytes) => pen.sign(signBytes)); const openedClient = (client as unknown) as PrivateCosmWasmClient; - const blockLatestSpy = spyOn(openedClient.restClient, "blocksLatest").and.callThrough(); - const authAccountsSpy = spyOn(openedClient.restClient, "authAccounts").and.callThrough(); + const blockLatestSpy = spyOn(openedClient.lcdClient, "blocksLatest").and.callThrough(); + const authAccountsSpy = spyOn(openedClient.lcdClient, "authAccounts").and.callThrough(); const height = await client.getHeight(); expect(height).toBeGreaterThan(0); diff --git a/packages/sdk38/types/cosmosclient.d.ts b/packages/sdk38/types/cosmosclient.d.ts index e83f6059..f5f31bc6 100644 --- a/packages/sdk38/types/cosmosclient.d.ts +++ b/packages/sdk38/types/cosmosclient.d.ts @@ -1,7 +1,6 @@ import { Coin } from "./coins"; -import { BroadcastMode } from "./lcdapi"; +import { BroadcastMode, LcdClient } from "./lcdapi"; import { Log } from "./logs"; -import { RestClient } from "./restclient"; import { CosmosSdkTx, PubKey, StdTx } from "./types"; export interface GetNonceResult { readonly accountNumber: number; @@ -95,10 +94,10 @@ export interface Block { } /** Use for testing only */ export interface PrivateCosmWasmClient { - readonly restClient: RestClient; + readonly lcdClient: LcdClient; } export declare class CosmosClient { - protected readonly restClient: RestClient; + protected readonly lcdClient: LcdClient; /** Any address the chain considers valid (valid bech32 with proper prefix) */ protected anyValidAddress: string | undefined; private chainId; diff --git a/packages/sdk38/types/lcdapi/index.d.ts b/packages/sdk38/types/lcdapi/index.d.ts index 5c36adce..e8512af4 100644 --- a/packages/sdk38/types/lcdapi/index.d.ts +++ b/packages/sdk38/types/lcdapi/index.d.ts @@ -9,3 +9,4 @@ export { SearchTxsResponse, TxsResponse, } from "./base"; +export { LcdApiArray, LcdClient, LcdModule } from "./lcdclient"; From c9e5143d97fdbef414b7bbbe88dfdcaeca4c183f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 10:14:15 +0200 Subject: [PATCH 17/37] Remove reference to RestClient --- packages/sdk38/src/lcdapi/lcdclient.ts | 2 +- packages/sdk38/types/lcdapi/lcdclient.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sdk38/src/lcdapi/lcdclient.ts b/packages/sdk38/src/lcdapi/lcdclient.ts index a2bb655e..5151297a 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.ts @@ -331,7 +331,7 @@ export class LcdClient { /** * Broadcasts a signed transaction to into the transaction pool. - * Depending on the RestClient's broadcast mode, this might or might + * Depending on the client's broadcast mode, this might or might * wait for checkTx or deliverTx to be executed before returning. * * @param tx a signed transaction as StdTx (i.e. not wrapped in type/value container) diff --git a/packages/sdk38/types/lcdapi/lcdclient.d.ts b/packages/sdk38/types/lcdapi/lcdclient.d.ts index b0e6fe67..b03afd8b 100644 --- a/packages/sdk38/types/lcdapi/lcdclient.d.ts +++ b/packages/sdk38/types/lcdapi/lcdclient.d.ts @@ -157,7 +157,7 @@ export declare class LcdClient { encodeTx(tx: CosmosSdkTx): Promise; /** * Broadcasts a signed transaction to into the transaction pool. - * Depending on the RestClient's broadcast mode, this might or might + * Depending on the client's broadcast mode, this might or might * wait for checkTx or deliverTx to be executed before returning. * * @param tx a signed transaction as StdTx (i.e. not wrapped in type/value container) From 21898bc8bd46668365019b1e7e7218d2b050c742 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 10:17:36 +0200 Subject: [PATCH 18/37] Export LcdClient --- packages/sdk38/src/index.ts | 5 ++++- packages/sdk38/types/index.d.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/sdk38/src/index.ts b/packages/sdk38/src/index.ts index b7bed9a0..e8119970 100644 --- a/packages/sdk38/src/index.ts +++ b/packages/sdk38/src/index.ts @@ -25,8 +25,11 @@ export { BlockResponse, BroadcastMode, EncodeTxResponse, - PostTxsResponse, + LcdApiArray, + LcdClient, + LcdModule, NodeInfoResponse, + PostTxsResponse, SearchTxsResponse, TxsResponse, } from "./lcdapi"; diff --git a/packages/sdk38/types/index.d.ts b/packages/sdk38/types/index.d.ts index c6bcbb3c..b11f2e8a 100644 --- a/packages/sdk38/types/index.d.ts +++ b/packages/sdk38/types/index.d.ts @@ -23,8 +23,11 @@ export { BlockResponse, BroadcastMode, EncodeTxResponse, - PostTxsResponse, + LcdApiArray, + LcdClient, + LcdModule, NodeInfoResponse, + PostTxsResponse, SearchTxsResponse, TxsResponse, } from "./lcdapi"; From e121ea9d08e5db8e6d8f37597af88624baf2eb96 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 10:57:48 +0200 Subject: [PATCH 19/37] Move makeSignedTx to testutils --- packages/cosmwasm/src/restclient.spec.ts | 24 ++---------------------- packages/cosmwasm/src/testutils.spec.ts | 10 ++++++++++ 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/packages/cosmwasm/src/restclient.spec.ts b/packages/cosmwasm/src/restclient.spec.ts index 3b45542a..7afcd29a 100644 --- a/packages/cosmwasm/src/restclient.spec.ts +++ b/packages/cosmwasm/src/restclient.spec.ts @@ -1,19 +1,7 @@ /* eslint-disable @typescript-eslint/camelcase */ import { Sha256 } from "@cosmjs/crypto"; import { Bech32, fromAscii, fromBase64, fromHex, toAscii, toBase64, toHex } from "@cosmjs/encoding"; -import { - Coin, - coin, - coins, - makeSignBytes, - Msg, - Pen, - PostTxsResponse, - Secp256k1Pen, - StdFee, - StdSignature, - StdTx, -} from "@cosmjs/sdk38"; +import { Coin, coin, coins, makeSignBytes, Pen, PostTxsResponse, Secp256k1Pen, StdFee } from "@cosmjs/sdk38"; import { assert } from "@cosmjs/utils"; import { findAttribute, parseLogs } from "./logs"; @@ -33,20 +21,12 @@ import { fromOneElementArray, getHackatom, makeRandomAddress, + makeSignedTx, pendingWithoutWasmd, wasmd, wasmdEnabled, } from "./testutils.spec"; -function makeSignedTx(firstMsg: Msg, fee: StdFee, memo: string, firstSignature: StdSignature): StdTx { - return { - msg: [firstMsg], - fee: fee, - memo: memo, - signatures: [firstSignature], - }; -} - async function uploadContract( client: RestClient, pen: Pen, diff --git a/packages/cosmwasm/src/testutils.spec.ts b/packages/cosmwasm/src/testutils.spec.ts index e2fb52f6..5cfe24a4 100644 --- a/packages/cosmwasm/src/testutils.spec.ts +++ b/packages/cosmwasm/src/testutils.spec.ts @@ -1,5 +1,6 @@ import { Random } from "@cosmjs/crypto"; import { Bech32, fromBase64 } from "@cosmjs/encoding"; +import { Msg, StdFee, StdSignature, StdTx } from "@cosmjs/sdk38"; import hackatom from "./testdata/contract.json"; @@ -85,3 +86,12 @@ export function fromOneElementArray(elements: ArrayLike): T { if (elements.length !== 1) throw new Error(`Expected exactly one element but got ${elements.length}`); return elements[0]; } + +export function makeSignedTx(firstMsg: Msg, fee: StdFee, memo: string, firstSignature: StdSignature): StdTx { + return { + msg: [firstMsg], + fee: fee, + memo: memo, + signatures: [firstSignature], + }; +} From ab4bbb7d28cdf11f0fa53ebe1e76bb41bc644490 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 23:20:43 +0200 Subject: [PATCH 20/37] Export normalizeLcdApiArray --- packages/sdk38/src/index.ts | 1 + packages/sdk38/src/lcdapi/index.ts | 2 +- packages/sdk38/src/lcdapi/lcdclient.spec.ts | 6 +++--- packages/sdk38/src/lcdapi/lcdclient.ts | 2 +- packages/sdk38/types/index.d.ts | 1 + packages/sdk38/types/lcdapi/index.d.ts | 2 +- packages/sdk38/types/lcdapi/lcdclient.d.ts | 2 +- 7 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/sdk38/src/index.ts b/packages/sdk38/src/index.ts index e8119970..d55d2149 100644 --- a/packages/sdk38/src/index.ts +++ b/packages/sdk38/src/index.ts @@ -29,6 +29,7 @@ export { LcdClient, LcdModule, NodeInfoResponse, + normalizeLcdApiArray, PostTxsResponse, SearchTxsResponse, TxsResponse, diff --git a/packages/sdk38/src/lcdapi/index.ts b/packages/sdk38/src/lcdapi/index.ts index 04eeb8d9..2dfbd914 100644 --- a/packages/sdk38/src/lcdapi/index.ts +++ b/packages/sdk38/src/lcdapi/index.ts @@ -18,4 +18,4 @@ export { SearchTxsResponse, TxsResponse, } from "./base"; -export { LcdApiArray, LcdClient, LcdModule } from "./lcdclient"; +export { LcdApiArray, LcdClient, LcdModule, normalizeLcdApiArray } from "./lcdclient"; diff --git a/packages/sdk38/src/lcdapi/lcdclient.spec.ts b/packages/sdk38/src/lcdapi/lcdclient.spec.ts index e80ee2c0..a8e0951d 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.spec.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.spec.ts @@ -22,7 +22,7 @@ import { } from "../testutils.spec"; import { StdFee } from "../types"; import { TxsResponse } from "./base"; -import { LcdApiArray, LcdClient, normalizeArray } from "./lcdclient"; +import { LcdApiArray, LcdClient, normalizeLcdApiArray } from "./lcdclient"; /** Deployed as part of scripts/wasmd/init.sh */ export const deployedErc20 = { @@ -94,7 +94,7 @@ describe("LcdClient", () => { listCodeInfo: async (): Promise => { const path = `/wasm/code`; const responseData = (await base.get(path)) as WasmResponse>; - return normalizeArray(unwrapWasmResponse(responseData)); + return normalizeLcdApiArray(unwrapWasmResponse(responseData)); }, }; } @@ -115,7 +115,7 @@ describe("LcdClient", () => { listCodeInfo: async (): Promise => { const path = `/wasm/code`; const responseData = (await base.get(path)) as WasmResponse>; - return normalizeArray(unwrapWasmResponse(responseData)); + return normalizeLcdApiArray(unwrapWasmResponse(responseData)); }, }; } diff --git a/packages/sdk38/src/lcdapi/lcdclient.ts b/packages/sdk38/src/lcdapi/lcdclient.ts index 5151297a..f0889396 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.ts @@ -17,7 +17,7 @@ import { /** Unfortunately, Cosmos SDK encodes empty arrays as null */ export type LcdApiArray = readonly T[] | null; -export function normalizeArray(backend: LcdApiArray): readonly T[] { +export function normalizeLcdApiArray(backend: LcdApiArray): readonly T[] { return backend || []; } diff --git a/packages/sdk38/types/index.d.ts b/packages/sdk38/types/index.d.ts index b11f2e8a..b3fd530d 100644 --- a/packages/sdk38/types/index.d.ts +++ b/packages/sdk38/types/index.d.ts @@ -27,6 +27,7 @@ export { LcdClient, LcdModule, NodeInfoResponse, + normalizeLcdApiArray, PostTxsResponse, SearchTxsResponse, TxsResponse, diff --git a/packages/sdk38/types/lcdapi/index.d.ts b/packages/sdk38/types/lcdapi/index.d.ts index e8512af4..5dd4140a 100644 --- a/packages/sdk38/types/lcdapi/index.d.ts +++ b/packages/sdk38/types/lcdapi/index.d.ts @@ -9,4 +9,4 @@ export { SearchTxsResponse, TxsResponse, } from "./base"; -export { LcdApiArray, LcdClient, LcdModule } from "./lcdclient"; +export { LcdApiArray, LcdClient, LcdModule, normalizeLcdApiArray } from "./lcdclient"; diff --git a/packages/sdk38/types/lcdapi/lcdclient.d.ts b/packages/sdk38/types/lcdapi/lcdclient.d.ts index b03afd8b..379051c7 100644 --- a/packages/sdk38/types/lcdapi/lcdclient.d.ts +++ b/packages/sdk38/types/lcdapi/lcdclient.d.ts @@ -11,7 +11,7 @@ import { } from "./base"; /** Unfortunately, Cosmos SDK encodes empty arrays as null */ export declare type LcdApiArray = readonly T[] | null; -export declare function normalizeArray(backend: LcdApiArray): readonly T[]; +export declare function normalizeLcdApiArray(backend: LcdApiArray): readonly T[]; export declare type LcdModule = Record any>; declare type LcdModuleSetup = (base: LcdClient) => M; export interface LcdClientBaseOptions { From b352e942ab8f0bbdc48a7fe8ab8b7a97043ce208 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 11:07:39 +0200 Subject: [PATCH 21/37] Create Wasm module for LCD --- packages/cosmwasm/src/lcdapi/wasm.spec.ts | 510 ++++++++++++++++++ packages/cosmwasm/src/lcdapi/wasm.ts | 157 ++++++ packages/cosmwasm/types/lcdapi/wasm.d.ts | 72 +++ packages/cosmwasm/types/lcdapi/wasm.spec.d.ts | 1 + 4 files changed, 740 insertions(+) create mode 100644 packages/cosmwasm/src/lcdapi/wasm.spec.ts create mode 100644 packages/cosmwasm/src/lcdapi/wasm.ts create mode 100644 packages/cosmwasm/types/lcdapi/wasm.d.ts create mode 100644 packages/cosmwasm/types/lcdapi/wasm.spec.d.ts diff --git a/packages/cosmwasm/src/lcdapi/wasm.spec.ts b/packages/cosmwasm/src/lcdapi/wasm.spec.ts new file mode 100644 index 00000000..aa408871 --- /dev/null +++ b/packages/cosmwasm/src/lcdapi/wasm.spec.ts @@ -0,0 +1,510 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { Sha256 } from "@cosmjs/crypto"; +import { Bech32, fromAscii, fromBase64, fromHex, toAscii, toBase64, toHex } from "@cosmjs/encoding"; +import { + Coin, + coin, + coins, + LcdClient, + makeSignBytes, + Pen, + PostTxsResponse, + Secp256k1Pen, + StdFee, +} from "@cosmjs/sdk38"; +import { assert } from "@cosmjs/utils"; + +import { findAttribute, parseLogs } from "../logs"; +import { + isMsgInstantiateContract, + isMsgStoreCode, + MsgExecuteContract, + MsgInstantiateContract, + MsgStoreCode, +} from "../msgs"; +import { + alice, + bech32AddressMatcher, + ContractUploadInstructions, + deployedErc20, + fromOneElementArray, + getHackatom, + makeRandomAddress, + makeSignedTx, + pendingWithoutWasmd, + wasmd, + wasmdEnabled, +} from "../testutils.spec"; +import { setupWasmModule, WasmModule } from "./wasm"; + +type WasmClient = LcdClient & WasmModule; + +function makeWasmClient(apiUrl: string): WasmClient { + return LcdClient.withModules({ apiUrl }, setupWasmModule); +} + +async function uploadContract( + client: WasmClient, + pen: Pen, + contract: ContractUploadInstructions, +): Promise { + const memo = "My first contract on chain"; + const theMsg: MsgStoreCode = { + type: "wasm/store-code", + value: { + sender: alice.address0, + wasm_byte_code: toBase64(contract.data), + source: contract.source || "", + builder: contract.builder || "", + }, + }; + const fee: StdFee = { + amount: [ + { + amount: "5000000", + denom: "ucosm", + }, + ], + gas: "89000000", + }; + + const { account_number, sequence } = (await client.authAccounts(alice.address0)).result.value; + const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); + const signature = await pen.sign(signBytes); + const signedTx = makeSignedTx(theMsg, fee, memo, signature); + return client.postTx(signedTx); +} + +async function instantiateContract( + client: WasmClient, + pen: Pen, + codeId: number, + beneficiaryAddress: string, + transferAmount?: readonly Coin[], +): Promise { + const memo = "Create an escrow instance"; + const theMsg: MsgInstantiateContract = { + type: "wasm/instantiate", + value: { + sender: alice.address0, + code_id: codeId.toString(), + label: "my escrow", + init_msg: { + verifier: alice.address0, + beneficiary: beneficiaryAddress, + }, + init_funds: transferAmount || [], + }, + }; + const fee: StdFee = { + amount: [ + { + amount: "5000000", + denom: "ucosm", + }, + ], + gas: "89000000", + }; + + const { account_number, sequence } = (await client.authAccounts(alice.address0)).result.value; + const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); + const signature = await pen.sign(signBytes); + const signedTx = makeSignedTx(theMsg, fee, memo, signature); + return client.postTx(signedTx); +} + +async function executeContract( + client: WasmClient, + pen: Pen, + contractAddress: string, + msg: object, +): Promise { + const memo = "Time for action"; + const theMsg: MsgExecuteContract = { + type: "wasm/execute", + value: { + sender: alice.address0, + contract: contractAddress, + msg: msg, + sent_funds: [], + }, + }; + const fee: StdFee = { + amount: coins(5000000, "ucosm"), + gas: "89000000", + }; + + const { account_number, sequence } = (await client.authAccounts(alice.address0)).result.value; + const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); + const signature = await pen.sign(signBytes); + const signedTx = makeSignedTx(theMsg, fee, memo, signature); + return client.postTx(signedTx); +} + +describe("wasm", () => { + it("can be constructed", () => { + const client = makeWasmClient(wasmd.endpoint); + expect(client).toBeTruthy(); + }); + + describe("txsQuery", () => { + it("can query by tags (module + code_id)", async () => { + pendingWithoutWasmd(); + const client = makeWasmClient(wasmd.endpoint); + const result = await client.txsQuery(`message.module=wasm&message.code_id=${deployedErc20.codeId}`); + expect(parseInt(result.count, 10)).toBeGreaterThanOrEqual(4); + + // Check first 4 results + const [store, hash, isa, jade] = result.txs.map((tx) => fromOneElementArray(tx.tx.value.msg)); + assert(isMsgStoreCode(store)); + assert(isMsgInstantiateContract(hash)); + assert(isMsgInstantiateContract(isa)); + assert(isMsgInstantiateContract(jade)); + expect(store.value).toEqual( + jasmine.objectContaining({ + sender: alice.address0, + source: deployedErc20.source, + builder: deployedErc20.builder, + }), + ); + expect(hash.value).toEqual({ + code_id: deployedErc20.codeId.toString(), + init_funds: [], + init_msg: jasmine.objectContaining({ + symbol: "HASH", + }), + label: "HASH", + sender: alice.address0, + }); + expect(isa.value).toEqual({ + code_id: deployedErc20.codeId.toString(), + init_funds: [], + init_msg: jasmine.objectContaining({ symbol: "ISA" }), + label: "ISA", + sender: alice.address0, + }); + expect(jade.value).toEqual({ + code_id: deployedErc20.codeId.toString(), + init_funds: [], + init_msg: jasmine.objectContaining({ symbol: "JADE" }), + label: "JADE", + sender: alice.address0, + admin: alice.address1, + }); + }); + + // Like previous test but filtered by message.action=store-code and message.action=instantiate + it("can query by tags (module + code_id + action)", async () => { + pendingWithoutWasmd(); + const client = makeWasmClient(wasmd.endpoint); + + { + const uploads = await client.txsQuery( + `message.module=wasm&message.code_id=${deployedErc20.codeId}&message.action=store-code`, + ); + expect(parseInt(uploads.count, 10)).toEqual(1); + const store = fromOneElementArray(uploads.txs[0].tx.value.msg); + assert(isMsgStoreCode(store)); + expect(store.value).toEqual( + jasmine.objectContaining({ + sender: alice.address0, + source: deployedErc20.source, + builder: deployedErc20.builder, + }), + ); + } + + { + const instantiations = await client.txsQuery( + `message.module=wasm&message.code_id=${deployedErc20.codeId}&message.action=instantiate`, + ); + expect(parseInt(instantiations.count, 10)).toBeGreaterThanOrEqual(3); + const [hash, isa, jade] = instantiations.txs.map((tx) => fromOneElementArray(tx.tx.value.msg)); + assert(isMsgInstantiateContract(hash)); + assert(isMsgInstantiateContract(isa)); + assert(isMsgInstantiateContract(jade)); + expect(hash.value).toEqual({ + code_id: deployedErc20.codeId.toString(), + init_funds: [], + init_msg: jasmine.objectContaining({ + symbol: "HASH", + }), + label: "HASH", + sender: alice.address0, + }); + expect(isa.value).toEqual({ + code_id: deployedErc20.codeId.toString(), + init_funds: [], + init_msg: jasmine.objectContaining({ symbol: "ISA" }), + label: "ISA", + sender: alice.address0, + }); + expect(jade.value).toEqual({ + code_id: deployedErc20.codeId.toString(), + init_funds: [], + init_msg: jasmine.objectContaining({ symbol: "JADE" }), + label: "JADE", + sender: alice.address0, + admin: alice.address1, + }); + } + }); + }); + + describe("postTx", () => { + it("can upload, instantiate and execute wasm", async () => { + pendingWithoutWasmd(); + const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic); + const client = makeWasmClient(wasmd.endpoint); + + const transferAmount = [coin(1234, "ucosm"), coin(321, "ustake")]; + const beneficiaryAddress = makeRandomAddress(); + + let codeId: number; + + // upload + { + // console.log("Raw log:", result.raw_log); + const result = await uploadContract(client, pen, getHackatom()); + expect(result.code).toBeFalsy(); + const logs = parseLogs(result.logs); + const codeIdAttr = findAttribute(logs, "message", "code_id"); + codeId = Number.parseInt(codeIdAttr.value, 10); + expect(codeId).toBeGreaterThanOrEqual(1); + expect(codeId).toBeLessThanOrEqual(200); + expect(result.data).toEqual(toHex(toAscii(`${codeId}`)).toUpperCase()); + } + + let contractAddress: string; + + // instantiate + { + const result = await instantiateContract(client, pen, codeId, beneficiaryAddress, transferAmount); + expect(result.code).toBeFalsy(); + // console.log("Raw log:", result.raw_log); + const logs = parseLogs(result.logs); + const contractAddressAttr = findAttribute(logs, "message", "contract_address"); + contractAddress = contractAddressAttr.value; + const amountAttr = findAttribute(logs, "transfer", "amount"); + expect(amountAttr.value).toEqual("1234ucosm,321ustake"); + expect(result.data).toEqual(toHex(Bech32.decode(contractAddress).data).toUpperCase()); + + const balance = (await client.authAccounts(contractAddress)).result.value.coins; + expect(balance).toEqual(transferAmount); + } + + // execute + { + const result = await executeContract(client, pen, contractAddress, { release: {} }); + expect(result.data).toEqual("F00BAA"); + expect(result.code).toBeFalsy(); + // console.log("Raw log:", result.logs); + const logs = parseLogs(result.logs); + const wasmEvent = logs.find(() => true)?.events.find((e) => e.type === "wasm"); + assert(wasmEvent, "Event of type wasm expected"); + expect(wasmEvent.attributes).toContain({ key: "action", value: "release" }); + expect(wasmEvent.attributes).toContain({ + key: "destination", + value: beneficiaryAddress, + }); + + // Verify token transfer from contract to beneficiary + const beneficiaryBalance = (await client.authAccounts(beneficiaryAddress)).result.value.coins; + expect(beneficiaryBalance).toEqual(transferAmount); + const contractBalance = (await client.authAccounts(contractAddress)).result.value.coins; + expect(contractBalance).toEqual([]); + } + }); + }); + + // The /wasm endpoints + + describe("query", () => { + it("can list upload code", async () => { + pendingWithoutWasmd(); + const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic); + const client = makeWasmClient(wasmd.endpoint); + + // check with contracts were here first to compare + const existingInfos = await client.listCodeInfo(); + existingInfos.forEach((val, idx) => expect(val.id).toEqual(idx + 1)); + const numExisting = existingInfos.length; + + // upload data + const hackatom = getHackatom(); + const result = await uploadContract(client, pen, hackatom); + expect(result.code).toBeFalsy(); + const logs = parseLogs(result.logs); + const codeIdAttr = findAttribute(logs, "message", "code_id"); + const codeId = Number.parseInt(codeIdAttr.value, 10); + + // ensure we were added to the end of the list + const newInfos = await client.listCodeInfo(); + expect(newInfos.length).toEqual(numExisting + 1); + const lastInfo = newInfos[newInfos.length - 1]; + expect(lastInfo.id).toEqual(codeId); + expect(lastInfo.creator).toEqual(alice.address0); + + // ensure metadata is present + expect(lastInfo.source).toEqual(hackatom.source); + expect(lastInfo.builder).toEqual(hackatom.builder); + + // check code hash matches expectation + const wasmHash = new Sha256(hackatom.data).digest(); + expect(lastInfo.data_hash.toLowerCase()).toEqual(toHex(wasmHash)); + + // download code and check against auto-gen + const { data } = await client.getCode(codeId); + expect(fromBase64(data)).toEqual(hackatom.data); + }); + + it("can list contracts and get info", async () => { + pendingWithoutWasmd(); + const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic); + const client = makeWasmClient(wasmd.endpoint); + const beneficiaryAddress = makeRandomAddress(); + const transferAmount: readonly Coin[] = [ + { + amount: "707707", + denom: "ucosm", + }, + ]; + + // reuse an existing contract, or upload if needed + let codeId: number; + const existingInfos = await client.listCodeInfo(); + if (existingInfos.length > 0) { + codeId = existingInfos[existingInfos.length - 1].id; + } else { + const uploadResult = await uploadContract(client, pen, getHackatom()); + expect(uploadResult.code).toBeFalsy(); + const uploadLogs = parseLogs(uploadResult.logs); + const codeIdAttr = findAttribute(uploadLogs, "message", "code_id"); + codeId = Number.parseInt(codeIdAttr.value, 10); + } + + // create new instance and compare before and after + const existingContractsByCode = await client.listContractsByCodeId(codeId); + for (const contract of existingContractsByCode) { + expect(contract.address).toMatch(bech32AddressMatcher); + expect(contract.code_id).toEqual(codeId); + expect(contract.creator).toMatch(bech32AddressMatcher); + expect(contract.label).toMatch(/^.+$/); + } + + const result = await instantiateContract(client, pen, codeId, beneficiaryAddress, transferAmount); + expect(result.code).toBeFalsy(); + const logs = parseLogs(result.logs); + const contractAddressAttr = findAttribute(logs, "message", "contract_address"); + const myAddress = contractAddressAttr.value; + + const newContractsByCode = await client.listContractsByCodeId(codeId); + expect(newContractsByCode.length).toEqual(existingContractsByCode.length + 1); + const newContract = newContractsByCode[newContractsByCode.length - 1]; + expect(newContract).toEqual( + jasmine.objectContaining({ + code_id: codeId, + creator: alice.address0, + label: "my escrow", + }), + ); + + // check out info + const myInfo = await client.getContractInfo(myAddress); + assert(myInfo); + expect(myInfo).toEqual( + jasmine.objectContaining({ + code_id: codeId, + creator: alice.address0, + init_msg: jasmine.objectContaining({ + beneficiary: beneficiaryAddress, + }), + }), + ); + expect(myInfo.admin).toBeUndefined(); + + // make sure random addresses don't give useful info + const nonExistentAddress = makeRandomAddress(); + expect(await client.getContractInfo(nonExistentAddress)).toBeNull(); + }); + + describe("contract state", () => { + const client = makeWasmClient(wasmd.endpoint); + const noContract = makeRandomAddress(); + const expectedKey = toAscii("config"); + let contractAddress: string | undefined; + + beforeAll(async () => { + if (wasmdEnabled()) { + const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic); + const uploadResult = await uploadContract(client, pen, getHackatom()); + assert(!uploadResult.code); + const uploadLogs = parseLogs(uploadResult.logs); + const codeId = Number.parseInt(findAttribute(uploadLogs, "message", "code_id").value, 10); + const instantiateResult = await instantiateContract(client, pen, codeId, makeRandomAddress()); + assert(!instantiateResult.code); + const instantiateLogs = parseLogs(instantiateResult.logs); + const contractAddressAttr = findAttribute(instantiateLogs, "message", "contract_address"); + contractAddress = contractAddressAttr.value; + } + }); + + it("can get all state", async () => { + pendingWithoutWasmd(); + + // get contract state + const state = await client.getAllContractState(contractAddress!); + expect(state.length).toEqual(1); + const data = state[0]; + expect(data.key).toEqual(expectedKey); + const value = JSON.parse(fromAscii(data.val)); + expect(value.verifier).toBeDefined(); + expect(value.beneficiary).toBeDefined(); + + // bad address is empty array + const noContractState = await client.getAllContractState(noContract); + expect(noContractState).toEqual([]); + }); + + it("can query by key", async () => { + pendingWithoutWasmd(); + + // query by one key + const raw = await client.queryContractRaw(contractAddress!, expectedKey); + assert(raw, "must get result"); + const model = JSON.parse(fromAscii(raw)); + expect(model.verifier).toBeDefined(); + expect(model.beneficiary).toBeDefined(); + + // missing key is null + const missing = await client.queryContractRaw(contractAddress!, fromHex("cafe0dad")); + expect(missing).toBeNull(); + + // bad address is null + const noContractModel = await client.queryContractRaw(noContract, expectedKey); + expect(noContractModel).toBeNull(); + }); + + it("can make smart queries", async () => { + pendingWithoutWasmd(); + + // we can query the verifier properly + const resultDocument = await client.queryContractSmart(contractAddress!, { verifier: {} }); + expect(resultDocument).toEqual({ verifier: alice.address0 }); + + // invalid query syntax throws an error + await client.queryContractSmart(contractAddress!, { nosuchkey: {} }).then( + () => fail("shouldn't succeed"), + (error) => + expect(error).toMatch(/query wasm contract failed: parsing hackatom::contract::QueryMsg/), + ); + + // invalid address throws an error + await client.queryContractSmart(noContract, { verifier: {} }).then( + () => fail("shouldn't succeed"), + (error) => expect(error).toMatch("not found"), + ); + }); + }); + }); +}); diff --git a/packages/cosmwasm/src/lcdapi/wasm.ts b/packages/cosmwasm/src/lcdapi/wasm.ts new file mode 100644 index 00000000..538f4baa --- /dev/null +++ b/packages/cosmwasm/src/lcdapi/wasm.ts @@ -0,0 +1,157 @@ +import { fromBase64, fromUtf8, toHex, toUtf8 } from "@cosmjs/encoding"; +import { LcdApiArray, LcdClient, LcdModule, normalizeLcdApiArray } from "@cosmjs/sdk38"; + +import { JsonObject, Model, parseWasmData, WasmData } from "../types"; + +type WasmResponse = WasmSuccess | WasmError; + +interface WasmSuccess { + readonly height: string; + readonly result: T; +} + +interface WasmError { + readonly error: string; +} + +export interface CodeInfo { + readonly id: number; + /** Bech32 account address */ + readonly creator: string; + /** Hex-encoded sha256 hash of the code stored here */ + readonly data_hash: string; + /** + * An URL to a .tar.gz archive of the source code of the contract, which can be used to reproducibly build the Wasm bytecode. + * + * @see https://github.com/CosmWasm/cosmwasm-verify + */ + readonly source?: string; + /** + * A docker image (including version) to reproducibly build the Wasm bytecode from the source code. + * + * @example ```cosmwasm/rust-optimizer:0.8.0``` + * @see https://github.com/CosmWasm/cosmwasm-verify + */ + readonly builder?: string; +} + +export interface CodeDetails extends CodeInfo { + /** Base64 encoded raw wasm data */ + readonly data: string; +} + +// This is list view, without contract info +export interface ContractInfo { + readonly address: string; + readonly code_id: number; + /** Bech32 account address */ + readonly creator: string; + /** Bech32-encoded admin address */ + readonly admin?: string; + readonly label: string; +} + +export interface ContractDetails extends ContractInfo { + /** Argument passed on initialization of the contract */ + readonly init_msg: object; +} + +interface SmartQueryResponse { + // base64 encoded response + readonly smart: string; +} + +function isWasmError(resp: WasmResponse): resp is WasmError { + return (resp as WasmError).error !== undefined; +} + +function unwrapWasmResponse(response: WasmResponse): T { + if (isWasmError(response)) { + throw new Error(response.error); + } + return response.result; +} + +/** + * @see https://github.com/cosmwasm/wasmd/blob/master/x/wasm/client/rest/query.go#L19-L27 + */ +export interface WasmModule extends LcdModule { + readonly listCodeInfo: () => Promise; + + /** + * Downloads the original wasm bytecode by code ID. + * + * Throws an error if no code with this id + */ + readonly getCode: (id: number) => Promise; + + readonly listContractsByCodeId: (id: number) => Promise; + + /** + * Returns null when contract was not found at this address. + */ + readonly getContractInfo: (address: string) => Promise; + + /** + * Returns all contract state. + * This is an empty array if no such contract, or contract has no data. + */ + readonly getAllContractState: (address: string) => Promise; + + /** + * Returns the data at the key if present (unknown decoded json), + * or null if no data at this (contract address, key) pair + */ + readonly queryContractRaw: (address: string, key: Uint8Array) => Promise; + + /** + * Makes a smart query on the contract and parses the reponse as JSON. + * Throws error if no such contract exists, the query format is invalid or the response is invalid. + */ + readonly queryContractSmart: (address: string, query: object) => Promise; +} + +export function setupWasmModule(base: LcdClient): WasmModule { + return { + listCodeInfo: async () => { + const path = `/wasm/code`; + const responseData = (await base.get(path)) as WasmResponse>; + return normalizeLcdApiArray(unwrapWasmResponse(responseData)); + }, + getCode: async (id: number) => { + const path = `/wasm/code/${id}`; + const responseData = (await base.get(path)) as WasmResponse; + return unwrapWasmResponse(responseData); + }, + listContractsByCodeId: async (id: number) => { + const path = `/wasm/code/${id}/contracts`; + const responseData = (await base.get(path)) as WasmResponse>; + return normalizeLcdApiArray(unwrapWasmResponse(responseData)); + }, + getContractInfo: async (address: string) => { + const path = `/wasm/contract/${address}`; + const response = (await base.get(path)) as WasmResponse; + return unwrapWasmResponse(response); + }, + getAllContractState: async (address: string) => { + const path = `/wasm/contract/${address}/state`; + const responseData = (await base.get(path)) as WasmResponse>; + return normalizeLcdApiArray(unwrapWasmResponse(responseData)).map(parseWasmData); + }, + queryContractRaw: async (address: string, key: Uint8Array) => { + const hexKey = toHex(key); + const path = `/wasm/contract/${address}/raw/${hexKey}?encoding=hex`; + const responseData = (await base.get(path)) as WasmResponse; + const data = unwrapWasmResponse(responseData); + return data.length === 0 ? null : fromBase64(data[0].val); + }, + queryContractSmart: async (address: string, query: object) => { + const encoded = toHex(toUtf8(JSON.stringify(query))); + const path = `/wasm/contract/${address}/smart/${encoded}?encoding=hex`; + const responseData = (await base.get(path)) as WasmResponse; + const result = unwrapWasmResponse(responseData); + // By convention, smart queries must return a valid JSON document (see https://github.com/CosmWasm/cosmwasm/issues/144) + return JSON.parse(fromUtf8(fromBase64(result.smart))); + }, + }; +} diff --git a/packages/cosmwasm/types/lcdapi/wasm.d.ts b/packages/cosmwasm/types/lcdapi/wasm.d.ts new file mode 100644 index 00000000..3e4d67b8 --- /dev/null +++ b/packages/cosmwasm/types/lcdapi/wasm.d.ts @@ -0,0 +1,72 @@ +import { LcdClient, LcdModule } from "@cosmjs/sdk38"; +import { JsonObject, Model } from "../types"; +export interface CodeInfo { + readonly id: number; + /** Bech32 account address */ + readonly creator: string; + /** Hex-encoded sha256 hash of the code stored here */ + readonly data_hash: string; + /** + * An URL to a .tar.gz archive of the source code of the contract, which can be used to reproducibly build the Wasm bytecode. + * + * @see https://github.com/CosmWasm/cosmwasm-verify + */ + readonly source?: string; + /** + * A docker image (including version) to reproducibly build the Wasm bytecode from the source code. + * + * @example ```cosmwasm/rust-optimizer:0.8.0``` + * @see https://github.com/CosmWasm/cosmwasm-verify + */ + readonly builder?: string; +} +export interface CodeDetails extends CodeInfo { + /** Base64 encoded raw wasm data */ + readonly data: string; +} +export interface ContractInfo { + readonly address: string; + readonly code_id: number; + /** Bech32 account address */ + readonly creator: string; + /** Bech32-encoded admin address */ + readonly admin?: string; + readonly label: string; +} +export interface ContractDetails extends ContractInfo { + /** Argument passed on initialization of the contract */ + readonly init_msg: object; +} +/** + * @see https://github.com/cosmwasm/wasmd/blob/master/x/wasm/client/rest/query.go#L19-L27 + */ +export interface WasmModule extends LcdModule { + readonly listCodeInfo: () => Promise; + /** + * Downloads the original wasm bytecode by code ID. + * + * Throws an error if no code with this id + */ + readonly getCode: (id: number) => Promise; + readonly listContractsByCodeId: (id: number) => Promise; + /** + * Returns null when contract was not found at this address. + */ + readonly getContractInfo: (address: string) => Promise; + /** + * Returns all contract state. + * This is an empty array if no such contract, or contract has no data. + */ + readonly getAllContractState: (address: string) => Promise; + /** + * Returns the data at the key if present (unknown decoded json), + * or null if no data at this (contract address, key) pair + */ + readonly queryContractRaw: (address: string, key: Uint8Array) => Promise; + /** + * Makes a smart query on the contract and parses the reponse as JSON. + * Throws error if no such contract exists, the query format is invalid or the response is invalid. + */ + readonly queryContractSmart: (address: string, query: object) => Promise; +} +export declare function setupWasmModule(base: LcdClient): WasmModule; diff --git a/packages/cosmwasm/types/lcdapi/wasm.spec.d.ts b/packages/cosmwasm/types/lcdapi/wasm.spec.d.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/packages/cosmwasm/types/lcdapi/wasm.spec.d.ts @@ -0,0 +1 @@ +export {}; From c85c172395450b36a073d9ea6b3c137360db0ee4 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 23:31:14 +0200 Subject: [PATCH 22/37] Pull out AuthModule --- packages/cosmwasm/src/lcdapi/wasm.spec.ts | 6 +- packages/sdk38/src/cosmosclient.ts | 8 +-- packages/sdk38/src/index.ts | 4 ++ packages/sdk38/src/lcdapi/auth.spec.ts | 68 +++++++++++++++++++++ packages/sdk38/src/lcdapi/auth.ts | 37 +++++++++++ packages/sdk38/src/lcdapi/base.ts | 19 ------ packages/sdk38/src/lcdapi/index.ts | 4 +- packages/sdk38/src/lcdapi/lcdclient.spec.ts | 13 ++-- packages/sdk38/src/lcdapi/lcdclient.ts | 12 ---- packages/sdk38/types/cosmosclient.d.ts | 6 +- packages/sdk38/types/index.d.ts | 4 ++ packages/sdk38/types/lcdapi/auth.d.ts | 22 +++++++ packages/sdk38/types/lcdapi/auth.spec.d.ts | 1 + packages/sdk38/types/lcdapi/base.d.ts | 17 ------ packages/sdk38/types/lcdapi/index.d.ts | 4 +- packages/sdk38/types/lcdapi/lcdclient.d.ts | 2 - 16 files changed, 158 insertions(+), 69 deletions(-) create mode 100644 packages/sdk38/src/lcdapi/auth.spec.ts create mode 100644 packages/sdk38/src/lcdapi/auth.ts create mode 100644 packages/sdk38/types/lcdapi/auth.d.ts create mode 100644 packages/sdk38/types/lcdapi/auth.spec.d.ts diff --git a/packages/cosmwasm/src/lcdapi/wasm.spec.ts b/packages/cosmwasm/src/lcdapi/wasm.spec.ts index aa408871..183d086a 100644 --- a/packages/cosmwasm/src/lcdapi/wasm.spec.ts +++ b/packages/cosmwasm/src/lcdapi/wasm.spec.ts @@ -2,6 +2,7 @@ import { Sha256 } from "@cosmjs/crypto"; import { Bech32, fromAscii, fromBase64, fromHex, toAscii, toBase64, toHex } from "@cosmjs/encoding"; import { + AuthModule, Coin, coin, coins, @@ -10,6 +11,7 @@ import { Pen, PostTxsResponse, Secp256k1Pen, + setupAuthModule, StdFee, } from "@cosmjs/sdk38"; import { assert } from "@cosmjs/utils"; @@ -37,10 +39,10 @@ import { } from "../testutils.spec"; import { setupWasmModule, WasmModule } from "./wasm"; -type WasmClient = LcdClient & WasmModule; +type WasmClient = LcdClient & AuthModule & WasmModule; function makeWasmClient(apiUrl: string): WasmClient { - return LcdClient.withModules({ apiUrl }, setupWasmModule); + return LcdClient.withModules({ apiUrl }, setupAuthModule, setupWasmModule); } async function uploadContract( diff --git a/packages/sdk38/src/cosmosclient.ts b/packages/sdk38/src/cosmosclient.ts index 5ab8b2b2..bcdfdc63 100644 --- a/packages/sdk38/src/cosmosclient.ts +++ b/packages/sdk38/src/cosmosclient.ts @@ -3,7 +3,7 @@ import { fromBase64, fromHex, toHex } from "@cosmjs/encoding"; import { Uint53 } from "@cosmjs/math"; import { Coin } from "./coins"; -import { BroadcastMode, LcdClient } from "./lcdapi"; +import { AuthModule, BroadcastMode, LcdClient, setupAuthModule } from "./lcdapi"; import { Log, parseLogs } from "./logs"; import { decodeBech32Pubkey } from "./pubkey"; import { CosmosSdkTx, PubKey, StdTx } from "./types"; @@ -130,11 +130,11 @@ export interface Block { /** Use for testing only */ export interface PrivateCosmWasmClient { - readonly lcdClient: LcdClient; + readonly lcdClient: LcdClient & AuthModule; } export class CosmosClient { - protected readonly lcdClient: LcdClient; + protected readonly lcdClient: LcdClient & AuthModule; /** Any address the chain considers valid (valid bech32 with proper prefix) */ protected anyValidAddress: string | undefined; @@ -150,7 +150,7 @@ export class CosmosClient { * @param broadcastMode Defines at which point of the transaction processing the postTx method (i.e. transaction broadcasting) returns */ public constructor(apiUrl: string, broadcastMode = BroadcastMode.Block) { - this.lcdClient = new LcdClient(apiUrl, broadcastMode); + this.lcdClient = LcdClient.withModules({ apiUrl: apiUrl, broadcastMode: broadcastMode }, setupAuthModule); } public async getChainId(): Promise { diff --git a/packages/sdk38/src/index.ts b/packages/sdk38/src/index.ts index d55d2149..55d6360d 100644 --- a/packages/sdk38/src/index.ts +++ b/packages/sdk38/src/index.ts @@ -22,6 +22,7 @@ export { export { makeSignBytes } from "./encoding"; export { AuthAccountsResponse, + AuthModule, BlockResponse, BroadcastMode, EncodeTxResponse, @@ -32,6 +33,9 @@ export { normalizeLcdApiArray, PostTxsResponse, SearchTxsResponse, + setupAuthModule, + setupSupplyModule, + SupplyModule, TxsResponse, } from "./lcdapi"; export { RestClient } from "./restclient"; diff --git a/packages/sdk38/src/lcdapi/auth.spec.ts b/packages/sdk38/src/lcdapi/auth.spec.ts new file mode 100644 index 00000000..2aabe9c1 --- /dev/null +++ b/packages/sdk38/src/lcdapi/auth.spec.ts @@ -0,0 +1,68 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { encodeBech32Pubkey } from "../pubkey"; +import { + faucet, + makeRandomAddress, + nonNegativeIntegerMatcher, + pendingWithoutWasmd, + unused, + wasmd, +} from "../testutils.spec"; +import { AuthModule, setupAuthModule } from "./auth"; +import { LcdClient } from "./lcdclient"; + +function makeAuthClient(apiUrl: string): LcdClient & AuthModule { + return LcdClient.withModules({ apiUrl }, setupAuthModule); +} + +describe("auth", () => { + it("works for unused account without pubkey", async () => { + pendingWithoutWasmd(); + const client = makeAuthClient(wasmd.endpoint); + const { height, result } = await client.authAccounts(unused.address); + expect(height).toMatch(nonNegativeIntegerMatcher); + expect(result).toEqual({ + type: "cosmos-sdk/Account", + value: { + address: unused.address, + public_key: "", // not known to the chain + coins: [ + { + amount: "1000000000", + denom: "ucosm", + }, + { + amount: "1000000000", + denom: "ustake", + }, + ], + account_number: unused.accountNumber, + sequence: 0, + }, + }); + }); + + // This fails in the first test run if you forget to run `./scripts/wasmd/init.sh` + it("has correct pubkey for faucet", async () => { + pendingWithoutWasmd(); + const client = makeAuthClient(wasmd.endpoint); + const { result } = await client.authAccounts(faucet.address); + expect(result.value).toEqual( + jasmine.objectContaining({ + public_key: encodeBech32Pubkey(faucet.pubkey, "cosmospub"), + }), + ); + }); + + // This property is used by CosmWasmClient.getAccount + it("returns empty address for non-existent account", async () => { + pendingWithoutWasmd(); + const client = makeAuthClient(wasmd.endpoint); + const nonExistentAccount = makeRandomAddress(); + const { result } = await client.authAccounts(nonExistentAccount); + expect(result).toEqual({ + type: "cosmos-sdk/Account", + value: jasmine.objectContaining({ address: "" }), + }); + }); +}); diff --git a/packages/sdk38/src/lcdapi/auth.ts b/packages/sdk38/src/lcdapi/auth.ts new file mode 100644 index 00000000..497f834f --- /dev/null +++ b/packages/sdk38/src/lcdapi/auth.ts @@ -0,0 +1,37 @@ +import { Coin } from "../coins"; +import { LcdClient, LcdModule } from "./lcdclient"; + +export interface CosmosSdkAccount { + /** Bech32 account address */ + readonly address: string; + readonly coins: readonly Coin[]; + /** Bech32 encoded pubkey */ + readonly public_key: string; + readonly account_number: number; + readonly sequence: number; +} + +export interface AuthAccountsResponse { + readonly height: string; + readonly result: { + readonly type: "cosmos-sdk/Account"; + readonly value: CosmosSdkAccount; + }; +} + +export interface AuthModule extends LcdModule { + readonly authAccounts: (address: string) => Promise; +} + +export function setupAuthModule(base: LcdClient): AuthModule { + return { + authAccounts: async (address: string) => { + const path = `/auth/accounts/${address}`; + const responseData = await base.get(path); + if (responseData.result.type !== "cosmos-sdk/Account") { + throw new Error("Unexpected response data format"); + } + return responseData as AuthAccountsResponse; + }, + }; +} diff --git a/packages/sdk38/src/lcdapi/base.ts b/packages/sdk38/src/lcdapi/base.ts index bcf457cf..45f46e0f 100644 --- a/packages/sdk38/src/lcdapi/base.ts +++ b/packages/sdk38/src/lcdapi/base.ts @@ -1,4 +1,3 @@ -import { Coin } from "../coins"; import { CosmosSdkTx } from "../types"; /** @@ -100,24 +99,6 @@ export interface BlockResponse { readonly block: Block; } -export interface CosmosSdkAccount { - /** Bech32 account address */ - readonly address: string; - readonly coins: readonly Coin[]; - /** Bech32 encoded pubkey */ - readonly public_key: string; - readonly account_number: number; - readonly sequence: number; -} - -export interface AuthAccountsResponse { - readonly height: string; - readonly result: { - readonly type: "cosmos-sdk/Account"; - readonly value: CosmosSdkAccount; - }; -} - export interface TxsResponse { readonly height: string; readonly txhash: string; diff --git a/packages/sdk38/src/lcdapi/index.ts b/packages/sdk38/src/lcdapi/index.ts index 2dfbd914..19876b9d 100644 --- a/packages/sdk38/src/lcdapi/index.ts +++ b/packages/sdk38/src/lcdapi/index.ts @@ -2,14 +2,14 @@ // Standard modules (see tracking issue https://github.com/CosmWasm/cosmjs/issues/276) // -export { SupplyModule, TotalSupplyAllReponse, TotalSupplyReponse } from "./supply"; +export { AuthModule, AuthAccountsResponse, setupAuthModule } from "./auth"; +export { setupSupplyModule, SupplyModule, TotalSupplyAllReponse, TotalSupplyReponse } from "./supply"; // // Base types // export { - AuthAccountsResponse, BlockResponse, BroadcastMode, EncodeTxResponse, diff --git a/packages/sdk38/src/lcdapi/lcdclient.spec.ts b/packages/sdk38/src/lcdapi/lcdclient.spec.ts index a8e0951d..7ec00c0c 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.spec.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.spec.ts @@ -21,6 +21,7 @@ import { wasmdEnabled, } from "../testutils.spec"; import { StdFee } from "../types"; +import { setupAuthModule } from "./auth"; import { TxsResponse } from "./base"; import { LcdApiArray, LcdClient, normalizeLcdApiArray } from "./lcdclient"; @@ -523,7 +524,7 @@ describe("LcdClient", () => { gas: "890000", }; - const client = new LcdClient(wasmd.endpoint); + const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); const { account_number, sequence } = (await client.authAccounts(faucet.address)).result.value; const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); @@ -576,7 +577,7 @@ describe("LcdClient", () => { gas: "890000", }; - const client = new LcdClient(wasmd.endpoint); + const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); const { account_number: an1, sequence: sequence1 } = (await client.authAccounts(address1)).result.value; const { account_number: an2, sequence: sequence2 } = (await client.authAccounts(address2)).result.value; const { account_number: an3, sequence: sequence3 } = (await client.authAccounts(address3)).result.value; @@ -641,7 +642,7 @@ describe("LcdClient", () => { gas: "890000", }; - const client = new LcdClient(wasmd.endpoint); + const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); const { account_number, sequence } = (await client.authAccounts(address1)).result.value; const signBytes = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, account_number, sequence); @@ -701,7 +702,7 @@ describe("LcdClient", () => { gas: "890000", }; - const client = new LcdClient(wasmd.endpoint); + const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); const { account_number: an1, sequence: sequence1 } = (await client.authAccounts(address1)).result.value; const { account_number: an2, sequence: sequence2 } = (await client.authAccounts(address2)).result.value; @@ -769,7 +770,7 @@ describe("LcdClient", () => { gas: "890000", }; - const client = new LcdClient(wasmd.endpoint); + const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); const { account_number: an1, sequence: sequence1 } = (await client.authAccounts(address1)).result.value; const { account_number: an2, sequence: sequence2 } = (await client.authAccounts(address2)).result.value; @@ -832,7 +833,7 @@ describe("LcdClient", () => { gas: "890000", }; - const client = new LcdClient(wasmd.endpoint); + const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); const { account_number: an1, sequence: sequence1 } = (await client.authAccounts(address1)).result.value; const { account_number: an2, sequence: sequence2 } = (await client.authAccounts(address2)).result.value; diff --git a/packages/sdk38/src/lcdapi/lcdclient.ts b/packages/sdk38/src/lcdapi/lcdclient.ts index f0889396..cbe1e4ab 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.ts @@ -4,7 +4,6 @@ import axios, { AxiosError, AxiosInstance } from "axios"; import { CosmosSdkTx, StdTx } from "../types"; import { - AuthAccountsResponse, BlockResponse, BroadcastMode, EncodeTxResponse, @@ -263,17 +262,6 @@ export class LcdClient { return data; } - // The /auth endpoints - - public async authAccounts(address: string): Promise { - const path = `/auth/accounts/${address}`; - const responseData = await this.get(path); - if (responseData.result.type !== "cosmos-sdk/Account") { - throw new Error("Unexpected response data format"); - } - return responseData as AuthAccountsResponse; - } - // The /blocks endpoints public async blocksLatest(): Promise { diff --git a/packages/sdk38/types/cosmosclient.d.ts b/packages/sdk38/types/cosmosclient.d.ts index f5f31bc6..3232ba75 100644 --- a/packages/sdk38/types/cosmosclient.d.ts +++ b/packages/sdk38/types/cosmosclient.d.ts @@ -1,5 +1,5 @@ import { Coin } from "./coins"; -import { BroadcastMode, LcdClient } from "./lcdapi"; +import { AuthModule, BroadcastMode, LcdClient } from "./lcdapi"; import { Log } from "./logs"; import { CosmosSdkTx, PubKey, StdTx } from "./types"; export interface GetNonceResult { @@ -94,10 +94,10 @@ export interface Block { } /** Use for testing only */ export interface PrivateCosmWasmClient { - readonly lcdClient: LcdClient; + readonly lcdClient: LcdClient & AuthModule; } export declare class CosmosClient { - protected readonly lcdClient: LcdClient; + protected readonly lcdClient: LcdClient & AuthModule; /** Any address the chain considers valid (valid bech32 with proper prefix) */ protected anyValidAddress: string | undefined; private chainId; diff --git a/packages/sdk38/types/index.d.ts b/packages/sdk38/types/index.d.ts index b3fd530d..d94febb4 100644 --- a/packages/sdk38/types/index.d.ts +++ b/packages/sdk38/types/index.d.ts @@ -20,6 +20,7 @@ export { export { makeSignBytes } from "./encoding"; export { AuthAccountsResponse, + AuthModule, BlockResponse, BroadcastMode, EncodeTxResponse, @@ -30,6 +31,9 @@ export { normalizeLcdApiArray, PostTxsResponse, SearchTxsResponse, + setupAuthModule, + setupSupplyModule, + SupplyModule, TxsResponse, } from "./lcdapi"; export { RestClient } from "./restclient"; diff --git a/packages/sdk38/types/lcdapi/auth.d.ts b/packages/sdk38/types/lcdapi/auth.d.ts new file mode 100644 index 00000000..ecd2020b --- /dev/null +++ b/packages/sdk38/types/lcdapi/auth.d.ts @@ -0,0 +1,22 @@ +import { Coin } from "../coins"; +import { LcdClient, LcdModule } from "./lcdclient"; +export interface CosmosSdkAccount { + /** Bech32 account address */ + readonly address: string; + readonly coins: readonly Coin[]; + /** Bech32 encoded pubkey */ + readonly public_key: string; + readonly account_number: number; + readonly sequence: number; +} +export interface AuthAccountsResponse { + readonly height: string; + readonly result: { + readonly type: "cosmos-sdk/Account"; + readonly value: CosmosSdkAccount; + }; +} +export interface AuthModule extends LcdModule { + readonly authAccounts: (address: string) => Promise; +} +export declare function setupAuthModule(base: LcdClient): AuthModule; diff --git a/packages/sdk38/types/lcdapi/auth.spec.d.ts b/packages/sdk38/types/lcdapi/auth.spec.d.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/packages/sdk38/types/lcdapi/auth.spec.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/sdk38/types/lcdapi/base.d.ts b/packages/sdk38/types/lcdapi/base.d.ts index 3f520738..d7927b0f 100644 --- a/packages/sdk38/types/lcdapi/base.d.ts +++ b/packages/sdk38/types/lcdapi/base.d.ts @@ -1,4 +1,3 @@ -import { Coin } from "../coins"; import { CosmosSdkTx } from "../types"; /** * The mode used to send transaction @@ -85,22 +84,6 @@ export interface BlockResponse { readonly block_id: BlockId; readonly block: Block; } -export interface CosmosSdkAccount { - /** Bech32 account address */ - readonly address: string; - readonly coins: readonly Coin[]; - /** Bech32 encoded pubkey */ - readonly public_key: string; - readonly account_number: number; - readonly sequence: number; -} -export interface AuthAccountsResponse { - readonly height: string; - readonly result: { - readonly type: "cosmos-sdk/Account"; - readonly value: CosmosSdkAccount; - }; -} export interface TxsResponse { readonly height: string; readonly txhash: string; diff --git a/packages/sdk38/types/lcdapi/index.d.ts b/packages/sdk38/types/lcdapi/index.d.ts index 5dd4140a..ab6fc2a3 100644 --- a/packages/sdk38/types/lcdapi/index.d.ts +++ b/packages/sdk38/types/lcdapi/index.d.ts @@ -1,6 +1,6 @@ -export { SupplyModule, TotalSupplyAllReponse, TotalSupplyReponse } from "./supply"; +export { AuthModule, AuthAccountsResponse, setupAuthModule } from "./auth"; +export { setupSupplyModule, SupplyModule, TotalSupplyAllReponse, TotalSupplyReponse } from "./supply"; export { - AuthAccountsResponse, BlockResponse, BroadcastMode, EncodeTxResponse, diff --git a/packages/sdk38/types/lcdapi/lcdclient.d.ts b/packages/sdk38/types/lcdapi/lcdclient.d.ts index 379051c7..924fc62a 100644 --- a/packages/sdk38/types/lcdapi/lcdclient.d.ts +++ b/packages/sdk38/types/lcdapi/lcdclient.d.ts @@ -1,6 +1,5 @@ import { CosmosSdkTx, StdTx } from "../types"; import { - AuthAccountsResponse, BlockResponse, BroadcastMode, EncodeTxResponse, @@ -147,7 +146,6 @@ export declare class LcdClient { constructor(apiUrl: string, broadcastMode?: BroadcastMode); get(path: string): Promise; post(path: string, params: any): Promise; - authAccounts(address: string): Promise; blocksLatest(): Promise; blocks(height: number): Promise; nodeInfo(): Promise; From ad07231bf3a9e4d69c11878e9829d0776564db13 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 11:37:31 +0200 Subject: [PATCH 23/37] Add generic restriction to LcdModuleSetup --- packages/sdk38/src/lcdapi/lcdclient.ts | 2 +- packages/sdk38/types/lcdapi/lcdclient.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sdk38/src/lcdapi/lcdclient.ts b/packages/sdk38/src/lcdapi/lcdclient.ts index cbe1e4ab..fb51c90d 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.ts @@ -22,7 +22,7 @@ export function normalizeLcdApiArray(backend: LcdApiArray): readonly T[] { export type LcdModule = Record any>; -type LcdModuleSetup = (base: LcdClient) => M; +type LcdModuleSetup = (base: LcdClient) => M; export interface LcdClientBaseOptions { readonly apiUrl: string; diff --git a/packages/sdk38/types/lcdapi/lcdclient.d.ts b/packages/sdk38/types/lcdapi/lcdclient.d.ts index 924fc62a..7774b320 100644 --- a/packages/sdk38/types/lcdapi/lcdclient.d.ts +++ b/packages/sdk38/types/lcdapi/lcdclient.d.ts @@ -12,7 +12,7 @@ import { export declare type LcdApiArray = readonly T[] | null; export declare function normalizeLcdApiArray(backend: LcdApiArray): readonly T[]; export declare type LcdModule = Record any>; -declare type LcdModuleSetup = (base: LcdClient) => M; +declare type LcdModuleSetup = (base: LcdClient) => M; export interface LcdClientBaseOptions { readonly apiUrl: string; readonly broadcastMode?: BroadcastMode; From e42620bf70471e646b4182712dc73a4a5cffa60d Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 23:38:28 +0200 Subject: [PATCH 24/37] Connect modules in properties --- packages/cosmwasm/src/lcdapi/wasm.spec.ts | 44 +++--- packages/cosmwasm/src/lcdapi/wasm.ts | 136 +++++++++--------- packages/cosmwasm/types/lcdapi/wasm.d.ts | 56 ++++---- packages/sdk38/src/cosmosclient.spec.ts | 2 +- packages/sdk38/src/cosmosclient.ts | 4 +- packages/sdk38/src/lcdapi/auth.spec.ts | 6 +- packages/sdk38/src/lcdapi/auth.ts | 20 +-- packages/sdk38/src/lcdapi/lcdclient.spec.ts | 78 +++++----- packages/sdk38/src/lcdapi/lcdclient.ts | 2 +- packages/sdk38/src/lcdapi/supply.spec.ts | 8 +- packages/sdk38/src/lcdapi/supply.ts | 18 ++- .../sdk38/src/signingcosmosclient.spec.ts | 2 +- packages/sdk38/types/lcdapi/auth.d.ts | 4 +- packages/sdk38/types/lcdapi/lcdclient.d.ts | 2 +- packages/sdk38/types/lcdapi/supply.d.ts | 6 +- 15 files changed, 202 insertions(+), 186 deletions(-) diff --git a/packages/cosmwasm/src/lcdapi/wasm.spec.ts b/packages/cosmwasm/src/lcdapi/wasm.spec.ts index 183d086a..9ec247ed 100644 --- a/packages/cosmwasm/src/lcdapi/wasm.spec.ts +++ b/packages/cosmwasm/src/lcdapi/wasm.spec.ts @@ -70,7 +70,7 @@ async function uploadContract( gas: "89000000", }; - const { account_number, sequence } = (await client.authAccounts(alice.address0)).result.value; + const { account_number, sequence } = (await client.auth.account(alice.address0)).result.value; const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); const signature = await pen.sign(signBytes); const signedTx = makeSignedTx(theMsg, fee, memo, signature); @@ -108,7 +108,7 @@ async function instantiateContract( gas: "89000000", }; - const { account_number, sequence } = (await client.authAccounts(alice.address0)).result.value; + const { account_number, sequence } = (await client.auth.account(alice.address0)).result.value; const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); const signature = await pen.sign(signBytes); const signedTx = makeSignedTx(theMsg, fee, memo, signature); @@ -136,7 +136,7 @@ async function executeContract( gas: "89000000", }; - const { account_number, sequence } = (await client.authAccounts(alice.address0)).result.value; + const { account_number, sequence } = (await client.auth.account(alice.address0)).result.value; const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); const signature = await pen.sign(signBytes); const signedTx = makeSignedTx(theMsg, fee, memo, signature); @@ -291,7 +291,7 @@ describe("wasm", () => { expect(amountAttr.value).toEqual("1234ucosm,321ustake"); expect(result.data).toEqual(toHex(Bech32.decode(contractAddress).data).toUpperCase()); - const balance = (await client.authAccounts(contractAddress)).result.value.coins; + const balance = (await client.auth.account(contractAddress)).result.value.coins; expect(balance).toEqual(transferAmount); } @@ -311,9 +311,9 @@ describe("wasm", () => { }); // Verify token transfer from contract to beneficiary - const beneficiaryBalance = (await client.authAccounts(beneficiaryAddress)).result.value.coins; + const beneficiaryBalance = (await client.auth.account(beneficiaryAddress)).result.value.coins; expect(beneficiaryBalance).toEqual(transferAmount); - const contractBalance = (await client.authAccounts(contractAddress)).result.value.coins; + const contractBalance = (await client.auth.account(contractAddress)).result.value.coins; expect(contractBalance).toEqual([]); } }); @@ -328,7 +328,7 @@ describe("wasm", () => { const client = makeWasmClient(wasmd.endpoint); // check with contracts were here first to compare - const existingInfos = await client.listCodeInfo(); + const existingInfos = await client.wasm.listCodeInfo(); existingInfos.forEach((val, idx) => expect(val.id).toEqual(idx + 1)); const numExisting = existingInfos.length; @@ -341,7 +341,7 @@ describe("wasm", () => { const codeId = Number.parseInt(codeIdAttr.value, 10); // ensure we were added to the end of the list - const newInfos = await client.listCodeInfo(); + const newInfos = await client.wasm.listCodeInfo(); expect(newInfos.length).toEqual(numExisting + 1); const lastInfo = newInfos[newInfos.length - 1]; expect(lastInfo.id).toEqual(codeId); @@ -356,7 +356,7 @@ describe("wasm", () => { expect(lastInfo.data_hash.toLowerCase()).toEqual(toHex(wasmHash)); // download code and check against auto-gen - const { data } = await client.getCode(codeId); + const { data } = await client.wasm.getCode(codeId); expect(fromBase64(data)).toEqual(hackatom.data); }); @@ -374,7 +374,7 @@ describe("wasm", () => { // reuse an existing contract, or upload if needed let codeId: number; - const existingInfos = await client.listCodeInfo(); + const existingInfos = await client.wasm.listCodeInfo(); if (existingInfos.length > 0) { codeId = existingInfos[existingInfos.length - 1].id; } else { @@ -386,7 +386,7 @@ describe("wasm", () => { } // create new instance and compare before and after - const existingContractsByCode = await client.listContractsByCodeId(codeId); + const existingContractsByCode = await client.wasm.listContractsByCodeId(codeId); for (const contract of existingContractsByCode) { expect(contract.address).toMatch(bech32AddressMatcher); expect(contract.code_id).toEqual(codeId); @@ -400,7 +400,7 @@ describe("wasm", () => { const contractAddressAttr = findAttribute(logs, "message", "contract_address"); const myAddress = contractAddressAttr.value; - const newContractsByCode = await client.listContractsByCodeId(codeId); + const newContractsByCode = await client.wasm.listContractsByCodeId(codeId); expect(newContractsByCode.length).toEqual(existingContractsByCode.length + 1); const newContract = newContractsByCode[newContractsByCode.length - 1]; expect(newContract).toEqual( @@ -412,7 +412,7 @@ describe("wasm", () => { ); // check out info - const myInfo = await client.getContractInfo(myAddress); + const myInfo = await client.wasm.getContractInfo(myAddress); assert(myInfo); expect(myInfo).toEqual( jasmine.objectContaining({ @@ -427,7 +427,7 @@ describe("wasm", () => { // make sure random addresses don't give useful info const nonExistentAddress = makeRandomAddress(); - expect(await client.getContractInfo(nonExistentAddress)).toBeNull(); + expect(await client.wasm.getContractInfo(nonExistentAddress)).toBeNull(); }); describe("contract state", () => { @@ -455,7 +455,7 @@ describe("wasm", () => { pendingWithoutWasmd(); // get contract state - const state = await client.getAllContractState(contractAddress!); + const state = await client.wasm.getAllContractState(contractAddress!); expect(state.length).toEqual(1); const data = state[0]; expect(data.key).toEqual(expectedKey); @@ -464,7 +464,7 @@ describe("wasm", () => { expect(value.beneficiary).toBeDefined(); // bad address is empty array - const noContractState = await client.getAllContractState(noContract); + const noContractState = await client.wasm.getAllContractState(noContract); expect(noContractState).toEqual([]); }); @@ -472,18 +472,18 @@ describe("wasm", () => { pendingWithoutWasmd(); // query by one key - const raw = await client.queryContractRaw(contractAddress!, expectedKey); + const raw = await client.wasm.queryContractRaw(contractAddress!, expectedKey); assert(raw, "must get result"); const model = JSON.parse(fromAscii(raw)); expect(model.verifier).toBeDefined(); expect(model.beneficiary).toBeDefined(); // missing key is null - const missing = await client.queryContractRaw(contractAddress!, fromHex("cafe0dad")); + const missing = await client.wasm.queryContractRaw(contractAddress!, fromHex("cafe0dad")); expect(missing).toBeNull(); // bad address is null - const noContractModel = await client.queryContractRaw(noContract, expectedKey); + const noContractModel = await client.wasm.queryContractRaw(noContract, expectedKey); expect(noContractModel).toBeNull(); }); @@ -491,18 +491,18 @@ describe("wasm", () => { pendingWithoutWasmd(); // we can query the verifier properly - const resultDocument = await client.queryContractSmart(contractAddress!, { verifier: {} }); + const resultDocument = await client.wasm.queryContractSmart(contractAddress!, { verifier: {} }); expect(resultDocument).toEqual({ verifier: alice.address0 }); // invalid query syntax throws an error - await client.queryContractSmart(contractAddress!, { nosuchkey: {} }).then( + await client.wasm.queryContractSmart(contractAddress!, { nosuchkey: {} }).then( () => fail("shouldn't succeed"), (error) => expect(error).toMatch(/query wasm contract failed: parsing hackatom::contract::QueryMsg/), ); // invalid address throws an error - await client.queryContractSmart(noContract, { verifier: {} }).then( + await client.wasm.queryContractSmart(noContract, { verifier: {} }).then( () => fail("shouldn't succeed"), (error) => expect(error).toMatch("not found"), ); diff --git a/packages/cosmwasm/src/lcdapi/wasm.ts b/packages/cosmwasm/src/lcdapi/wasm.ts index 538f4baa..5e7287d4 100644 --- a/packages/cosmwasm/src/lcdapi/wasm.ts +++ b/packages/cosmwasm/src/lcdapi/wasm.ts @@ -76,82 +76,86 @@ function unwrapWasmResponse(response: WasmResponse): T { * @see https://github.com/cosmwasm/wasmd/blob/master/x/wasm/client/rest/query.go#L19-L27 */ export interface WasmModule extends LcdModule { - readonly listCodeInfo: () => Promise; + readonly wasm: { + readonly listCodeInfo: () => Promise; - /** - * Downloads the original wasm bytecode by code ID. - * - * Throws an error if no code with this id - */ - readonly getCode: (id: number) => Promise; + /** + * Downloads the original wasm bytecode by code ID. + * + * Throws an error if no code with this id + */ + readonly getCode: (id: number) => Promise; - readonly listContractsByCodeId: (id: number) => Promise; + readonly listContractsByCodeId: (id: number) => Promise; - /** - * Returns null when contract was not found at this address. - */ - readonly getContractInfo: (address: string) => Promise; + /** + * Returns null when contract was not found at this address. + */ + readonly getContractInfo: (address: string) => Promise; - /** - * Returns all contract state. - * This is an empty array if no such contract, or contract has no data. - */ - readonly getAllContractState: (address: string) => Promise; + /** + * Returns all contract state. + * This is an empty array if no such contract, or contract has no data. + */ + readonly getAllContractState: (address: string) => Promise; - /** - * Returns the data at the key if present (unknown decoded json), - * or null if no data at this (contract address, key) pair - */ - readonly queryContractRaw: (address: string, key: Uint8Array) => Promise; + /** + * Returns the data at the key if present (unknown decoded json), + * or null if no data at this (contract address, key) pair + */ + readonly queryContractRaw: (address: string, key: Uint8Array) => Promise; - /** - * Makes a smart query on the contract and parses the reponse as JSON. - * Throws error if no such contract exists, the query format is invalid or the response is invalid. - */ - readonly queryContractSmart: (address: string, query: object) => Promise; + /** + * Makes a smart query on the contract and parses the reponse as JSON. + * Throws error if no such contract exists, the query format is invalid or the response is invalid. + */ + readonly queryContractSmart: (address: string, query: object) => Promise; + }; } export function setupWasmModule(base: LcdClient): WasmModule { return { - listCodeInfo: async () => { - const path = `/wasm/code`; - const responseData = (await base.get(path)) as WasmResponse>; - return normalizeLcdApiArray(unwrapWasmResponse(responseData)); - }, - getCode: async (id: number) => { - const path = `/wasm/code/${id}`; - const responseData = (await base.get(path)) as WasmResponse; - return unwrapWasmResponse(responseData); - }, - listContractsByCodeId: async (id: number) => { - const path = `/wasm/code/${id}/contracts`; - const responseData = (await base.get(path)) as WasmResponse>; - return normalizeLcdApiArray(unwrapWasmResponse(responseData)); - }, - getContractInfo: async (address: string) => { - const path = `/wasm/contract/${address}`; - const response = (await base.get(path)) as WasmResponse; - return unwrapWasmResponse(response); - }, - getAllContractState: async (address: string) => { - const path = `/wasm/contract/${address}/state`; - const responseData = (await base.get(path)) as WasmResponse>; - return normalizeLcdApiArray(unwrapWasmResponse(responseData)).map(parseWasmData); - }, - queryContractRaw: async (address: string, key: Uint8Array) => { - const hexKey = toHex(key); - const path = `/wasm/contract/${address}/raw/${hexKey}?encoding=hex`; - const responseData = (await base.get(path)) as WasmResponse; - const data = unwrapWasmResponse(responseData); - return data.length === 0 ? null : fromBase64(data[0].val); - }, - queryContractSmart: async (address: string, query: object) => { - const encoded = toHex(toUtf8(JSON.stringify(query))); - const path = `/wasm/contract/${address}/smart/${encoded}?encoding=hex`; - const responseData = (await base.get(path)) as WasmResponse; - const result = unwrapWasmResponse(responseData); - // By convention, smart queries must return a valid JSON document (see https://github.com/CosmWasm/cosmwasm/issues/144) - return JSON.parse(fromUtf8(fromBase64(result.smart))); + wasm: { + listCodeInfo: async () => { + const path = `/wasm/code`; + const responseData = (await base.get(path)) as WasmResponse>; + return normalizeLcdApiArray(unwrapWasmResponse(responseData)); + }, + getCode: async (id: number) => { + const path = `/wasm/code/${id}`; + const responseData = (await base.get(path)) as WasmResponse; + return unwrapWasmResponse(responseData); + }, + listContractsByCodeId: async (id: number) => { + const path = `/wasm/code/${id}/contracts`; + const responseData = (await base.get(path)) as WasmResponse>; + return normalizeLcdApiArray(unwrapWasmResponse(responseData)); + }, + getContractInfo: async (address: string) => { + const path = `/wasm/contract/${address}`; + const response = (await base.get(path)) as WasmResponse; + return unwrapWasmResponse(response); + }, + getAllContractState: async (address: string) => { + const path = `/wasm/contract/${address}/state`; + const responseData = (await base.get(path)) as WasmResponse>; + return normalizeLcdApiArray(unwrapWasmResponse(responseData)).map(parseWasmData); + }, + queryContractRaw: async (address: string, key: Uint8Array) => { + const hexKey = toHex(key); + const path = `/wasm/contract/${address}/raw/${hexKey}?encoding=hex`; + const responseData = (await base.get(path)) as WasmResponse; + const data = unwrapWasmResponse(responseData); + return data.length === 0 ? null : fromBase64(data[0].val); + }, + queryContractSmart: async (address: string, query: object) => { + const encoded = toHex(toUtf8(JSON.stringify(query))); + const path = `/wasm/contract/${address}/smart/${encoded}?encoding=hex`; + const responseData = (await base.get(path)) as WasmResponse; + const result = unwrapWasmResponse(responseData); + // By convention, smart queries must return a valid JSON document (see https://github.com/CosmWasm/cosmwasm/issues/144) + return JSON.parse(fromUtf8(fromBase64(result.smart))); + }, }, }; } diff --git a/packages/cosmwasm/types/lcdapi/wasm.d.ts b/packages/cosmwasm/types/lcdapi/wasm.d.ts index 3e4d67b8..ed6a6576 100644 --- a/packages/cosmwasm/types/lcdapi/wasm.d.ts +++ b/packages/cosmwasm/types/lcdapi/wasm.d.ts @@ -41,32 +41,34 @@ export interface ContractDetails extends ContractInfo { * @see https://github.com/cosmwasm/wasmd/blob/master/x/wasm/client/rest/query.go#L19-L27 */ export interface WasmModule extends LcdModule { - readonly listCodeInfo: () => Promise; - /** - * Downloads the original wasm bytecode by code ID. - * - * Throws an error if no code with this id - */ - readonly getCode: (id: number) => Promise; - readonly listContractsByCodeId: (id: number) => Promise; - /** - * Returns null when contract was not found at this address. - */ - readonly getContractInfo: (address: string) => Promise; - /** - * Returns all contract state. - * This is an empty array if no such contract, or contract has no data. - */ - readonly getAllContractState: (address: string) => Promise; - /** - * Returns the data at the key if present (unknown decoded json), - * or null if no data at this (contract address, key) pair - */ - readonly queryContractRaw: (address: string, key: Uint8Array) => Promise; - /** - * Makes a smart query on the contract and parses the reponse as JSON. - * Throws error if no such contract exists, the query format is invalid or the response is invalid. - */ - readonly queryContractSmart: (address: string, query: object) => Promise; + readonly wasm: { + readonly listCodeInfo: () => Promise; + /** + * Downloads the original wasm bytecode by code ID. + * + * Throws an error if no code with this id + */ + readonly getCode: (id: number) => Promise; + readonly listContractsByCodeId: (id: number) => Promise; + /** + * Returns null when contract was not found at this address. + */ + readonly getContractInfo: (address: string) => Promise; + /** + * Returns all contract state. + * This is an empty array if no such contract, or contract has no data. + */ + readonly getAllContractState: (address: string) => Promise; + /** + * Returns the data at the key if present (unknown decoded json), + * or null if no data at this (contract address, key) pair + */ + readonly queryContractRaw: (address: string, key: Uint8Array) => Promise; + /** + * Makes a smart query on the contract and parses the reponse as JSON. + * Throws error if no such contract exists, the query format is invalid or the response is invalid. + */ + readonly queryContractSmart: (address: string, query: object) => Promise; + }; } export declare function setupWasmModule(base: LcdClient): WasmModule; diff --git a/packages/sdk38/src/cosmosclient.spec.ts b/packages/sdk38/src/cosmosclient.spec.ts index d05ddd94..64d9806d 100644 --- a/packages/sdk38/src/cosmosclient.spec.ts +++ b/packages/sdk38/src/cosmosclient.spec.ts @@ -75,7 +75,7 @@ describe("CosmosClient", () => { const openedClient = (client as unknown) as PrivateCosmWasmClient; const blockLatestSpy = spyOn(openedClient.lcdClient, "blocksLatest").and.callThrough(); - const authAccountsSpy = spyOn(openedClient.lcdClient, "authAccounts").and.callThrough(); + const authAccountsSpy = spyOn(openedClient.lcdClient.auth, "account").and.callThrough(); const height1 = await client.getHeight(); expect(height1).toBeGreaterThan(0); diff --git a/packages/sdk38/src/cosmosclient.ts b/packages/sdk38/src/cosmosclient.ts index bcdfdc63..71dc8be2 100644 --- a/packages/sdk38/src/cosmosclient.ts +++ b/packages/sdk38/src/cosmosclient.ts @@ -166,7 +166,7 @@ export class CosmosClient { public async getHeight(): Promise { if (this.anyValidAddress) { - const { height } = await this.lcdClient.authAccounts(this.anyValidAddress); + const { height } = await this.lcdClient.auth.account(this.anyValidAddress); return parseInt(height, 10); } else { // Note: this gets inefficient when blocks contain a lot of transactions since it @@ -207,7 +207,7 @@ export class CosmosClient { } public async getAccount(address: string): Promise { - const account = await this.lcdClient.authAccounts(address); + const account = await this.lcdClient.auth.account(address); const value = account.result.value; if (value.address === "") { return undefined; diff --git a/packages/sdk38/src/lcdapi/auth.spec.ts b/packages/sdk38/src/lcdapi/auth.spec.ts index 2aabe9c1..633c2f00 100644 --- a/packages/sdk38/src/lcdapi/auth.spec.ts +++ b/packages/sdk38/src/lcdapi/auth.spec.ts @@ -19,7 +19,7 @@ describe("auth", () => { it("works for unused account without pubkey", async () => { pendingWithoutWasmd(); const client = makeAuthClient(wasmd.endpoint); - const { height, result } = await client.authAccounts(unused.address); + const { height, result } = await client.auth.account(unused.address); expect(height).toMatch(nonNegativeIntegerMatcher); expect(result).toEqual({ type: "cosmos-sdk/Account", @@ -46,7 +46,7 @@ describe("auth", () => { it("has correct pubkey for faucet", async () => { pendingWithoutWasmd(); const client = makeAuthClient(wasmd.endpoint); - const { result } = await client.authAccounts(faucet.address); + const { result } = await client.auth.account(faucet.address); expect(result.value).toEqual( jasmine.objectContaining({ public_key: encodeBech32Pubkey(faucet.pubkey, "cosmospub"), @@ -59,7 +59,7 @@ describe("auth", () => { pendingWithoutWasmd(); const client = makeAuthClient(wasmd.endpoint); const nonExistentAccount = makeRandomAddress(); - const { result } = await client.authAccounts(nonExistentAccount); + const { result } = await client.auth.account(nonExistentAccount); expect(result).toEqual({ type: "cosmos-sdk/Account", value: jasmine.objectContaining({ address: "" }), diff --git a/packages/sdk38/src/lcdapi/auth.ts b/packages/sdk38/src/lcdapi/auth.ts index 497f834f..817c4b46 100644 --- a/packages/sdk38/src/lcdapi/auth.ts +++ b/packages/sdk38/src/lcdapi/auth.ts @@ -20,18 +20,22 @@ export interface AuthAccountsResponse { } export interface AuthModule extends LcdModule { - readonly authAccounts: (address: string) => Promise; + readonly auth: { + readonly account: (address: string) => Promise; + }; } export function setupAuthModule(base: LcdClient): AuthModule { return { - authAccounts: async (address: string) => { - const path = `/auth/accounts/${address}`; - const responseData = await base.get(path); - if (responseData.result.type !== "cosmos-sdk/Account") { - throw new Error("Unexpected response data format"); - } - return responseData as AuthAccountsResponse; + auth: { + account: async (address: string) => { + const path = `/auth/accounts/${address}`; + const responseData = await base.get(path); + if (responseData.result.type !== "cosmos-sdk/Account") { + throw new Error("Unexpected response data format"); + } + return responseData as AuthAccountsResponse; + }, }, }; } diff --git a/packages/sdk38/src/lcdapi/lcdclient.spec.ts b/packages/sdk38/src/lcdapi/lcdclient.spec.ts index 7ec00c0c..6ea7e05a 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.spec.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.spec.ts @@ -23,7 +23,7 @@ import { import { StdFee } from "../types"; import { setupAuthModule } from "./auth"; import { TxsResponse } from "./base"; -import { LcdApiArray, LcdClient, normalizeLcdApiArray } from "./lcdclient"; +import { LcdApiArray, LcdClient, LcdModule, normalizeLcdApiArray } from "./lcdclient"; /** Deployed as part of scripts/wasmd/init.sh */ export const deployedErc20 = { @@ -79,8 +79,22 @@ describe("LcdClient", () => { return response.result; } - interface WasmModule extends Record any> { - listCodeInfo: () => Promise; + interface WasmModule extends LcdModule { + wasm: { + listCodeInfo: () => Promise; + }; + } + + function setupWasmModule(base: LcdClient): WasmModule { + return { + wasm: { + listCodeInfo: async (): Promise => { + const path = `/wasm/code`; + const responseData = (await base.get(path)) as WasmResponse>; + return normalizeLcdApiArray(unwrapWasmResponse(responseData)); + }, + }, + }; } it("works for no modules", async () => { @@ -90,18 +104,9 @@ describe("LcdClient", () => { it("works for one module", async () => { pendingWithoutWasmd(); - function wasmClientRegisterer(base: LcdClient): WasmModule { - return { - listCodeInfo: async (): Promise => { - const path = `/wasm/code`; - const responseData = (await base.get(path)) as WasmResponse>; - return normalizeLcdApiArray(unwrapWasmResponse(responseData)); - }, - }; - } - const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, wasmClientRegisterer); - const codes = await client.listCodeInfo(); + const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupWasmModule); + const codes = await client.wasm.listCodeInfo(); expect(codes.length).toBeGreaterThanOrEqual(3); expect(codes[0].id).toEqual(deployedErc20.codeId); expect(codes[0].data_hash).toEqual(deployedErc20.checksum.toUpperCase()); @@ -111,15 +116,6 @@ describe("LcdClient", () => { it("works for two modules", async () => { pendingWithoutWasmd(); - function registerWasmModule(base: LcdClient): WasmModule { - return { - listCodeInfo: async (): Promise => { - const path = `/wasm/code`; - const responseData = (await base.get(path)) as WasmResponse>; - return normalizeLcdApiArray(unwrapWasmResponse(responseData)); - }, - }; - } interface TotalSupplyAllReponse { readonly height: string; @@ -129,21 +125,23 @@ describe("LcdClient", () => { // eslint-disable-next-line @typescript-eslint/explicit-function-return-type function setupSupplyModule(base: LcdClient) { return { - totalSupplyAll: async (): Promise => { - const path = `/supply/total`; - return (await base.get(path)) as TotalSupplyAllReponse; + supply: { + totalAll: async (): Promise => { + const path = `/supply/total`; + return (await base.get(path)) as TotalSupplyAllReponse; + }, }, }; } - const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, registerWasmModule, setupSupplyModule); - const codes = await client.listCodeInfo(); + const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupWasmModule, setupSupplyModule); + const codes = await client.wasm.listCodeInfo(); expect(codes.length).toBeGreaterThanOrEqual(3); expect(codes[0].id).toEqual(deployedErc20.codeId); expect(codes[0].data_hash).toEqual(deployedErc20.checksum.toUpperCase()); expect(codes[0].builder).toEqual(deployedErc20.builder); expect(codes[0].source).toEqual(deployedErc20.source); - const supply = await client.totalSupplyAll(); + const supply = await client.supply.totalAll(); expect(supply).toEqual({ height: jasmine.stringMatching(/^[0-9]+$/), result: [ @@ -525,7 +523,7 @@ describe("LcdClient", () => { }; const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); - const { account_number, sequence } = (await client.authAccounts(faucet.address)).result.value; + const { account_number, sequence } = (await client.auth.account(faucet.address)).result.value; const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); const signature = await pen.sign(signBytes); @@ -578,9 +576,9 @@ describe("LcdClient", () => { }; const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); - const { account_number: an1, sequence: sequence1 } = (await client.authAccounts(address1)).result.value; - const { account_number: an2, sequence: sequence2 } = (await client.authAccounts(address2)).result.value; - const { account_number: an3, sequence: sequence3 } = (await client.authAccounts(address3)).result.value; + const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value; + const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value; + const { account_number: an3, sequence: sequence3 } = (await client.auth.account(address3)).result.value; const signBytes1 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an1, sequence1); const signBytes2 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an2, sequence2); @@ -643,7 +641,7 @@ describe("LcdClient", () => { }; const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); - const { account_number, sequence } = (await client.authAccounts(address1)).result.value; + const { account_number, sequence } = (await client.auth.account(address1)).result.value; const signBytes = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, account_number, sequence); const signature1 = await account1.sign(signBytes); @@ -703,8 +701,8 @@ describe("LcdClient", () => { }; const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); - const { account_number: an1, sequence: sequence1 } = (await client.authAccounts(address1)).result.value; - const { account_number: an2, sequence: sequence2 } = (await client.authAccounts(address2)).result.value; + const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value; + const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value; const signBytes1 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1); const signBytes2 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2); @@ -771,8 +769,8 @@ describe("LcdClient", () => { }; const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); - const { account_number: an1, sequence: sequence1 } = (await client.authAccounts(address1)).result.value; - const { account_number: an2, sequence: sequence2 } = (await client.authAccounts(address2)).result.value; + const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value; + const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value; const signBytes1 = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, an1, sequence1); const signBytes2 = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, an2, sequence2); @@ -834,8 +832,8 @@ describe("LcdClient", () => { }; const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); - const { account_number: an1, sequence: sequence1 } = (await client.authAccounts(address1)).result.value; - const { account_number: an2, sequence: sequence2 } = (await client.authAccounts(address2)).result.value; + const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value; + const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value; const signBytes1 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1); const signBytes2 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2); diff --git a/packages/sdk38/src/lcdapi/lcdclient.ts b/packages/sdk38/src/lcdapi/lcdclient.ts index fb51c90d..b5406c6e 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.ts @@ -20,7 +20,7 @@ export function normalizeLcdApiArray(backend: LcdApiArray): readonly T[] { return backend || []; } -export type LcdModule = Record any>; +export type LcdModule = Record any>>; type LcdModuleSetup = (base: LcdClient) => M; diff --git a/packages/sdk38/src/lcdapi/supply.spec.ts b/packages/sdk38/src/lcdapi/supply.spec.ts index 3d905fc9..59b03009 100644 --- a/packages/sdk38/src/lcdapi/supply.spec.ts +++ b/packages/sdk38/src/lcdapi/supply.spec.ts @@ -3,12 +3,12 @@ import { LcdClient } from "./lcdclient"; import { setupSupplyModule } from "./supply"; describe("supply", () => { - describe("totalSupplyAll", () => { + describe("totalAll", () => { it("works", async () => { pendingWithoutWasmd(); const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupSupplyModule); - const supply = await client.totalSupplyAll(); + const supply = await client.supply.totalAll(); expect(supply).toEqual({ height: jasmine.stringMatching(/^[0-9]+$/), result: [ @@ -25,12 +25,12 @@ describe("supply", () => { }); }); - describe("totalSupply", () => { + describe("total", () => { it("works", async () => { pendingWithoutWasmd(); const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupSupplyModule); - const supply = await client.totalSupply("ucosm"); + const supply = await client.supply.total("ucosm"); expect(supply).toEqual({ height: jasmine.stringMatching(/^[0-9]+$/), result: jasmine.stringMatching(/^[0-9]+$/), diff --git a/packages/sdk38/src/lcdapi/supply.ts b/packages/sdk38/src/lcdapi/supply.ts index 081310ad..efbd6833 100644 --- a/packages/sdk38/src/lcdapi/supply.ts +++ b/packages/sdk38/src/lcdapi/supply.ts @@ -13,17 +13,21 @@ export interface TotalSupplyReponse { } export interface SupplyModule extends LcdModule { - readonly totalSupplyAll: () => Promise; - readonly totalSupply: (denom: string) => Promise; + readonly supply: { + readonly totalAll: () => Promise; + readonly total: (denom: string) => Promise; + }; } export function setupSupplyModule(base: LcdClient): SupplyModule { return { - totalSupplyAll: async () => { - return base.get(`/supply/total`); - }, - totalSupply: async (denom: string) => { - return base.get(`/supply/total/${denom}`); + supply: { + totalAll: async () => { + return base.get(`/supply/total`); + }, + total: async (denom: string) => { + return base.get(`/supply/total/${denom}`); + }, }, }; } diff --git a/packages/sdk38/src/signingcosmosclient.spec.ts b/packages/sdk38/src/signingcosmosclient.spec.ts index 5ad36ce6..ece3473c 100644 --- a/packages/sdk38/src/signingcosmosclient.spec.ts +++ b/packages/sdk38/src/signingcosmosclient.spec.ts @@ -35,7 +35,7 @@ describe("SigningCosmosClient", () => { const openedClient = (client as unknown) as PrivateCosmWasmClient; const blockLatestSpy = spyOn(openedClient.lcdClient, "blocksLatest").and.callThrough(); - const authAccountsSpy = spyOn(openedClient.lcdClient, "authAccounts").and.callThrough(); + const authAccountsSpy = spyOn(openedClient.lcdClient.auth, "account").and.callThrough(); const height = await client.getHeight(); expect(height).toBeGreaterThan(0); diff --git a/packages/sdk38/types/lcdapi/auth.d.ts b/packages/sdk38/types/lcdapi/auth.d.ts index ecd2020b..19945fc7 100644 --- a/packages/sdk38/types/lcdapi/auth.d.ts +++ b/packages/sdk38/types/lcdapi/auth.d.ts @@ -17,6 +17,8 @@ export interface AuthAccountsResponse { }; } export interface AuthModule extends LcdModule { - readonly authAccounts: (address: string) => Promise; + readonly auth: { + readonly account: (address: string) => Promise; + }; } export declare function setupAuthModule(base: LcdClient): AuthModule; diff --git a/packages/sdk38/types/lcdapi/lcdclient.d.ts b/packages/sdk38/types/lcdapi/lcdclient.d.ts index 7774b320..f0aaaeef 100644 --- a/packages/sdk38/types/lcdapi/lcdclient.d.ts +++ b/packages/sdk38/types/lcdapi/lcdclient.d.ts @@ -11,7 +11,7 @@ import { /** Unfortunately, Cosmos SDK encodes empty arrays as null */ export declare type LcdApiArray = readonly T[] | null; export declare function normalizeLcdApiArray(backend: LcdApiArray): readonly T[]; -export declare type LcdModule = Record any>; +export declare type LcdModule = Record any>>; declare type LcdModuleSetup = (base: LcdClient) => M; export interface LcdClientBaseOptions { readonly apiUrl: string; diff --git a/packages/sdk38/types/lcdapi/supply.d.ts b/packages/sdk38/types/lcdapi/supply.d.ts index 90186538..f5ce19e2 100644 --- a/packages/sdk38/types/lcdapi/supply.d.ts +++ b/packages/sdk38/types/lcdapi/supply.d.ts @@ -10,7 +10,9 @@ export interface TotalSupplyReponse { readonly result: string; } export interface SupplyModule extends LcdModule { - readonly totalSupplyAll: () => Promise; - readonly totalSupply: (denom: string) => Promise; + readonly supply: { + readonly totalAll: () => Promise; + readonly total: (denom: string) => Promise; + }; } export declare function setupSupplyModule(base: LcdClient): SupplyModule; From f047df1af0d9a933b8749cd5523f1b77798cb601 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 23:51:59 +0200 Subject: [PATCH 25/37] Rename *Module to *Extension --- packages/cosmwasm/src/lcdapi/wasm.spec.ts | 10 +- packages/cosmwasm/src/lcdapi/wasm.ts | 6 +- packages/cosmwasm/types/lcdapi/wasm.d.ts | 6 +- packages/sdk38/src/cosmosclient.ts | 11 +- packages/sdk38/src/index.ts | 10 +- packages/sdk38/src/lcdapi/auth.spec.ts | 8 +- packages/sdk38/src/lcdapi/auth.ts | 6 +- packages/sdk38/src/lcdapi/index.ts | 6 +- packages/sdk38/src/lcdapi/lcdclient.spec.ts | 30 ++-- packages/sdk38/src/lcdapi/lcdclient.ts | 190 ++++++++++---------- packages/sdk38/src/lcdapi/supply.spec.ts | 8 +- packages/sdk38/src/lcdapi/supply.ts | 6 +- packages/sdk38/types/cosmosclient.d.ts | 6 +- packages/sdk38/types/index.d.ts | 10 +- packages/sdk38/types/lcdapi/auth.d.ts | 6 +- packages/sdk38/types/lcdapi/index.d.ts | 6 +- packages/sdk38/types/lcdapi/lcdclient.d.ts | 151 ++++++++-------- packages/sdk38/types/lcdapi/supply.d.ts | 6 +- 18 files changed, 247 insertions(+), 235 deletions(-) diff --git a/packages/cosmwasm/src/lcdapi/wasm.spec.ts b/packages/cosmwasm/src/lcdapi/wasm.spec.ts index 9ec247ed..596ad3e0 100644 --- a/packages/cosmwasm/src/lcdapi/wasm.spec.ts +++ b/packages/cosmwasm/src/lcdapi/wasm.spec.ts @@ -2,7 +2,7 @@ import { Sha256 } from "@cosmjs/crypto"; import { Bech32, fromAscii, fromBase64, fromHex, toAscii, toBase64, toHex } from "@cosmjs/encoding"; import { - AuthModule, + AuthExtension, Coin, coin, coins, @@ -11,7 +11,7 @@ import { Pen, PostTxsResponse, Secp256k1Pen, - setupAuthModule, + setupAuthExtension, StdFee, } from "@cosmjs/sdk38"; import { assert } from "@cosmjs/utils"; @@ -37,12 +37,12 @@ import { wasmd, wasmdEnabled, } from "../testutils.spec"; -import { setupWasmModule, WasmModule } from "./wasm"; +import { setupWasmModule, WasmExtension } from "./wasm"; -type WasmClient = LcdClient & AuthModule & WasmModule; +type WasmClient = LcdClient & AuthExtension & WasmExtension; function makeWasmClient(apiUrl: string): WasmClient { - return LcdClient.withModules({ apiUrl }, setupAuthModule, setupWasmModule); + return LcdClient.withExtensions({ apiUrl }, setupAuthExtension, setupWasmModule); } async function uploadContract( diff --git a/packages/cosmwasm/src/lcdapi/wasm.ts b/packages/cosmwasm/src/lcdapi/wasm.ts index 5e7287d4..6c836744 100644 --- a/packages/cosmwasm/src/lcdapi/wasm.ts +++ b/packages/cosmwasm/src/lcdapi/wasm.ts @@ -1,5 +1,5 @@ import { fromBase64, fromUtf8, toHex, toUtf8 } from "@cosmjs/encoding"; -import { LcdApiArray, LcdClient, LcdModule, normalizeLcdApiArray } from "@cosmjs/sdk38"; +import { LcdApiArray, LcdClient, LcdExtension, normalizeLcdApiArray } from "@cosmjs/sdk38"; import { JsonObject, Model, parseWasmData, WasmData } from "../types"; @@ -75,7 +75,7 @@ function unwrapWasmResponse(response: WasmResponse): T { /** * @see https://github.com/cosmwasm/wasmd/blob/master/x/wasm/client/rest/query.go#L19-L27 */ -export interface WasmModule extends LcdModule { +export interface WasmExtension extends LcdExtension { readonly wasm: { readonly listCodeInfo: () => Promise; @@ -113,7 +113,7 @@ export interface WasmModule extends LcdModule { }; } -export function setupWasmModule(base: LcdClient): WasmModule { +export function setupWasmModule(base: LcdClient): WasmExtension { return { wasm: { listCodeInfo: async () => { diff --git a/packages/cosmwasm/types/lcdapi/wasm.d.ts b/packages/cosmwasm/types/lcdapi/wasm.d.ts index ed6a6576..5e454691 100644 --- a/packages/cosmwasm/types/lcdapi/wasm.d.ts +++ b/packages/cosmwasm/types/lcdapi/wasm.d.ts @@ -1,4 +1,4 @@ -import { LcdClient, LcdModule } from "@cosmjs/sdk38"; +import { LcdClient, LcdExtension } from "@cosmjs/sdk38"; import { JsonObject, Model } from "../types"; export interface CodeInfo { readonly id: number; @@ -40,7 +40,7 @@ export interface ContractDetails extends ContractInfo { /** * @see https://github.com/cosmwasm/wasmd/blob/master/x/wasm/client/rest/query.go#L19-L27 */ -export interface WasmModule extends LcdModule { +export interface WasmExtension extends LcdExtension { readonly wasm: { readonly listCodeInfo: () => Promise; /** @@ -71,4 +71,4 @@ export interface WasmModule extends LcdModule { readonly queryContractSmart: (address: string, query: object) => Promise; }; } -export declare function setupWasmModule(base: LcdClient): WasmModule; +export declare function setupWasmModule(base: LcdClient): WasmExtension; diff --git a/packages/sdk38/src/cosmosclient.ts b/packages/sdk38/src/cosmosclient.ts index 71dc8be2..0d977502 100644 --- a/packages/sdk38/src/cosmosclient.ts +++ b/packages/sdk38/src/cosmosclient.ts @@ -3,7 +3,7 @@ import { fromBase64, fromHex, toHex } from "@cosmjs/encoding"; import { Uint53 } from "@cosmjs/math"; import { Coin } from "./coins"; -import { AuthModule, BroadcastMode, LcdClient, setupAuthModule } from "./lcdapi"; +import { AuthExtension, BroadcastMode, LcdClient, setupAuthExtension } from "./lcdapi"; import { Log, parseLogs } from "./logs"; import { decodeBech32Pubkey } from "./pubkey"; import { CosmosSdkTx, PubKey, StdTx } from "./types"; @@ -130,11 +130,11 @@ export interface Block { /** Use for testing only */ export interface PrivateCosmWasmClient { - readonly lcdClient: LcdClient & AuthModule; + readonly lcdClient: LcdClient & AuthExtension; } export class CosmosClient { - protected readonly lcdClient: LcdClient & AuthModule; + protected readonly lcdClient: LcdClient & AuthExtension; /** Any address the chain considers valid (valid bech32 with proper prefix) */ protected anyValidAddress: string | undefined; @@ -150,7 +150,10 @@ export class CosmosClient { * @param broadcastMode Defines at which point of the transaction processing the postTx method (i.e. transaction broadcasting) returns */ public constructor(apiUrl: string, broadcastMode = BroadcastMode.Block) { - this.lcdClient = LcdClient.withModules({ apiUrl: apiUrl, broadcastMode: broadcastMode }, setupAuthModule); + this.lcdClient = LcdClient.withExtensions( + { apiUrl: apiUrl, broadcastMode: broadcastMode }, + setupAuthExtension, + ); } public async getChainId(): Promise { diff --git a/packages/sdk38/src/index.ts b/packages/sdk38/src/index.ts index 55d6360d..6e9e6acf 100644 --- a/packages/sdk38/src/index.ts +++ b/packages/sdk38/src/index.ts @@ -22,20 +22,20 @@ export { export { makeSignBytes } from "./encoding"; export { AuthAccountsResponse, - AuthModule, + AuthExtension, BlockResponse, BroadcastMode, EncodeTxResponse, LcdApiArray, + LcdExtension, LcdClient, - LcdModule, NodeInfoResponse, normalizeLcdApiArray, PostTxsResponse, SearchTxsResponse, - setupAuthModule, - setupSupplyModule, - SupplyModule, + setupAuthExtension, + setupSupplyExtension, + SupplyExtension, TxsResponse, } from "./lcdapi"; export { RestClient } from "./restclient"; diff --git a/packages/sdk38/src/lcdapi/auth.spec.ts b/packages/sdk38/src/lcdapi/auth.spec.ts index 633c2f00..2cd2ed45 100644 --- a/packages/sdk38/src/lcdapi/auth.spec.ts +++ b/packages/sdk38/src/lcdapi/auth.spec.ts @@ -8,14 +8,14 @@ import { unused, wasmd, } from "../testutils.spec"; -import { AuthModule, setupAuthModule } from "./auth"; +import { AuthExtension, setupAuthExtension } from "./auth"; import { LcdClient } from "./lcdclient"; -function makeAuthClient(apiUrl: string): LcdClient & AuthModule { - return LcdClient.withModules({ apiUrl }, setupAuthModule); +function makeAuthClient(apiUrl: string): LcdClient & AuthExtension { + return LcdClient.withExtensions({ apiUrl }, setupAuthExtension); } -describe("auth", () => { +describe("AuthExtension", () => { it("works for unused account without pubkey", async () => { pendingWithoutWasmd(); const client = makeAuthClient(wasmd.endpoint); diff --git a/packages/sdk38/src/lcdapi/auth.ts b/packages/sdk38/src/lcdapi/auth.ts index 817c4b46..6ba13a63 100644 --- a/packages/sdk38/src/lcdapi/auth.ts +++ b/packages/sdk38/src/lcdapi/auth.ts @@ -1,5 +1,5 @@ import { Coin } from "../coins"; -import { LcdClient, LcdModule } from "./lcdclient"; +import { LcdClient, LcdExtension } from "./lcdclient"; export interface CosmosSdkAccount { /** Bech32 account address */ @@ -19,13 +19,13 @@ export interface AuthAccountsResponse { }; } -export interface AuthModule extends LcdModule { +export interface AuthExtension extends LcdExtension { readonly auth: { readonly account: (address: string) => Promise; }; } -export function setupAuthModule(base: LcdClient): AuthModule { +export function setupAuthExtension(base: LcdClient): AuthExtension { return { auth: { account: async (address: string) => { diff --git a/packages/sdk38/src/lcdapi/index.ts b/packages/sdk38/src/lcdapi/index.ts index 19876b9d..96e2c8b6 100644 --- a/packages/sdk38/src/lcdapi/index.ts +++ b/packages/sdk38/src/lcdapi/index.ts @@ -2,8 +2,8 @@ // Standard modules (see tracking issue https://github.com/CosmWasm/cosmjs/issues/276) // -export { AuthModule, AuthAccountsResponse, setupAuthModule } from "./auth"; -export { setupSupplyModule, SupplyModule, TotalSupplyAllReponse, TotalSupplyReponse } from "./supply"; +export { AuthExtension, AuthAccountsResponse, setupAuthExtension } from "./auth"; +export { setupSupplyExtension, SupplyExtension, TotalSupplyAllReponse, TotalSupplyReponse } from "./supply"; // // Base types @@ -18,4 +18,4 @@ export { SearchTxsResponse, TxsResponse, } from "./base"; -export { LcdApiArray, LcdClient, LcdModule, normalizeLcdApiArray } from "./lcdclient"; +export { LcdApiArray, LcdClient, LcdExtension, normalizeLcdApiArray } from "./lcdclient"; diff --git a/packages/sdk38/src/lcdapi/lcdclient.spec.ts b/packages/sdk38/src/lcdapi/lcdclient.spec.ts index 6ea7e05a..97bf9990 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.spec.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.spec.ts @@ -21,9 +21,9 @@ import { wasmdEnabled, } from "../testutils.spec"; import { StdFee } from "../types"; -import { setupAuthModule } from "./auth"; +import { setupAuthExtension } from "./auth"; import { TxsResponse } from "./base"; -import { LcdApiArray, LcdClient, LcdModule, normalizeLcdApiArray } from "./lcdclient"; +import { LcdApiArray, LcdClient, LcdExtension, normalizeLcdApiArray } from "./lcdclient"; /** Deployed as part of scripts/wasmd/init.sh */ export const deployedErc20 = { @@ -79,13 +79,13 @@ describe("LcdClient", () => { return response.result; } - interface WasmModule extends LcdModule { + interface WasmExtension extends LcdExtension { wasm: { listCodeInfo: () => Promise; }; } - function setupWasmModule(base: LcdClient): WasmModule { + function setupWasmExtension(base: LcdClient): WasmExtension { return { wasm: { listCodeInfo: async (): Promise => { @@ -98,14 +98,14 @@ describe("LcdClient", () => { } it("works for no modules", async () => { - const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }); + const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }); expect(client).toBeTruthy(); }); it("works for one module", async () => { pendingWithoutWasmd(); - const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupWasmModule); + const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }, setupWasmExtension); const codes = await client.wasm.listCodeInfo(); expect(codes.length).toBeGreaterThanOrEqual(3); expect(codes[0].id).toEqual(deployedErc20.codeId); @@ -134,7 +134,11 @@ describe("LcdClient", () => { }; } - const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupWasmModule, setupSupplyModule); + const client = LcdClient.withExtensions( + { apiUrl: wasmd.endpoint }, + setupWasmExtension, + setupSupplyModule, + ); const codes = await client.wasm.listCodeInfo(); expect(codes.length).toBeGreaterThanOrEqual(3); expect(codes[0].id).toEqual(deployedErc20.codeId); @@ -522,7 +526,7 @@ describe("LcdClient", () => { gas: "890000", }; - const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); + const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }, setupAuthExtension); const { account_number, sequence } = (await client.auth.account(faucet.address)).result.value; const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); @@ -575,7 +579,7 @@ describe("LcdClient", () => { gas: "890000", }; - const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); + const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }, setupAuthExtension); const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value; const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value; const { account_number: an3, sequence: sequence3 } = (await client.auth.account(address3)).result.value; @@ -640,7 +644,7 @@ describe("LcdClient", () => { gas: "890000", }; - const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); + const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }, setupAuthExtension); const { account_number, sequence } = (await client.auth.account(address1)).result.value; const signBytes = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, account_number, sequence); @@ -700,7 +704,7 @@ describe("LcdClient", () => { gas: "890000", }; - const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); + const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }, setupAuthExtension); const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value; const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value; @@ -768,7 +772,7 @@ describe("LcdClient", () => { gas: "890000", }; - const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); + const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }, setupAuthExtension); const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value; const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value; @@ -831,7 +835,7 @@ describe("LcdClient", () => { gas: "890000", }; - const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupAuthModule); + const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }, setupAuthExtension); const { account_number: an1, sequence: sequence1 } = (await client.auth.account(address1)).result.value; const { account_number: an2, sequence: sequence2 } = (await client.auth.account(address2)).result.value; diff --git a/packages/sdk38/src/lcdapi/lcdclient.ts b/packages/sdk38/src/lcdapi/lcdclient.ts index b5406c6e..6938af98 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.ts @@ -20,9 +20,9 @@ export function normalizeLcdApiArray(backend: LcdApiArray): readonly T[] { return backend || []; } -export type LcdModule = Record any>>; +export type LcdExtension = Record any>>; -type LcdModuleSetup = (base: LcdClient) => M; +type LcdExtensionSetup

= (base: LcdClient) => P; export interface LcdClientBaseOptions { readonly apiUrl: string; readonly broadcastMode?: BroadcastMode; @@ -31,104 +31,109 @@ export interface LcdClientBaseOptions { */ export declare class LcdClient { /** Constructs an LCD client with 0 modules */ - static withModules(options: LcdClientBaseOptions): LcdClient; + static withExtensions(options: LcdClientBaseOptions): LcdClient; /** Constructs an LCD client with 1 module */ - static withModules( + static withExtensions( options: LcdClientBaseOptions, - setupModuleA: LcdModuleSetup, + setupModuleA: LcdExtensionSetup, ): LcdClient & A; /** Constructs an LCD client with 2 modules */ - static withModules( + static withExtensions( options: LcdClientBaseOptions, - setupModuleA: LcdModuleSetup, - setupModuleB: LcdModuleSetup, + setupModuleA: LcdExtensionSetup, + setupModuleB: LcdExtensionSetup, ): LcdClient & A & B; /** Constructs an LCD client with 3 modules */ - static withModules( + static withExtensions( options: LcdClientBaseOptions, - setupModuleA: LcdModuleSetup, - setupModuleB: LcdModuleSetup, - setupModuleC: LcdModuleSetup, + setupModuleA: LcdExtensionSetup, + setupModuleB: LcdExtensionSetup, + setupModuleC: LcdExtensionSetup, ): LcdClient & A & B & C; /** Constructs an LCD client with 4 modules */ - static withModules( + static withExtensions< + A extends LcdExtension, + B extends LcdExtension, + C extends LcdExtension, + D extends LcdExtension + >( options: LcdClientBaseOptions, - setupModuleA: LcdModuleSetup, - setupModuleB: LcdModuleSetup, - setupModuleC: LcdModuleSetup, - setupModuleD: LcdModuleSetup, + setupModuleA: LcdExtensionSetup, + setupModuleB: LcdExtensionSetup, + setupModuleC: LcdExtensionSetup, + setupModuleD: LcdExtensionSetup, ): LcdClient & A & B & C & D; /** Constructs an LCD client with 5 modules */ - static withModules< - A extends LcdModule, - B extends LcdModule, - C extends LcdModule, - D extends LcdModule, - E extends LcdModule + static withExtensions< + A extends LcdExtension, + B extends LcdExtension, + C extends LcdExtension, + D extends LcdExtension, + E extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdModuleSetup, - setupModuleB: LcdModuleSetup, - setupModuleC: LcdModuleSetup, - setupModuleD: LcdModuleSetup, - setupModuleE: LcdModuleSetup, + setupModuleA: LcdExtensionSetup, + setupModuleB: LcdExtensionSetup, + setupModuleC: LcdExtensionSetup, + setupModuleD: LcdExtensionSetup, + setupModuleE: LcdExtensionSetup, ): LcdClient & A & B & C & D & E; /** Constructs an LCD client with 6 modules */ - static withModules< - A extends LcdModule, - B extends LcdModule, - C extends LcdModule, - D extends LcdModule, - E extends LcdModule, - F extends LcdModule + static withExtensions< + A extends LcdExtension, + B extends LcdExtension, + C extends LcdExtension, + D extends LcdExtension, + E extends LcdExtension, + F extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdModuleSetup, - setupModuleB: LcdModuleSetup, - setupModuleC: LcdModuleSetup, - setupModuleD: LcdModuleSetup, - setupModuleE: LcdModuleSetup, - setupModuleF: LcdModuleSetup, + setupModuleA: LcdExtensionSetup, + setupModuleB: LcdExtensionSetup, + setupModuleC: LcdExtensionSetup, + setupModuleD: LcdExtensionSetup, + setupModuleE: LcdExtensionSetup, + setupModuleF: LcdExtensionSetup, ): LcdClient & A & B & C & D & E & F; /** Constructs an LCD client with 7 modules */ - static withModules< - A extends LcdModule, - B extends LcdModule, - C extends LcdModule, - D extends LcdModule, - E extends LcdModule, - F extends LcdModule, - G extends LcdModule + static withExtensions< + A extends LcdExtension, + B extends LcdExtension, + C extends LcdExtension, + D extends LcdExtension, + E extends LcdExtension, + F extends LcdExtension, + G extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdModuleSetup, - setupModuleB: LcdModuleSetup, - setupModuleC: LcdModuleSetup, - setupModuleD: LcdModuleSetup, - setupModuleE: LcdModuleSetup, - setupModuleF: LcdModuleSetup, - setupModuleG: LcdModuleSetup, + setupModuleA: LcdExtensionSetup, + setupModuleB: LcdExtensionSetup, + setupModuleC: LcdExtensionSetup, + setupModuleD: LcdExtensionSetup, + setupModuleE: LcdExtensionSetup, + setupModuleF: LcdExtensionSetup, + setupModuleG: LcdExtensionSetup, ): LcdClient & A & B & C & D & E & F & G; /** Constructs an LCD client with 8 modules */ - static withModules< - A extends LcdModule, - B extends LcdModule, - C extends LcdModule, - D extends LcdModule, - E extends LcdModule, - F extends LcdModule, - G extends LcdModule, - H extends LcdModule + static withExtensions< + A extends LcdExtension, + B extends LcdExtension, + C extends LcdExtension, + D extends LcdExtension, + E extends LcdExtension, + F extends LcdExtension, + G extends LcdExtension, + H extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdModuleSetup, - setupModuleB: LcdModuleSetup, - setupModuleC: LcdModuleSetup, - setupModuleD: LcdModuleSetup, - setupModuleE: LcdModuleSetup, - setupModuleF: LcdModuleSetup, - setupModuleG: LcdModuleSetup, - setupModuleH: LcdModuleSetup, + setupModuleA: LcdExtensionSetup, + setupModuleB: LcdExtensionSetup, + setupModuleC: LcdExtensionSetup, + setupModuleD: LcdExtensionSetup, + setupModuleE: LcdExtensionSetup, + setupModuleF: LcdExtensionSetup, + setupModuleG: LcdExtensionSetup, + setupModuleH: LcdExtensionSetup, ): LcdClient & A & B & C & D & E & F & G & H; private readonly client; private readonly broadcastMode; diff --git a/packages/sdk38/types/lcdapi/supply.d.ts b/packages/sdk38/types/lcdapi/supply.d.ts index f5ce19e2..4b45aeb7 100644 --- a/packages/sdk38/types/lcdapi/supply.d.ts +++ b/packages/sdk38/types/lcdapi/supply.d.ts @@ -1,5 +1,5 @@ import { Coin } from "../coins"; -import { LcdApiArray, LcdClient, LcdModule } from "./lcdclient"; +import { LcdApiArray, LcdClient, LcdExtension } from "./lcdclient"; export interface TotalSupplyAllReponse { readonly height: string; readonly result: LcdApiArray; @@ -9,10 +9,10 @@ export interface TotalSupplyReponse { /** The amount */ readonly result: string; } -export interface SupplyModule extends LcdModule { +export interface SupplyExtension extends LcdExtension { readonly supply: { readonly totalAll: () => Promise; readonly total: (denom: string) => Promise; }; } -export declare function setupSupplyModule(base: LcdClient): SupplyModule; +export declare function setupSupplyExtension(base: LcdClient): SupplyExtension; From fa2eae5060266c907eab04dc87d008366977fd39 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 12:05:05 +0200 Subject: [PATCH 26/37] Rename module -> extension --- packages/sdk38/src/lcdapi/lcdclient.ts | 134 ++++++++++----------- packages/sdk38/types/lcdapi/lcdclient.d.ts | 90 +++++++------- 2 files changed, 112 insertions(+), 112 deletions(-) diff --git a/packages/sdk38/src/lcdapi/lcdclient.ts b/packages/sdk38/src/lcdapi/lcdclient.ts index 6938af98..1d82478f 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.ts @@ -64,31 +64,31 @@ function parseAxiosError(err: AxiosError): never { * @see https://cosmos.network/rpc */ export class LcdClient { - /** Constructs an LCD client with 0 modules */ + /** Constructs an LCD client with 0 extensions */ public static withExtensions(options: LcdClientBaseOptions): LcdClient; - /** Constructs an LCD client with 1 module */ + /** Constructs an LCD client with 1 extension */ public static withExtensions( options: LcdClientBaseOptions, - setupModuleA: LcdExtensionSetup, + setupExtensionA: LcdExtensionSetup, ): LcdClient & A; - /** Constructs an LCD client with 2 modules */ + /** Constructs an LCD client with 2 extensions */ public static withExtensions( options: LcdClientBaseOptions, - setupModuleA: LcdExtensionSetup, - setupModuleB: LcdExtensionSetup, + setupExtensionA: LcdExtensionSetup, + setupExtensionB: LcdExtensionSetup, ): LcdClient & A & B; - /** Constructs an LCD client with 3 modules */ + /** Constructs an LCD client with 3 extensions */ public static withExtensions( options: LcdClientBaseOptions, - setupModuleA: LcdExtensionSetup, - setupModuleB: LcdExtensionSetup, - setupModuleC: LcdExtensionSetup, + setupExtensionA: LcdExtensionSetup, + setupExtensionB: LcdExtensionSetup, + setupExtensionC: LcdExtensionSetup, ): LcdClient & A & B & C; - /** Constructs an LCD client with 4 modules */ + /** Constructs an LCD client with 4 extensions */ public static withExtensions< A extends LcdExtension, B extends LcdExtension, @@ -96,13 +96,13 @@ export class LcdClient { D extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdExtensionSetup, - setupModuleB: LcdExtensionSetup, - setupModuleC: LcdExtensionSetup, - setupModuleD: LcdExtensionSetup, + setupExtensionA: LcdExtensionSetup, + setupExtensionB: LcdExtensionSetup, + setupExtensionC: LcdExtensionSetup, + setupExtensionD: LcdExtensionSetup, ): LcdClient & A & B & C & D; - /** Constructs an LCD client with 5 modules */ + /** Constructs an LCD client with 5 extensions */ public static withExtensions< A extends LcdExtension, B extends LcdExtension, @@ -111,14 +111,14 @@ export class LcdClient { E extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdExtensionSetup, - setupModuleB: LcdExtensionSetup, - setupModuleC: LcdExtensionSetup, - setupModuleD: LcdExtensionSetup, - setupModuleE: LcdExtensionSetup, + setupExtensionA: LcdExtensionSetup, + setupExtensionB: LcdExtensionSetup, + setupExtensionC: LcdExtensionSetup, + setupExtensionD: LcdExtensionSetup, + setupExtensionE: LcdExtensionSetup, ): LcdClient & A & B & C & D & E; - /** Constructs an LCD client with 6 modules */ + /** Constructs an LCD client with 6 extensions */ public static withExtensions< A extends LcdExtension, B extends LcdExtension, @@ -128,15 +128,15 @@ export class LcdClient { F extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdExtensionSetup, - setupModuleB: LcdExtensionSetup, - setupModuleC: LcdExtensionSetup, - setupModuleD: LcdExtensionSetup, - setupModuleE: LcdExtensionSetup, - setupModuleF: LcdExtensionSetup, + setupExtensionA: LcdExtensionSetup, + setupExtensionB: LcdExtensionSetup, + setupExtensionC: LcdExtensionSetup, + setupExtensionD: LcdExtensionSetup, + setupExtensionE: LcdExtensionSetup, + setupExtensionF: LcdExtensionSetup, ): LcdClient & A & B & C & D & E & F; - /** Constructs an LCD client with 7 modules */ + /** Constructs an LCD client with 7 extensions */ public static withExtensions< A extends LcdExtension, B extends LcdExtension, @@ -147,16 +147,16 @@ export class LcdClient { G extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdExtensionSetup, - setupModuleB: LcdExtensionSetup, - setupModuleC: LcdExtensionSetup, - setupModuleD: LcdExtensionSetup, - setupModuleE: LcdExtensionSetup, - setupModuleF: LcdExtensionSetup, - setupModuleG: LcdExtensionSetup, + setupExtensionA: LcdExtensionSetup, + setupExtensionB: LcdExtensionSetup, + setupExtensionC: LcdExtensionSetup, + setupExtensionD: LcdExtensionSetup, + setupExtensionE: LcdExtensionSetup, + setupExtensionF: LcdExtensionSetup, + setupExtensionG: LcdExtensionSetup, ): LcdClient & A & B & C & D & E & F & G; - /** Constructs an LCD client with 8 modules */ + /** Constructs an LCD client with 8 extensions */ public static withExtensions< A extends LcdExtension, B extends LcdExtension, @@ -168,14 +168,14 @@ export class LcdClient { H extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdExtensionSetup, - setupModuleB: LcdExtensionSetup, - setupModuleC: LcdExtensionSetup, - setupModuleD: LcdExtensionSetup, - setupModuleE: LcdExtensionSetup, - setupModuleF: LcdExtensionSetup, - setupModuleG: LcdExtensionSetup, - setupModuleH: LcdExtensionSetup, + setupExtensionA: LcdExtensionSetup, + setupExtensionB: LcdExtensionSetup, + setupExtensionC: LcdExtensionSetup, + setupExtensionD: LcdExtensionSetup, + setupExtensionE: LcdExtensionSetup, + setupExtensionF: LcdExtensionSetup, + setupExtensionG: LcdExtensionSetup, + setupExtensionH: LcdExtensionSetup, ): LcdClient & A & B & C & D & E & F & G & H; public static withExtensions< @@ -189,31 +189,31 @@ export class LcdClient { H extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA?: LcdExtensionSetup, - setupModuleB?: LcdExtensionSetup, - setupModuleC?: LcdExtensionSetup, - setupModuleD?: LcdExtensionSetup, - setupModuleE?: LcdExtensionSetup, - setupModuleF?: LcdExtensionSetup, - setupModuleG?: LcdExtensionSetup, - setupModuleH?: LcdExtensionSetup, + setupExtensionA?: LcdExtensionSetup, + setupExtensionB?: LcdExtensionSetup, + setupExtensionC?: LcdExtensionSetup, + setupExtensionD?: LcdExtensionSetup, + setupExtensionE?: LcdExtensionSetup, + setupExtensionF?: LcdExtensionSetup, + setupExtensionG?: LcdExtensionSetup, + setupExtensionH?: LcdExtensionSetup, ): any { const client = new LcdClient(options.apiUrl, options.broadcastMode); - const modules = new Array(); - if (setupModuleA) modules.push(setupModuleA(client)); - if (setupModuleB) modules.push(setupModuleB(client)); - if (setupModuleC) modules.push(setupModuleC(client)); - if (setupModuleD) modules.push(setupModuleD(client)); - if (setupModuleE) modules.push(setupModuleE(client)); - if (setupModuleF) modules.push(setupModuleF(client)); - if (setupModuleG) modules.push(setupModuleG(client)); - if (setupModuleH) modules.push(setupModuleH(client)); - for (const module of modules) { - assert(isNonNullObject(module), `Module must be a non-null object`); - for (const key in module) { - assert(typeof key == "string", `Found non-string module key: ${key}`); - (client as any)[key] = module[key]; + const extensions = new Array(); + if (setupExtensionA) extensions.push(setupExtensionA(client)); + if (setupExtensionB) extensions.push(setupExtensionB(client)); + if (setupExtensionC) extensions.push(setupExtensionC(client)); + if (setupExtensionD) extensions.push(setupExtensionD(client)); + if (setupExtensionE) extensions.push(setupExtensionE(client)); + if (setupExtensionF) extensions.push(setupExtensionF(client)); + if (setupExtensionG) extensions.push(setupExtensionG(client)); + if (setupExtensionH) extensions.push(setupExtensionH(client)); + for (const extension of extensions) { + assert(isNonNullObject(extension), `Extension must be a non-null object`); + for (const key in extension) { + assert(typeof key == "string", `Found non-string extension key: ${key}`); + (client as any)[key] = extension[key]; } } diff --git a/packages/sdk38/types/lcdapi/lcdclient.d.ts b/packages/sdk38/types/lcdapi/lcdclient.d.ts index 5b7616f6..6df6dc3a 100644 --- a/packages/sdk38/types/lcdapi/lcdclient.d.ts +++ b/packages/sdk38/types/lcdapi/lcdclient.d.ts @@ -30,27 +30,27 @@ export interface LcdClientBaseOptions { * @see https://cosmos.network/rpc */ export declare class LcdClient { - /** Constructs an LCD client with 0 modules */ + /** Constructs an LCD client with 0 extensions */ static withExtensions(options: LcdClientBaseOptions): LcdClient; - /** Constructs an LCD client with 1 module */ + /** Constructs an LCD client with 1 extension */ static withExtensions( options: LcdClientBaseOptions, - setupModuleA: LcdExtensionSetup, + setupExtensionA: LcdExtensionSetup, ): LcdClient & A; - /** Constructs an LCD client with 2 modules */ + /** Constructs an LCD client with 2 extensions */ static withExtensions( options: LcdClientBaseOptions, - setupModuleA: LcdExtensionSetup, - setupModuleB: LcdExtensionSetup, + setupExtensionA: LcdExtensionSetup, + setupExtensionB: LcdExtensionSetup, ): LcdClient & A & B; - /** Constructs an LCD client with 3 modules */ + /** Constructs an LCD client with 3 extensions */ static withExtensions( options: LcdClientBaseOptions, - setupModuleA: LcdExtensionSetup, - setupModuleB: LcdExtensionSetup, - setupModuleC: LcdExtensionSetup, + setupExtensionA: LcdExtensionSetup, + setupExtensionB: LcdExtensionSetup, + setupExtensionC: LcdExtensionSetup, ): LcdClient & A & B & C; - /** Constructs an LCD client with 4 modules */ + /** Constructs an LCD client with 4 extensions */ static withExtensions< A extends LcdExtension, B extends LcdExtension, @@ -58,12 +58,12 @@ export declare class LcdClient { D extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdExtensionSetup, - setupModuleB: LcdExtensionSetup, - setupModuleC: LcdExtensionSetup, - setupModuleD: LcdExtensionSetup, + setupExtensionA: LcdExtensionSetup, + setupExtensionB: LcdExtensionSetup, + setupExtensionC: LcdExtensionSetup, + setupExtensionD: LcdExtensionSetup, ): LcdClient & A & B & C & D; - /** Constructs an LCD client with 5 modules */ + /** Constructs an LCD client with 5 extensions */ static withExtensions< A extends LcdExtension, B extends LcdExtension, @@ -72,13 +72,13 @@ export declare class LcdClient { E extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdExtensionSetup, - setupModuleB: LcdExtensionSetup, - setupModuleC: LcdExtensionSetup, - setupModuleD: LcdExtensionSetup, - setupModuleE: LcdExtensionSetup, + setupExtensionA: LcdExtensionSetup, + setupExtensionB: LcdExtensionSetup, + setupExtensionC: LcdExtensionSetup, + setupExtensionD: LcdExtensionSetup, + setupExtensionE: LcdExtensionSetup, ): LcdClient & A & B & C & D & E; - /** Constructs an LCD client with 6 modules */ + /** Constructs an LCD client with 6 extensions */ static withExtensions< A extends LcdExtension, B extends LcdExtension, @@ -88,14 +88,14 @@ export declare class LcdClient { F extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdExtensionSetup, - setupModuleB: LcdExtensionSetup, - setupModuleC: LcdExtensionSetup, - setupModuleD: LcdExtensionSetup, - setupModuleE: LcdExtensionSetup, - setupModuleF: LcdExtensionSetup, + setupExtensionA: LcdExtensionSetup, + setupExtensionB: LcdExtensionSetup, + setupExtensionC: LcdExtensionSetup, + setupExtensionD: LcdExtensionSetup, + setupExtensionE: LcdExtensionSetup, + setupExtensionF: LcdExtensionSetup, ): LcdClient & A & B & C & D & E & F; - /** Constructs an LCD client with 7 modules */ + /** Constructs an LCD client with 7 extensions */ static withExtensions< A extends LcdExtension, B extends LcdExtension, @@ -106,15 +106,15 @@ export declare class LcdClient { G extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdExtensionSetup, - setupModuleB: LcdExtensionSetup, - setupModuleC: LcdExtensionSetup, - setupModuleD: LcdExtensionSetup, - setupModuleE: LcdExtensionSetup, - setupModuleF: LcdExtensionSetup, - setupModuleG: LcdExtensionSetup, + setupExtensionA: LcdExtensionSetup, + setupExtensionB: LcdExtensionSetup, + setupExtensionC: LcdExtensionSetup, + setupExtensionD: LcdExtensionSetup, + setupExtensionE: LcdExtensionSetup, + setupExtensionF: LcdExtensionSetup, + setupExtensionG: LcdExtensionSetup, ): LcdClient & A & B & C & D & E & F & G; - /** Constructs an LCD client with 8 modules */ + /** Constructs an LCD client with 8 extensions */ static withExtensions< A extends LcdExtension, B extends LcdExtension, @@ -126,14 +126,14 @@ export declare class LcdClient { H extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdExtensionSetup, - setupModuleB: LcdExtensionSetup, - setupModuleC: LcdExtensionSetup, - setupModuleD: LcdExtensionSetup, - setupModuleE: LcdExtensionSetup, - setupModuleF: LcdExtensionSetup, - setupModuleG: LcdExtensionSetup, - setupModuleH: LcdExtensionSetup, + setupExtensionA: LcdExtensionSetup, + setupExtensionB: LcdExtensionSetup, + setupExtensionC: LcdExtensionSetup, + setupExtensionD: LcdExtensionSetup, + setupExtensionE: LcdExtensionSetup, + setupExtensionF: LcdExtensionSetup, + setupExtensionG: LcdExtensionSetup, + setupExtensionH: LcdExtensionSetup, ): LcdClient & A & B & C & D & E & F & G & H; private readonly client; private readonly broadcastMode; From eb04b02b820215cd2c93d5505c4a306fe8e858e0 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 23:41:35 +0200 Subject: [PATCH 27/37] Remove index signature for better type safety --- packages/cosmwasm/src/lcdapi/wasm.ts | 4 +- packages/cosmwasm/types/lcdapi/wasm.d.ts | 4 +- packages/sdk38/src/index.ts | 1 - packages/sdk38/src/lcdapi/auth.ts | 4 +- packages/sdk38/src/lcdapi/index.ts | 2 +- packages/sdk38/src/lcdapi/lcdclient.spec.ts | 4 +- packages/sdk38/src/lcdapi/lcdclient.ts | 92 ++++++++++----------- packages/sdk38/src/lcdapi/supply.ts | 4 +- packages/sdk38/types/index.d.ts | 1 - packages/sdk38/types/lcdapi/auth.d.ts | 4 +- packages/sdk38/types/lcdapi/index.d.ts | 2 +- packages/sdk38/types/lcdapi/lcdclient.d.ts | 68 +++++++-------- packages/sdk38/types/lcdapi/supply.d.ts | 4 +- 13 files changed, 89 insertions(+), 105 deletions(-) diff --git a/packages/cosmwasm/src/lcdapi/wasm.ts b/packages/cosmwasm/src/lcdapi/wasm.ts index 6c836744..521faeb6 100644 --- a/packages/cosmwasm/src/lcdapi/wasm.ts +++ b/packages/cosmwasm/src/lcdapi/wasm.ts @@ -1,5 +1,5 @@ import { fromBase64, fromUtf8, toHex, toUtf8 } from "@cosmjs/encoding"; -import { LcdApiArray, LcdClient, LcdExtension, normalizeLcdApiArray } from "@cosmjs/sdk38"; +import { LcdApiArray, LcdClient, normalizeLcdApiArray } from "@cosmjs/sdk38"; import { JsonObject, Model, parseWasmData, WasmData } from "../types"; @@ -75,7 +75,7 @@ function unwrapWasmResponse(response: WasmResponse): T { /** * @see https://github.com/cosmwasm/wasmd/blob/master/x/wasm/client/rest/query.go#L19-L27 */ -export interface WasmExtension extends LcdExtension { +export interface WasmExtension { readonly wasm: { readonly listCodeInfo: () => Promise; diff --git a/packages/cosmwasm/types/lcdapi/wasm.d.ts b/packages/cosmwasm/types/lcdapi/wasm.d.ts index 5e454691..8859ffad 100644 --- a/packages/cosmwasm/types/lcdapi/wasm.d.ts +++ b/packages/cosmwasm/types/lcdapi/wasm.d.ts @@ -1,4 +1,4 @@ -import { LcdClient, LcdExtension } from "@cosmjs/sdk38"; +import { LcdClient } from "@cosmjs/sdk38"; import { JsonObject, Model } from "../types"; export interface CodeInfo { readonly id: number; @@ -40,7 +40,7 @@ export interface ContractDetails extends ContractInfo { /** * @see https://github.com/cosmwasm/wasmd/blob/master/x/wasm/client/rest/query.go#L19-L27 */ -export interface WasmExtension extends LcdExtension { +export interface WasmExtension { readonly wasm: { readonly listCodeInfo: () => Promise; /** diff --git a/packages/sdk38/src/index.ts b/packages/sdk38/src/index.ts index 6e9e6acf..d6053e6e 100644 --- a/packages/sdk38/src/index.ts +++ b/packages/sdk38/src/index.ts @@ -27,7 +27,6 @@ export { BroadcastMode, EncodeTxResponse, LcdApiArray, - LcdExtension, LcdClient, NodeInfoResponse, normalizeLcdApiArray, diff --git a/packages/sdk38/src/lcdapi/auth.ts b/packages/sdk38/src/lcdapi/auth.ts index 6ba13a63..d9066733 100644 --- a/packages/sdk38/src/lcdapi/auth.ts +++ b/packages/sdk38/src/lcdapi/auth.ts @@ -1,5 +1,5 @@ import { Coin } from "../coins"; -import { LcdClient, LcdExtension } from "./lcdclient"; +import { LcdClient } from "./lcdclient"; export interface CosmosSdkAccount { /** Bech32 account address */ @@ -19,7 +19,7 @@ export interface AuthAccountsResponse { }; } -export interface AuthExtension extends LcdExtension { +export interface AuthExtension { readonly auth: { readonly account: (address: string) => Promise; }; diff --git a/packages/sdk38/src/lcdapi/index.ts b/packages/sdk38/src/lcdapi/index.ts index 96e2c8b6..bc39d863 100644 --- a/packages/sdk38/src/lcdapi/index.ts +++ b/packages/sdk38/src/lcdapi/index.ts @@ -18,4 +18,4 @@ export { SearchTxsResponse, TxsResponse, } from "./base"; -export { LcdApiArray, LcdClient, LcdExtension, normalizeLcdApiArray } from "./lcdclient"; +export { LcdApiArray, LcdClient, normalizeLcdApiArray } from "./lcdclient"; diff --git a/packages/sdk38/src/lcdapi/lcdclient.spec.ts b/packages/sdk38/src/lcdapi/lcdclient.spec.ts index 97bf9990..2a35ee24 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.spec.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.spec.ts @@ -23,7 +23,7 @@ import { import { StdFee } from "../types"; import { setupAuthExtension } from "./auth"; import { TxsResponse } from "./base"; -import { LcdApiArray, LcdClient, LcdExtension, normalizeLcdApiArray } from "./lcdclient"; +import { LcdApiArray, LcdClient, normalizeLcdApiArray } from "./lcdclient"; /** Deployed as part of scripts/wasmd/init.sh */ export const deployedErc20 = { @@ -79,7 +79,7 @@ describe("LcdClient", () => { return response.result; } - interface WasmExtension extends LcdExtension { + interface WasmExtension { wasm: { listCodeInfo: () => Promise; }; diff --git a/packages/sdk38/src/lcdapi/lcdclient.ts b/packages/sdk38/src/lcdapi/lcdclient.ts index 1d82478f..d1685b68 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.ts @@ -20,9 +20,7 @@ export function normalizeLcdApiArray(backend: LcdApiArray): readonly T[] { return backend || []; } -export type LcdExtension = Record any>>; - -type LcdExtensionSetup

= (base: LcdClient) => P; +type LcdExtensionSetup

= (base: LcdClient) => P; export interface LcdClientBaseOptions { readonly apiUrl: string; @@ -68,20 +66,20 @@ export class LcdClient { public static withExtensions(options: LcdClientBaseOptions): LcdClient; /** Constructs an LCD client with 1 extension */ - public static withExtensions( + public static withExtensions( options: LcdClientBaseOptions, setupExtensionA: LcdExtensionSetup, ): LcdClient & A; /** Constructs an LCD client with 2 extensions */ - public static withExtensions( + public static withExtensions( options: LcdClientBaseOptions, setupExtensionA: LcdExtensionSetup, setupExtensionB: LcdExtensionSetup, ): LcdClient & A & B; /** Constructs an LCD client with 3 extensions */ - public static withExtensions( + public static withExtensions( options: LcdClientBaseOptions, setupExtensionA: LcdExtensionSetup, setupExtensionB: LcdExtensionSetup, @@ -89,12 +87,7 @@ export class LcdClient { ): LcdClient & A & B & C; /** Constructs an LCD client with 4 extensions */ - public static withExtensions< - A extends LcdExtension, - B extends LcdExtension, - C extends LcdExtension, - D extends LcdExtension - >( + public static withExtensions( options: LcdClientBaseOptions, setupExtensionA: LcdExtensionSetup, setupExtensionB: LcdExtensionSetup, @@ -104,11 +97,11 @@ export class LcdClient { /** Constructs an LCD client with 5 extensions */ public static withExtensions< - A extends LcdExtension, - B extends LcdExtension, - C extends LcdExtension, - D extends LcdExtension, - E extends LcdExtension + A extends object, + B extends object, + C extends object, + D extends object, + E extends object >( options: LcdClientBaseOptions, setupExtensionA: LcdExtensionSetup, @@ -120,12 +113,12 @@ export class LcdClient { /** Constructs an LCD client with 6 extensions */ public static withExtensions< - A extends LcdExtension, - B extends LcdExtension, - C extends LcdExtension, - D extends LcdExtension, - E extends LcdExtension, - F extends LcdExtension + A extends object, + B extends object, + C extends object, + D extends object, + E extends object, + F extends object >( options: LcdClientBaseOptions, setupExtensionA: LcdExtensionSetup, @@ -138,13 +131,13 @@ export class LcdClient { /** Constructs an LCD client with 7 extensions */ public static withExtensions< - A extends LcdExtension, - B extends LcdExtension, - C extends LcdExtension, - D extends LcdExtension, - E extends LcdExtension, - F extends LcdExtension, - G extends LcdExtension + A extends object, + B extends object, + C extends object, + D extends object, + E extends object, + F extends object, + G extends object >( options: LcdClientBaseOptions, setupExtensionA: LcdExtensionSetup, @@ -158,14 +151,14 @@ export class LcdClient { /** Constructs an LCD client with 8 extensions */ public static withExtensions< - A extends LcdExtension, - B extends LcdExtension, - C extends LcdExtension, - D extends LcdExtension, - E extends LcdExtension, - F extends LcdExtension, - G extends LcdExtension, - H extends LcdExtension + A extends object, + B extends object, + C extends object, + D extends object, + E extends object, + F extends object, + G extends object, + H extends object >( options: LcdClientBaseOptions, setupExtensionA: LcdExtensionSetup, @@ -179,14 +172,14 @@ export class LcdClient { ): LcdClient & A & B & C & D & E & F & G & H; public static withExtensions< - A extends LcdExtension, - B extends LcdExtension, - C extends LcdExtension, - D extends LcdExtension, - E extends LcdExtension, - F extends LcdExtension, - G extends LcdExtension, - H extends LcdExtension + A extends object, + B extends object, + C extends object, + D extends object, + E extends object, + F extends object, + G extends object, + H extends object >( options: LcdClientBaseOptions, setupExtensionA?: LcdExtensionSetup, @@ -200,7 +193,7 @@ export class LcdClient { ): any { const client = new LcdClient(options.apiUrl, options.broadcastMode); - const extensions = new Array(); + const extensions = new Array(); if (setupExtensionA) extensions.push(setupExtensionA(client)); if (setupExtensionB) extensions.push(setupExtensionB(client)); if (setupExtensionC) extensions.push(setupExtensionC(client)); @@ -211,9 +204,8 @@ export class LcdClient { if (setupExtensionH) extensions.push(setupExtensionH(client)); for (const extension of extensions) { assert(isNonNullObject(extension), `Extension must be a non-null object`); - for (const key in extension) { - assert(typeof key == "string", `Found non-string extension key: ${key}`); - (client as any)[key] = extension[key]; + for (const [key, value] of Object.entries(extension)) { + (client as any)[key] = value; } } diff --git a/packages/sdk38/src/lcdapi/supply.ts b/packages/sdk38/src/lcdapi/supply.ts index a0bfb77a..06443134 100644 --- a/packages/sdk38/src/lcdapi/supply.ts +++ b/packages/sdk38/src/lcdapi/supply.ts @@ -1,5 +1,5 @@ import { Coin } from "../coins"; -import { LcdApiArray, LcdClient, LcdExtension } from "./lcdclient"; +import { LcdApiArray, LcdClient } from "./lcdclient"; export interface TotalSupplyAllReponse { readonly height: string; @@ -12,7 +12,7 @@ export interface TotalSupplyReponse { readonly result: string; } -export interface SupplyExtension extends LcdExtension { +export interface SupplyExtension { readonly supply: { readonly totalAll: () => Promise; readonly total: (denom: string) => Promise; diff --git a/packages/sdk38/types/index.d.ts b/packages/sdk38/types/index.d.ts index fe93970c..38123a21 100644 --- a/packages/sdk38/types/index.d.ts +++ b/packages/sdk38/types/index.d.ts @@ -25,7 +25,6 @@ export { BroadcastMode, EncodeTxResponse, LcdApiArray, - LcdExtension, LcdClient, NodeInfoResponse, normalizeLcdApiArray, diff --git a/packages/sdk38/types/lcdapi/auth.d.ts b/packages/sdk38/types/lcdapi/auth.d.ts index 23ecb851..95f404d3 100644 --- a/packages/sdk38/types/lcdapi/auth.d.ts +++ b/packages/sdk38/types/lcdapi/auth.d.ts @@ -1,5 +1,5 @@ import { Coin } from "../coins"; -import { LcdClient, LcdExtension } from "./lcdclient"; +import { LcdClient } from "./lcdclient"; export interface CosmosSdkAccount { /** Bech32 account address */ readonly address: string; @@ -16,7 +16,7 @@ export interface AuthAccountsResponse { readonly value: CosmosSdkAccount; }; } -export interface AuthExtension extends LcdExtension { +export interface AuthExtension { readonly auth: { readonly account: (address: string) => Promise; }; diff --git a/packages/sdk38/types/lcdapi/index.d.ts b/packages/sdk38/types/lcdapi/index.d.ts index ad1eb321..dc0274a9 100644 --- a/packages/sdk38/types/lcdapi/index.d.ts +++ b/packages/sdk38/types/lcdapi/index.d.ts @@ -9,4 +9,4 @@ export { SearchTxsResponse, TxsResponse, } from "./base"; -export { LcdApiArray, LcdClient, LcdExtension, normalizeLcdApiArray } from "./lcdclient"; +export { LcdApiArray, LcdClient, normalizeLcdApiArray } from "./lcdclient"; diff --git a/packages/sdk38/types/lcdapi/lcdclient.d.ts b/packages/sdk38/types/lcdapi/lcdclient.d.ts index 6df6dc3a..009e556e 100644 --- a/packages/sdk38/types/lcdapi/lcdclient.d.ts +++ b/packages/sdk38/types/lcdapi/lcdclient.d.ts @@ -11,8 +11,7 @@ import { /** Unfortunately, Cosmos SDK encodes empty arrays as null */ export declare type LcdApiArray = readonly T[] | null; export declare function normalizeLcdApiArray(backend: LcdApiArray): readonly T[]; -export declare type LcdExtension = Record any>>; -declare type LcdExtensionSetup

= (base: LcdClient) => P; +declare type LcdExtensionSetup

= (base: LcdClient) => P; export interface LcdClientBaseOptions { readonly apiUrl: string; readonly broadcastMode?: BroadcastMode; @@ -33,30 +32,25 @@ export declare class LcdClient { /** Constructs an LCD client with 0 extensions */ static withExtensions(options: LcdClientBaseOptions): LcdClient; /** Constructs an LCD client with 1 extension */ - static withExtensions( + static withExtensions( options: LcdClientBaseOptions, setupExtensionA: LcdExtensionSetup, ): LcdClient & A; /** Constructs an LCD client with 2 extensions */ - static withExtensions( + static withExtensions( options: LcdClientBaseOptions, setupExtensionA: LcdExtensionSetup, setupExtensionB: LcdExtensionSetup, ): LcdClient & A & B; /** Constructs an LCD client with 3 extensions */ - static withExtensions( + static withExtensions( options: LcdClientBaseOptions, setupExtensionA: LcdExtensionSetup, setupExtensionB: LcdExtensionSetup, setupExtensionC: LcdExtensionSetup, ): LcdClient & A & B & C; /** Constructs an LCD client with 4 extensions */ - static withExtensions< - A extends LcdExtension, - B extends LcdExtension, - C extends LcdExtension, - D extends LcdExtension - >( + static withExtensions( options: LcdClientBaseOptions, setupExtensionA: LcdExtensionSetup, setupExtensionB: LcdExtensionSetup, @@ -65,11 +59,11 @@ export declare class LcdClient { ): LcdClient & A & B & C & D; /** Constructs an LCD client with 5 extensions */ static withExtensions< - A extends LcdExtension, - B extends LcdExtension, - C extends LcdExtension, - D extends LcdExtension, - E extends LcdExtension + A extends object, + B extends object, + C extends object, + D extends object, + E extends object >( options: LcdClientBaseOptions, setupExtensionA: LcdExtensionSetup, @@ -80,12 +74,12 @@ export declare class LcdClient { ): LcdClient & A & B & C & D & E; /** Constructs an LCD client with 6 extensions */ static withExtensions< - A extends LcdExtension, - B extends LcdExtension, - C extends LcdExtension, - D extends LcdExtension, - E extends LcdExtension, - F extends LcdExtension + A extends object, + B extends object, + C extends object, + D extends object, + E extends object, + F extends object >( options: LcdClientBaseOptions, setupExtensionA: LcdExtensionSetup, @@ -97,13 +91,13 @@ export declare class LcdClient { ): LcdClient & A & B & C & D & E & F; /** Constructs an LCD client with 7 extensions */ static withExtensions< - A extends LcdExtension, - B extends LcdExtension, - C extends LcdExtension, - D extends LcdExtension, - E extends LcdExtension, - F extends LcdExtension, - G extends LcdExtension + A extends object, + B extends object, + C extends object, + D extends object, + E extends object, + F extends object, + G extends object >( options: LcdClientBaseOptions, setupExtensionA: LcdExtensionSetup, @@ -116,14 +110,14 @@ export declare class LcdClient { ): LcdClient & A & B & C & D & E & F & G; /** Constructs an LCD client with 8 extensions */ static withExtensions< - A extends LcdExtension, - B extends LcdExtension, - C extends LcdExtension, - D extends LcdExtension, - E extends LcdExtension, - F extends LcdExtension, - G extends LcdExtension, - H extends LcdExtension + A extends object, + B extends object, + C extends object, + D extends object, + E extends object, + F extends object, + G extends object, + H extends object >( options: LcdClientBaseOptions, setupExtensionA: LcdExtensionSetup, diff --git a/packages/sdk38/types/lcdapi/supply.d.ts b/packages/sdk38/types/lcdapi/supply.d.ts index 4b45aeb7..e9bf1d2c 100644 --- a/packages/sdk38/types/lcdapi/supply.d.ts +++ b/packages/sdk38/types/lcdapi/supply.d.ts @@ -1,5 +1,5 @@ import { Coin } from "../coins"; -import { LcdApiArray, LcdClient, LcdExtension } from "./lcdclient"; +import { LcdApiArray, LcdClient } from "./lcdclient"; export interface TotalSupplyAllReponse { readonly height: string; readonly result: LcdApiArray; @@ -9,7 +9,7 @@ export interface TotalSupplyReponse { /** The amount */ readonly result: string; } -export interface SupplyExtension extends LcdExtension { +export interface SupplyExtension { readonly supply: { readonly totalAll: () => Promise; readonly total: (denom: string) => Promise; From b5e0023b049163137d001e3124ee54263c0a0057 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 14:24:31 +0200 Subject: [PATCH 28/37] Module -> extension --- packages/sdk38/src/lcdapi/lcdclient.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/sdk38/src/lcdapi/lcdclient.spec.ts b/packages/sdk38/src/lcdapi/lcdclient.spec.ts index 2a35ee24..81f776d4 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.spec.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.spec.ts @@ -97,12 +97,12 @@ describe("LcdClient", () => { }; } - it("works for no modules", async () => { + it("works for no extension", async () => { const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }); expect(client).toBeTruthy(); }); - it("works for one module", async () => { + it("works for one extension", async () => { pendingWithoutWasmd(); const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }, setupWasmExtension); @@ -114,7 +114,7 @@ describe("LcdClient", () => { expect(codes[0].source).toEqual(deployedErc20.source); }); - it("works for two modules", async () => { + it("works for two extensions", async () => { pendingWithoutWasmd(); interface TotalSupplyAllReponse { @@ -123,7 +123,7 @@ describe("LcdClient", () => { } // eslint-disable-next-line @typescript-eslint/explicit-function-return-type - function setupSupplyModule(base: LcdClient) { + function setupSupplyExtension(base: LcdClient) { return { supply: { totalAll: async (): Promise => { @@ -137,7 +137,7 @@ describe("LcdClient", () => { const client = LcdClient.withExtensions( { apiUrl: wasmd.endpoint }, setupWasmExtension, - setupSupplyModule, + setupSupplyExtension, ); const codes = await client.wasm.listCodeInfo(); expect(codes.length).toBeGreaterThanOrEqual(3); From 40c7ec51462cc951d79469fbb718aad984b548a6 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 14:24:44 +0200 Subject: [PATCH 29/37] Allow merging modules --- packages/sdk38/src/lcdapi/lcdclient.spec.ts | 35 +++++++++++++++++++++ packages/sdk38/src/lcdapi/lcdclient.ts | 5 +-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/packages/sdk38/src/lcdapi/lcdclient.spec.ts b/packages/sdk38/src/lcdapi/lcdclient.spec.ts index 81f776d4..a048cfa3 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.spec.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.spec.ts @@ -160,6 +160,41 @@ describe("LcdClient", () => { ], }); }); + + it("can merge two extension into the same module", async () => { + pendingWithoutWasmd(); + + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + function setupSupplyExtensionBasic(base: LcdClient) { + return { + supply: { + totalAll: async () => { + const path = `/supply/total`; + return base.get(path); + }, + }, + }; + } + + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + function setupSupplyExtensionPremium(base: LcdClient) { + return { + supply: { + total: async (denom: string) => { + return base.get(`/supply/total/${denom}`); + }, + }, + }; + } + + const client = LcdClient.withExtensions( + { apiUrl: wasmd.endpoint }, + setupSupplyExtensionBasic, + setupSupplyExtensionPremium, + ); + expect(client.supply.totalAll).toEqual(jasmine.any(Function)); + expect(client.supply.total).toEqual(jasmine.any(Function)); + }); }); // The /txs endpoints diff --git a/packages/sdk38/src/lcdapi/lcdclient.ts b/packages/sdk38/src/lcdapi/lcdclient.ts index d1685b68..8be43afe 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.ts @@ -204,8 +204,9 @@ export class LcdClient { if (setupExtensionH) extensions.push(setupExtensionH(client)); for (const extension of extensions) { assert(isNonNullObject(extension), `Extension must be a non-null object`); - for (const [key, value] of Object.entries(extension)) { - (client as any)[key] = value; + for (const [moduleKey, moduleValue] of Object.entries(extension)) { + const current = (client as any)[moduleKey] || {}; + (client as any)[moduleKey] = Object.assign(current, moduleValue); } } From a317dac01e51f0d76165660d3c9023d4b4240770 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 14:39:03 +0200 Subject: [PATCH 30/37] Export setupWasmExtension, WasmExtension --- packages/cosmwasm/src/index.ts | 1 + packages/cosmwasm/src/lcdapi/wasm.spec.ts | 4 ++-- packages/cosmwasm/src/lcdapi/wasm.ts | 2 +- packages/cosmwasm/types/index.d.ts | 1 + packages/cosmwasm/types/lcdapi/wasm.d.ts | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/cosmwasm/src/index.ts b/packages/cosmwasm/src/index.ts index 3c554430..993c143e 100644 --- a/packages/cosmwasm/src/index.ts +++ b/packages/cosmwasm/src/index.ts @@ -1,6 +1,7 @@ import * as logs from "./logs"; export { logs }; +export { setupWasmExtension, WasmExtension } from "./lcdapi/wasm"; export { RestClient } from "./restclient"; export { Account, diff --git a/packages/cosmwasm/src/lcdapi/wasm.spec.ts b/packages/cosmwasm/src/lcdapi/wasm.spec.ts index 596ad3e0..08dac3cf 100644 --- a/packages/cosmwasm/src/lcdapi/wasm.spec.ts +++ b/packages/cosmwasm/src/lcdapi/wasm.spec.ts @@ -37,12 +37,12 @@ import { wasmd, wasmdEnabled, } from "../testutils.spec"; -import { setupWasmModule, WasmExtension } from "./wasm"; +import { setupWasmExtension, WasmExtension } from "./wasm"; type WasmClient = LcdClient & AuthExtension & WasmExtension; function makeWasmClient(apiUrl: string): WasmClient { - return LcdClient.withExtensions({ apiUrl }, setupAuthExtension, setupWasmModule); + return LcdClient.withExtensions({ apiUrl }, setupAuthExtension, setupWasmExtension); } async function uploadContract( diff --git a/packages/cosmwasm/src/lcdapi/wasm.ts b/packages/cosmwasm/src/lcdapi/wasm.ts index 521faeb6..b948184b 100644 --- a/packages/cosmwasm/src/lcdapi/wasm.ts +++ b/packages/cosmwasm/src/lcdapi/wasm.ts @@ -113,7 +113,7 @@ export interface WasmExtension { }; } -export function setupWasmModule(base: LcdClient): WasmExtension { +export function setupWasmExtension(base: LcdClient): WasmExtension { return { wasm: { listCodeInfo: async () => { diff --git a/packages/cosmwasm/types/index.d.ts b/packages/cosmwasm/types/index.d.ts index 0cd933f0..a56155bb 100644 --- a/packages/cosmwasm/types/index.d.ts +++ b/packages/cosmwasm/types/index.d.ts @@ -1,5 +1,6 @@ import * as logs from "./logs"; export { logs }; +export { setupWasmExtension, WasmExtension } from "./lcdapi/wasm"; export { RestClient } from "./restclient"; export { Account, diff --git a/packages/cosmwasm/types/lcdapi/wasm.d.ts b/packages/cosmwasm/types/lcdapi/wasm.d.ts index 8859ffad..bf01a01e 100644 --- a/packages/cosmwasm/types/lcdapi/wasm.d.ts +++ b/packages/cosmwasm/types/lcdapi/wasm.d.ts @@ -71,4 +71,4 @@ export interface WasmExtension { readonly queryContractSmart: (address: string, query: object) => Promise; }; } -export declare function setupWasmModule(base: LcdClient): WasmExtension; +export declare function setupWasmExtension(base: LcdClient): WasmExtension; From dc661dfe5cbbd6f4d414d34b75f750f1e869d5e9 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 14:49:01 +0200 Subject: [PATCH 31/37] Replace RestClient with LcdClient --- .../src/cosmwasmclient.searchtx.spec.ts | 18 +++++--- packages/cosmwasm/src/cosmwasmclient.spec.ts | 10 ++--- packages/cosmwasm/src/cosmwasmclient.ts | 43 ++++++++++-------- .../src/signingcosmwasmclient.spec.ts | 44 ++++++++++--------- packages/cosmwasm/types/cosmwasmclient.d.ts | 17 +++++-- 5 files changed, 80 insertions(+), 52 deletions(-) diff --git a/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts b/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts index 13b44308..e114e219 100644 --- a/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts +++ b/packages/cosmwasm/src/cosmwasmclient.searchtx.spec.ts @@ -1,10 +1,18 @@ /* eslint-disable @typescript-eslint/camelcase */ -import { Coin, coins, CosmosSdkTx, isMsgSend, makeSignBytes, MsgSend, Secp256k1Pen } from "@cosmjs/sdk38"; +import { + Coin, + coins, + CosmosSdkTx, + isMsgSend, + LcdClient, + makeSignBytes, + MsgSend, + Secp256k1Pen, +} from "@cosmjs/sdk38"; import { assert, sleep } from "@cosmjs/utils"; import { CosmWasmClient, isPostTxFailure } from "./cosmwasmclient"; import { isMsgExecuteContract, isMsgInstantiateContract } from "./msgs"; -import { RestClient } from "./restclient"; import { SigningCosmWasmClient } from "./signingcosmwasmclient"; import { alice, @@ -50,7 +58,7 @@ describe("CosmWasmClient.searchTx", () => { const transferAmount = coins(1234567, "ucosm"); const result = await client.sendTokens(recipient, transferAmount); await sleep(75); // wait until tx is indexed - const txDetails = await new RestClient(wasmd.endpoint).txById(result.transactionHash); + const txDetails = await new LcdClient(wasmd.endpoint).txById(result.transactionHash); sendSuccessful = { sender: alice.address0, recipient: recipient, @@ -68,7 +76,7 @@ describe("CosmWasmClient.searchTx", () => { }; const result = await client.sendTokens(recipient, [transferAmount]); await sleep(75); // wait until tx is indexed - const txDetails = await new RestClient(wasmd.endpoint).txById(result.transactionHash); + const txDetails = await new LcdClient(wasmd.endpoint).txById(result.transactionHash); sendSelfSuccessful = { sender: alice.address0, recipient: recipient, @@ -132,7 +140,7 @@ describe("CosmWasmClient.searchTx", () => { }; const result = await client.execute(hashInstance, msg); await sleep(75); // wait until tx is indexed - const txDetails = await new RestClient(wasmd.endpoint).txById(result.transactionHash); + const txDetails = await new LcdClient(wasmd.endpoint).txById(result.transactionHash); execute = { sender: alice.address0, contract: hashInstance, diff --git a/packages/cosmwasm/src/cosmwasmclient.spec.ts b/packages/cosmwasm/src/cosmwasmclient.spec.ts index 0d176f63..3928a075 100644 --- a/packages/cosmwasm/src/cosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/cosmwasmclient.spec.ts @@ -54,7 +54,7 @@ describe("CosmWasmClient", () => { pendingWithoutWasmd(); const client = new CosmWasmClient(wasmd.endpoint); const openedClient = (client as unknown) as PrivateCosmWasmClient; - const getCodeSpy = spyOn(openedClient.restClient, "nodeInfo").and.callThrough(); + const getCodeSpy = spyOn(openedClient.lcdClient, "nodeInfo").and.callThrough(); expect(await client.getChainId()).toEqual(wasmd.chainId); // from network expect(await client.getChainId()).toEqual(wasmd.chainId); // from cache @@ -68,7 +68,7 @@ describe("CosmWasmClient", () => { pendingWithoutWasmd(); const client = new CosmWasmClient(wasmd.endpoint); const openedClient = (client as unknown) as PrivateCosmWasmClient; - const blockLatestSpy = spyOn(openedClient.restClient, "blocksLatest").and.callThrough(); + const blockLatestSpy = spyOn(openedClient.lcdClient, "blocksLatest").and.callThrough(); const height1 = await client.getHeight(); expect(height1).toBeGreaterThan(0); @@ -85,8 +85,8 @@ describe("CosmWasmClient", () => { const client = new CosmWasmClient(wasmd.endpoint); const openedClient = (client as unknown) as PrivateCosmWasmClient; - const blockLatestSpy = spyOn(openedClient.restClient, "blocksLatest").and.callThrough(); - const authAccountsSpy = spyOn(openedClient.restClient, "authAccounts").and.callThrough(); + const blockLatestSpy = spyOn(openedClient.lcdClient, "blocksLatest").and.callThrough(); + const authAccountsSpy = spyOn(openedClient.lcdClient.auth, "account").and.callThrough(); const height1 = await client.getHeight(); expect(height1).toBeGreaterThan(0); @@ -292,7 +292,7 @@ describe("CosmWasmClient", () => { pendingWithoutWasmd(); const client = new CosmWasmClient(wasmd.endpoint); const openedClient = (client as unknown) as PrivateCosmWasmClient; - const getCodeSpy = spyOn(openedClient.restClient, "getCode").and.callThrough(); + const getCodeSpy = spyOn(openedClient.lcdClient.wasm, "getCode").and.callThrough(); const result1 = await client.getCodeDetails(deployedErc20.codeId); // from network const result2 = await client.getCodeDetails(deployedErc20.codeId); // from cache diff --git a/packages/cosmwasm/src/cosmwasmclient.ts b/packages/cosmwasm/src/cosmwasmclient.ts index 1df52b9f..66bd2286 100644 --- a/packages/cosmwasm/src/cosmwasmclient.ts +++ b/packages/cosmwasm/src/cosmwasmclient.ts @@ -2,17 +2,20 @@ import { Sha256 } from "@cosmjs/crypto"; import { fromBase64, fromHex, toHex } from "@cosmjs/encoding"; import { Uint53 } from "@cosmjs/math"; import { + AuthExtension, BroadcastMode, Coin, CosmosSdkTx, decodeBech32Pubkey, IndexedTx, + LcdClient, PubKey, + setupAuthExtension, StdTx, } from "@cosmjs/sdk38"; +import { setupWasmExtension, WasmExtension } from "./lcdapi/wasm"; import { Log, parseLogs } from "./logs"; -import { RestClient } from "./restclient"; import { JsonObject } from "./types"; export interface GetNonceResult { @@ -160,11 +163,11 @@ export interface Block { /** Use for testing only */ export interface PrivateCosmWasmClient { - readonly restClient: RestClient; + readonly lcdClient: LcdClient & AuthExtension & WasmExtension; } export class CosmWasmClient { - protected readonly restClient: RestClient; + protected readonly lcdClient: LcdClient & AuthExtension & WasmExtension; /** Any address the chain considers valid (valid bech32 with proper prefix) */ protected anyValidAddress: string | undefined; @@ -181,12 +184,16 @@ export class CosmWasmClient { * @param broadcastMode Defines at which point of the transaction processing the postTx method (i.e. transaction broadcasting) returns */ public constructor(apiUrl: string, broadcastMode = BroadcastMode.Block) { - this.restClient = new RestClient(apiUrl, broadcastMode); + this.lcdClient = LcdClient.withExtensions( + { apiUrl: apiUrl, broadcastMode: broadcastMode }, + setupAuthExtension, + setupWasmExtension, + ); } public async getChainId(): Promise { if (!this.chainId) { - const response = await this.restClient.nodeInfo(); + const response = await this.lcdClient.nodeInfo(); const chainId = response.node_info.network; if (!chainId) throw new Error("Chain ID must not be empty"); this.chainId = chainId; @@ -197,12 +204,12 @@ export class CosmWasmClient { public async getHeight(): Promise { if (this.anyValidAddress) { - const { height } = await this.restClient.authAccounts(this.anyValidAddress); + const { height } = await this.lcdClient.auth.account(this.anyValidAddress); return parseInt(height, 10); } else { // Note: this gets inefficient when blocks contain a lot of transactions since it // requires downloading and deserializing all transactions in the block. - const latest = await this.restClient.blocksLatest(); + const latest = await this.lcdClient.blocksLatest(); return parseInt(latest.block.header.height, 10); } } @@ -212,7 +219,7 @@ export class CosmWasmClient { */ public async getIdentifier(tx: CosmosSdkTx): Promise { // We consult the REST API because we don't have a local amino encoder - const response = await this.restClient.encodeTx(tx); + const response = await this.lcdClient.encodeTx(tx); const hash = new Sha256(fromBase64(response.tx)).digest(); return toHex(hash).toUpperCase(); } @@ -238,7 +245,7 @@ export class CosmWasmClient { } public async getAccount(address: string): Promise { - const account = await this.restClient.authAccounts(address); + const account = await this.lcdClient.auth.account(address); const value = account.result.value; if (value.address === "") { return undefined; @@ -261,7 +268,7 @@ export class CosmWasmClient { */ public async getBlock(height?: number): Promise { const response = - height !== undefined ? await this.restClient.blocks(height) : await this.restClient.blocksLatest(); + height !== undefined ? await this.lcdClient.blocks(height) : await this.lcdClient.blocksLatest(); return { id: response.block_id.hash, @@ -333,7 +340,7 @@ export class CosmWasmClient { } public async postTx(tx: StdTx): Promise { - const result = await this.restClient.postTx(tx); + const result = await this.lcdClient.postTx(tx); if (!result.txhash.match(/^([0-9A-F][0-9A-F])+$/)) { throw new Error("Received ill-formatted txhash. Must be non-empty upper-case hex"); } @@ -354,7 +361,7 @@ export class CosmWasmClient { } public async getCodes(): Promise { - const result = await this.restClient.listCodeInfo(); + const result = await this.lcdClient.wasm.listCodeInfo(); return result.map( (entry): Code => { this.anyValidAddress = entry.creator; @@ -373,7 +380,7 @@ export class CosmWasmClient { const cached = this.codesCache.get(codeId); if (cached) return cached; - const getCodeResult = await this.restClient.getCode(codeId); + const getCodeResult = await this.lcdClient.wasm.getCode(codeId); const codeDetails: CodeDetails = { id: getCodeResult.id, creator: getCodeResult.creator, @@ -387,7 +394,7 @@ export class CosmWasmClient { } public async getContracts(codeId: number): Promise { - const result = await this.restClient.listContractsByCodeId(codeId); + const result = await this.lcdClient.wasm.listContractsByCodeId(codeId); return result.map( (entry): Contract => ({ address: entry.address, @@ -403,7 +410,7 @@ export class CosmWasmClient { * Throws an error if no contract was found at the address */ public async getContract(address: string): Promise { - const result = await this.restClient.getContractInfo(address); + const result = await this.lcdClient.wasm.getContractInfo(address); if (!result) throw new Error(`No contract found at address "${address}"`); return { address: result.address, @@ -425,7 +432,7 @@ export class CosmWasmClient { // just test contract existence const _info = await this.getContract(address); - return this.restClient.queryContractRaw(address, key); + return this.lcdClient.wasm.queryContractRaw(address, key); } /** @@ -437,7 +444,7 @@ export class CosmWasmClient { */ public async queryContractSmart(address: string, queryMsg: object): Promise { try { - return await this.restClient.queryContractSmart(address, queryMsg); + return await this.lcdClient.wasm.queryContractSmart(address, queryMsg); } catch (error) { if (error instanceof Error) { if (error.message.startsWith("not found: contract")) { @@ -454,7 +461,7 @@ export class CosmWasmClient { private async txsQuery(query: string): Promise { // TODO: we need proper pagination support const limit = 100; - const result = await this.restClient.txsQuery(`${query}&limit=${limit}`); + const result = await this.lcdClient.txsQuery(`${query}&limit=${limit}`); const pages = parseInt(result.page_total, 10); if (pages > 1) { throw new Error( diff --git a/packages/cosmwasm/src/signingcosmwasmclient.spec.ts b/packages/cosmwasm/src/signingcosmwasmclient.spec.ts index b828edde..b1e71f07 100644 --- a/packages/cosmwasm/src/signingcosmwasmclient.spec.ts +++ b/packages/cosmwasm/src/signingcosmwasmclient.spec.ts @@ -1,15 +1,19 @@ import { Sha256 } from "@cosmjs/crypto"; import { toHex } from "@cosmjs/encoding"; -import { coin, coins, Secp256k1Pen } from "@cosmjs/sdk38"; +import { AuthExtension, coin, coins, LcdClient, Secp256k1Pen, setupAuthExtension } from "@cosmjs/sdk38"; import { assert } from "@cosmjs/utils"; import { isPostTxFailure, PrivateCosmWasmClient } from "./cosmwasmclient"; -import { RestClient } from "./restclient"; +import { setupWasmExtension, WasmExtension } from "./lcdapi/wasm"; import { SigningCosmWasmClient, UploadMeta } from "./signingcosmwasmclient"; import { alice, getHackatom, makeRandomAddress, pendingWithoutWasmd, unused } from "./testutils.spec"; const httpUrl = "http://localhost:1317"; +function makeWasmClient(apiUrl: string): LcdClient & AuthExtension & WasmExtension { + return LcdClient.withExtensions({ apiUrl }, setupAuthExtension, setupWasmExtension); +} + describe("SigningCosmWasmClient", () => { describe("makeReadOnly", () => { it("can be constructed", async () => { @@ -26,8 +30,8 @@ describe("SigningCosmWasmClient", () => { const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes)); const openedClient = (client as unknown) as PrivateCosmWasmClient; - const blockLatestSpy = spyOn(openedClient.restClient, "blocksLatest").and.callThrough(); - const authAccountsSpy = spyOn(openedClient.restClient, "authAccounts").and.callThrough(); + const blockLatestSpy = spyOn(openedClient.lcdClient, "blocksLatest").and.callThrough(); + const authAccountsSpy = spyOn(openedClient.lcdClient.auth, "account").and.callThrough(); const height = await client.getHeight(); expect(height).toBeGreaterThan(0); @@ -97,8 +101,8 @@ describe("SigningCosmWasmClient", () => { }, ); - const rest = new RestClient(httpUrl); - const balance = (await rest.authAccounts(contractAddress)).result.value.coins; + const lcdClient = makeWasmClient(httpUrl); + const balance = (await lcdClient.auth.account(contractAddress)).result.value.coins; expect(balance).toEqual(transferAmount); }); @@ -119,8 +123,8 @@ describe("SigningCosmWasmClient", () => { { admin: unused.address }, ); - const rest = new RestClient(httpUrl); - const contract = await rest.getContractInfo(contractAddress); + const lcdClient = makeWasmClient(httpUrl); + const contract = await lcdClient.wasm.getContractInfo(contractAddress); assert(contract); expect(contract.admin).toEqual(unused.address); }); @@ -171,14 +175,14 @@ describe("SigningCosmWasmClient", () => { }, ); - const rest = new RestClient(httpUrl); - const state1 = await rest.getContractInfo(contractAddress); + const lcdClient = makeWasmClient(httpUrl); + const state1 = await lcdClient.wasm.getContractInfo(contractAddress); assert(state1); expect(state1.admin).toEqual(alice.address0); await client.updateAdmin(contractAddress, unused.address); - const state2 = await rest.getContractInfo(contractAddress); + const state2 = await lcdClient.wasm.getContractInfo(contractAddress); assert(state2); expect(state2.admin).toEqual(unused.address); }); @@ -204,14 +208,14 @@ describe("SigningCosmWasmClient", () => { }, ); - const rest = new RestClient(httpUrl); - const state1 = await rest.getContractInfo(contractAddress); + const lcdClient = makeWasmClient(httpUrl); + const state1 = await lcdClient.wasm.getContractInfo(contractAddress); assert(state1); expect(state1.admin).toEqual(alice.address0); await client.clearAdmin(contractAddress); - const state2 = await rest.getContractInfo(contractAddress); + const state2 = await lcdClient.wasm.getContractInfo(contractAddress); assert(state2); expect(state2.admin).toBeUndefined(); }); @@ -238,15 +242,15 @@ describe("SigningCosmWasmClient", () => { }, ); - const rest = new RestClient(httpUrl); - const state1 = await rest.getContractInfo(contractAddress); + const lcdClient = makeWasmClient(httpUrl); + const state1 = await lcdClient.wasm.getContractInfo(contractAddress); assert(state1); expect(state1.admin).toEqual(alice.address0); const newVerifier = makeRandomAddress(); await client.migrate(contractAddress, codeId2, { verifier: newVerifier }); - const state2 = await rest.getContractInfo(contractAddress); + const state2 = await lcdClient.wasm.getContractInfo(contractAddress); assert(state2); expect(state2).toEqual({ ...state1, @@ -289,10 +293,10 @@ describe("SigningCosmWasmClient", () => { }); // Verify token transfer from contract to beneficiary - const rest = new RestClient(httpUrl); - const beneficiaryBalance = (await rest.authAccounts(beneficiaryAddress)).result.value.coins; + const lcdClient = makeWasmClient(httpUrl); + const beneficiaryBalance = (await lcdClient.auth.account(beneficiaryAddress)).result.value.coins; expect(beneficiaryBalance).toEqual(transferAmount); - const contractBalance = (await rest.authAccounts(contractAddress)).result.value.coins; + const contractBalance = (await lcdClient.auth.account(contractAddress)).result.value.coins; expect(contractBalance).toEqual([]); }); }); diff --git a/packages/cosmwasm/types/cosmwasmclient.d.ts b/packages/cosmwasm/types/cosmwasmclient.d.ts index 82eecb72..c72cdc17 100644 --- a/packages/cosmwasm/types/cosmwasmclient.d.ts +++ b/packages/cosmwasm/types/cosmwasmclient.d.ts @@ -1,6 +1,15 @@ -import { BroadcastMode, Coin, CosmosSdkTx, IndexedTx, PubKey, StdTx } from "@cosmjs/sdk38"; +import { + AuthExtension, + BroadcastMode, + Coin, + CosmosSdkTx, + IndexedTx, + LcdClient, + PubKey, + StdTx, +} from "@cosmjs/sdk38"; +import { WasmExtension } from "./lcdapi/wasm"; import { Log } from "./logs"; -import { RestClient } from "./restclient"; import { JsonObject } from "./types"; export interface GetNonceResult { readonly accountNumber: number; @@ -114,10 +123,10 @@ export interface Block { } /** Use for testing only */ export interface PrivateCosmWasmClient { - readonly restClient: RestClient; + readonly lcdClient: LcdClient & AuthExtension & WasmExtension; } export declare class CosmWasmClient { - protected readonly restClient: RestClient; + protected readonly lcdClient: LcdClient & AuthExtension & WasmExtension; /** Any address the chain considers valid (valid bech32 with proper prefix) */ protected anyValidAddress: string | undefined; private readonly codesCache; From de510c98e586ac55f9c4a055830728480904985f Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 23:42:18 +0200 Subject: [PATCH 32/37] Remove RestClient from sdk38 and cosmwasm --- packages/cli/examples/local_faucet.ts | 2 +- packages/cli/src/cli.ts | 2 +- packages/cosmwasm/src/index.ts | 1 - packages/cosmwasm/src/restclient.spec.ts | 494 ------------- packages/cosmwasm/src/restclient.ts | 163 ----- packages/cosmwasm/types/index.d.ts | 1 - packages/cosmwasm/types/restclient.d.ts | 67 -- packages/sdk38/src/index.ts | 1 - packages/sdk38/src/restclient.spec.ts | 885 ----------------------- packages/sdk38/src/restclient.ts | 168 ----- packages/sdk38/types/index.d.ts | 1 - packages/sdk38/types/restclient.d.ts | 48 -- 12 files changed, 2 insertions(+), 1831 deletions(-) delete mode 100644 packages/cosmwasm/src/restclient.spec.ts delete mode 100644 packages/cosmwasm/src/restclient.ts delete mode 100644 packages/cosmwasm/types/restclient.d.ts delete mode 100644 packages/sdk38/src/restclient.spec.ts delete mode 100644 packages/sdk38/src/restclient.ts delete mode 100644 packages/sdk38/types/restclient.d.ts diff --git a/packages/cli/examples/local_faucet.ts b/packages/cli/examples/local_faucet.ts index 77a447b6..687e3512 100644 --- a/packages/cli/examples/local_faucet.ts +++ b/packages/cli/examples/local_faucet.ts @@ -15,4 +15,4 @@ const faucetMnemonic = const faucetAddress = "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6"; const pen = await Secp256k1Pen.fromMnemonic(faucetMnemonic); -const client = new RestClient(defaultHttpUrl); +const client = new LcdClient(defaultHttpUrl); diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 3ac27c5c..d5ead030 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -96,10 +96,10 @@ export function main(originalArgs: readonly string[]): void { "Msg", "MsgDelegate", "MsgSend", + "LcdClient", "Pen", "PubKey", "pubkeyToAddress", - "RestClient", "Secp256k1Pen", "SigningCosmosClient", "StdFee", diff --git a/packages/cosmwasm/src/index.ts b/packages/cosmwasm/src/index.ts index 993c143e..8613a66a 100644 --- a/packages/cosmwasm/src/index.ts +++ b/packages/cosmwasm/src/index.ts @@ -2,7 +2,6 @@ import * as logs from "./logs"; export { logs }; export { setupWasmExtension, WasmExtension } from "./lcdapi/wasm"; -export { RestClient } from "./restclient"; export { Account, Block, diff --git a/packages/cosmwasm/src/restclient.spec.ts b/packages/cosmwasm/src/restclient.spec.ts deleted file mode 100644 index 7afcd29a..00000000 --- a/packages/cosmwasm/src/restclient.spec.ts +++ /dev/null @@ -1,494 +0,0 @@ -/* eslint-disable @typescript-eslint/camelcase */ -import { Sha256 } from "@cosmjs/crypto"; -import { Bech32, fromAscii, fromBase64, fromHex, toAscii, toBase64, toHex } from "@cosmjs/encoding"; -import { Coin, coin, coins, makeSignBytes, Pen, PostTxsResponse, Secp256k1Pen, StdFee } from "@cosmjs/sdk38"; -import { assert } from "@cosmjs/utils"; - -import { findAttribute, parseLogs } from "./logs"; -import { - isMsgInstantiateContract, - isMsgStoreCode, - MsgExecuteContract, - MsgInstantiateContract, - MsgStoreCode, -} from "./msgs"; -import { RestClient } from "./restclient"; -import { - alice, - bech32AddressMatcher, - ContractUploadInstructions, - deployedErc20, - fromOneElementArray, - getHackatom, - makeRandomAddress, - makeSignedTx, - pendingWithoutWasmd, - wasmd, - wasmdEnabled, -} from "./testutils.spec"; - -async function uploadContract( - client: RestClient, - pen: Pen, - contract: ContractUploadInstructions, -): Promise { - const memo = "My first contract on chain"; - const theMsg: MsgStoreCode = { - type: "wasm/store-code", - value: { - sender: alice.address0, - wasm_byte_code: toBase64(contract.data), - source: contract.source || "", - builder: contract.builder || "", - }, - }; - const fee: StdFee = { - amount: [ - { - amount: "5000000", - denom: "ucosm", - }, - ], - gas: "89000000", - }; - - const { account_number, sequence } = (await client.authAccounts(alice.address0)).result.value; - const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); - const signature = await pen.sign(signBytes); - const signedTx = makeSignedTx(theMsg, fee, memo, signature); - return client.postTx(signedTx); -} - -async function instantiateContract( - client: RestClient, - pen: Pen, - codeId: number, - beneficiaryAddress: string, - transferAmount?: readonly Coin[], -): Promise { - const memo = "Create an escrow instance"; - const theMsg: MsgInstantiateContract = { - type: "wasm/instantiate", - value: { - sender: alice.address0, - code_id: codeId.toString(), - label: "my escrow", - init_msg: { - verifier: alice.address0, - beneficiary: beneficiaryAddress, - }, - init_funds: transferAmount || [], - }, - }; - const fee: StdFee = { - amount: [ - { - amount: "5000000", - denom: "ucosm", - }, - ], - gas: "89000000", - }; - - const { account_number, sequence } = (await client.authAccounts(alice.address0)).result.value; - const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); - const signature = await pen.sign(signBytes); - const signedTx = makeSignedTx(theMsg, fee, memo, signature); - return client.postTx(signedTx); -} - -async function executeContract( - client: RestClient, - pen: Pen, - contractAddress: string, - msg: object, -): Promise { - const memo = "Time for action"; - const theMsg: MsgExecuteContract = { - type: "wasm/execute", - value: { - sender: alice.address0, - contract: contractAddress, - msg: msg, - sent_funds: [], - }, - }; - const fee: StdFee = { - amount: coins(5000000, "ucosm"), - gas: "89000000", - }; - - const { account_number, sequence } = (await client.authAccounts(alice.address0)).result.value; - const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); - const signature = await pen.sign(signBytes); - const signedTx = makeSignedTx(theMsg, fee, memo, signature); - return client.postTx(signedTx); -} - -describe("RestClient", () => { - it("can be constructed", () => { - const client = new RestClient(wasmd.endpoint); - expect(client).toBeTruthy(); - }); - - describe("txsQuery", () => { - it("can query by tags (module + code_id)", async () => { - pendingWithoutWasmd(); - const client = new RestClient(wasmd.endpoint); - const result = await client.txsQuery(`message.module=wasm&message.code_id=${deployedErc20.codeId}`); - expect(parseInt(result.count, 10)).toBeGreaterThanOrEqual(4); - - // Check first 4 results - const [store, hash, isa, jade] = result.txs.map((tx) => fromOneElementArray(tx.tx.value.msg)); - assert(isMsgStoreCode(store)); - assert(isMsgInstantiateContract(hash)); - assert(isMsgInstantiateContract(isa)); - assert(isMsgInstantiateContract(jade)); - expect(store.value).toEqual( - jasmine.objectContaining({ - sender: alice.address0, - source: deployedErc20.source, - builder: deployedErc20.builder, - }), - ); - expect(hash.value).toEqual({ - code_id: deployedErc20.codeId.toString(), - init_funds: [], - init_msg: jasmine.objectContaining({ - symbol: "HASH", - }), - label: "HASH", - sender: alice.address0, - }); - expect(isa.value).toEqual({ - code_id: deployedErc20.codeId.toString(), - init_funds: [], - init_msg: jasmine.objectContaining({ symbol: "ISA" }), - label: "ISA", - sender: alice.address0, - }); - expect(jade.value).toEqual({ - code_id: deployedErc20.codeId.toString(), - init_funds: [], - init_msg: jasmine.objectContaining({ symbol: "JADE" }), - label: "JADE", - sender: alice.address0, - admin: alice.address1, - }); - }); - - // Like previous test but filtered by message.action=store-code and message.action=instantiate - it("can query by tags (module + code_id + action)", async () => { - pendingWithoutWasmd(); - const client = new RestClient(wasmd.endpoint); - - { - const uploads = await client.txsQuery( - `message.module=wasm&message.code_id=${deployedErc20.codeId}&message.action=store-code`, - ); - expect(parseInt(uploads.count, 10)).toEqual(1); - const store = fromOneElementArray(uploads.txs[0].tx.value.msg); - assert(isMsgStoreCode(store)); - expect(store.value).toEqual( - jasmine.objectContaining({ - sender: alice.address0, - source: deployedErc20.source, - builder: deployedErc20.builder, - }), - ); - } - - { - const instantiations = await client.txsQuery( - `message.module=wasm&message.code_id=${deployedErc20.codeId}&message.action=instantiate`, - ); - expect(parseInt(instantiations.count, 10)).toBeGreaterThanOrEqual(3); - const [hash, isa, jade] = instantiations.txs.map((tx) => fromOneElementArray(tx.tx.value.msg)); - assert(isMsgInstantiateContract(hash)); - assert(isMsgInstantiateContract(isa)); - assert(isMsgInstantiateContract(jade)); - expect(hash.value).toEqual({ - code_id: deployedErc20.codeId.toString(), - init_funds: [], - init_msg: jasmine.objectContaining({ - symbol: "HASH", - }), - label: "HASH", - sender: alice.address0, - }); - expect(isa.value).toEqual({ - code_id: deployedErc20.codeId.toString(), - init_funds: [], - init_msg: jasmine.objectContaining({ symbol: "ISA" }), - label: "ISA", - sender: alice.address0, - }); - expect(jade.value).toEqual({ - code_id: deployedErc20.codeId.toString(), - init_funds: [], - init_msg: jasmine.objectContaining({ symbol: "JADE" }), - label: "JADE", - sender: alice.address0, - admin: alice.address1, - }); - } - }); - }); - - describe("postTx", () => { - it("can upload, instantiate and execute wasm", async () => { - pendingWithoutWasmd(); - const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic); - const client = new RestClient(wasmd.endpoint); - - const transferAmount = [coin(1234, "ucosm"), coin(321, "ustake")]; - const beneficiaryAddress = makeRandomAddress(); - - let codeId: number; - - // upload - { - // console.log("Raw log:", result.raw_log); - const result = await uploadContract(client, pen, getHackatom()); - expect(result.code).toBeFalsy(); - const logs = parseLogs(result.logs); - const codeIdAttr = findAttribute(logs, "message", "code_id"); - codeId = Number.parseInt(codeIdAttr.value, 10); - expect(codeId).toBeGreaterThanOrEqual(1); - expect(codeId).toBeLessThanOrEqual(200); - expect(result.data).toEqual(toHex(toAscii(`${codeId}`)).toUpperCase()); - } - - let contractAddress: string; - - // instantiate - { - const result = await instantiateContract(client, pen, codeId, beneficiaryAddress, transferAmount); - expect(result.code).toBeFalsy(); - // console.log("Raw log:", result.raw_log); - const logs = parseLogs(result.logs); - const contractAddressAttr = findAttribute(logs, "message", "contract_address"); - contractAddress = contractAddressAttr.value; - const amountAttr = findAttribute(logs, "transfer", "amount"); - expect(amountAttr.value).toEqual("1234ucosm,321ustake"); - expect(result.data).toEqual(toHex(Bech32.decode(contractAddress).data).toUpperCase()); - - const balance = (await client.authAccounts(contractAddress)).result.value.coins; - expect(balance).toEqual(transferAmount); - } - - // execute - { - const result = await executeContract(client, pen, contractAddress, { release: {} }); - expect(result.data).toEqual("F00BAA"); - expect(result.code).toBeFalsy(); - // console.log("Raw log:", result.logs); - const logs = parseLogs(result.logs); - const wasmEvent = logs.find(() => true)?.events.find((e) => e.type === "wasm"); - assert(wasmEvent, "Event of type wasm expected"); - expect(wasmEvent.attributes).toContain({ key: "action", value: "release" }); - expect(wasmEvent.attributes).toContain({ - key: "destination", - value: beneficiaryAddress, - }); - - // Verify token transfer from contract to beneficiary - const beneficiaryBalance = (await client.authAccounts(beneficiaryAddress)).result.value.coins; - expect(beneficiaryBalance).toEqual(transferAmount); - const contractBalance = (await client.authAccounts(contractAddress)).result.value.coins; - expect(contractBalance).toEqual([]); - } - }); - }); - - // The /wasm endpoints - - describe("query", () => { - it("can list upload code", async () => { - pendingWithoutWasmd(); - const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic); - const client = new RestClient(wasmd.endpoint); - - // check with contracts were here first to compare - const existingInfos = await client.listCodeInfo(); - existingInfos.forEach((val, idx) => expect(val.id).toEqual(idx + 1)); - const numExisting = existingInfos.length; - - // upload data - const hackatom = getHackatom(); - const result = await uploadContract(client, pen, hackatom); - expect(result.code).toBeFalsy(); - const logs = parseLogs(result.logs); - const codeIdAttr = findAttribute(logs, "message", "code_id"); - const codeId = Number.parseInt(codeIdAttr.value, 10); - - // ensure we were added to the end of the list - const newInfos = await client.listCodeInfo(); - expect(newInfos.length).toEqual(numExisting + 1); - const lastInfo = newInfos[newInfos.length - 1]; - expect(lastInfo.id).toEqual(codeId); - expect(lastInfo.creator).toEqual(alice.address0); - - // ensure metadata is present - expect(lastInfo.source).toEqual(hackatom.source); - expect(lastInfo.builder).toEqual(hackatom.builder); - - // check code hash matches expectation - const wasmHash = new Sha256(hackatom.data).digest(); - expect(lastInfo.data_hash.toLowerCase()).toEqual(toHex(wasmHash)); - - // download code and check against auto-gen - const { data } = await client.getCode(codeId); - expect(fromBase64(data)).toEqual(hackatom.data); - }); - - it("can list contracts and get info", async () => { - pendingWithoutWasmd(); - const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic); - const client = new RestClient(wasmd.endpoint); - const beneficiaryAddress = makeRandomAddress(); - const transferAmount: readonly Coin[] = [ - { - amount: "707707", - denom: "ucosm", - }, - ]; - - // reuse an existing contract, or upload if needed - let codeId: number; - const existingInfos = await client.listCodeInfo(); - if (existingInfos.length > 0) { - codeId = existingInfos[existingInfos.length - 1].id; - } else { - const uploadResult = await uploadContract(client, pen, getHackatom()); - expect(uploadResult.code).toBeFalsy(); - const uploadLogs = parseLogs(uploadResult.logs); - const codeIdAttr = findAttribute(uploadLogs, "message", "code_id"); - codeId = Number.parseInt(codeIdAttr.value, 10); - } - - // create new instance and compare before and after - const existingContractsByCode = await client.listContractsByCodeId(codeId); - for (const contract of existingContractsByCode) { - expect(contract.address).toMatch(bech32AddressMatcher); - expect(contract.code_id).toEqual(codeId); - expect(contract.creator).toMatch(bech32AddressMatcher); - expect(contract.label).toMatch(/^.+$/); - } - - const result = await instantiateContract(client, pen, codeId, beneficiaryAddress, transferAmount); - expect(result.code).toBeFalsy(); - const logs = parseLogs(result.logs); - const contractAddressAttr = findAttribute(logs, "message", "contract_address"); - const myAddress = contractAddressAttr.value; - - const newContractsByCode = await client.listContractsByCodeId(codeId); - expect(newContractsByCode.length).toEqual(existingContractsByCode.length + 1); - const newContract = newContractsByCode[newContractsByCode.length - 1]; - expect(newContract).toEqual( - jasmine.objectContaining({ - code_id: codeId, - creator: alice.address0, - label: "my escrow", - }), - ); - - // check out info - const myInfo = await client.getContractInfo(myAddress); - assert(myInfo); - expect(myInfo).toEqual( - jasmine.objectContaining({ - code_id: codeId, - creator: alice.address0, - init_msg: jasmine.objectContaining({ - beneficiary: beneficiaryAddress, - }), - }), - ); - expect(myInfo.admin).toBeUndefined(); - - // make sure random addresses don't give useful info - const nonExistentAddress = makeRandomAddress(); - expect(await client.getContractInfo(nonExistentAddress)).toBeNull(); - }); - - describe("contract state", () => { - const client = new RestClient(wasmd.endpoint); - const noContract = makeRandomAddress(); - const expectedKey = toAscii("config"); - let contractAddress: string | undefined; - - beforeAll(async () => { - if (wasmdEnabled()) { - const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic); - const uploadResult = await uploadContract(client, pen, getHackatom()); - assert(!uploadResult.code); - const uploadLogs = parseLogs(uploadResult.logs); - const codeId = Number.parseInt(findAttribute(uploadLogs, "message", "code_id").value, 10); - const instantiateResult = await instantiateContract(client, pen, codeId, makeRandomAddress()); - assert(!instantiateResult.code); - const instantiateLogs = parseLogs(instantiateResult.logs); - const contractAddressAttr = findAttribute(instantiateLogs, "message", "contract_address"); - contractAddress = contractAddressAttr.value; - } - }); - - it("can get all state", async () => { - pendingWithoutWasmd(); - - // get contract state - const state = await client.getAllContractState(contractAddress!); - expect(state.length).toEqual(1); - const data = state[0]; - expect(data.key).toEqual(expectedKey); - const value = JSON.parse(fromAscii(data.val)); - expect(value.verifier).toBeDefined(); - expect(value.beneficiary).toBeDefined(); - - // bad address is empty array - const noContractState = await client.getAllContractState(noContract); - expect(noContractState).toEqual([]); - }); - - it("can query by key", async () => { - pendingWithoutWasmd(); - - // query by one key - const raw = await client.queryContractRaw(contractAddress!, expectedKey); - assert(raw, "must get result"); - const model = JSON.parse(fromAscii(raw)); - expect(model.verifier).toBeDefined(); - expect(model.beneficiary).toBeDefined(); - - // missing key is null - const missing = await client.queryContractRaw(contractAddress!, fromHex("cafe0dad")); - expect(missing).toBeNull(); - - // bad address is null - const noContractModel = await client.queryContractRaw(noContract, expectedKey); - expect(noContractModel).toBeNull(); - }); - - it("can make smart queries", async () => { - pendingWithoutWasmd(); - - // we can query the verifier properly - const resultDocument = await client.queryContractSmart(contractAddress!, { verifier: {} }); - expect(resultDocument).toEqual({ verifier: alice.address0 }); - - // invalid query syntax throws an error - await client.queryContractSmart(contractAddress!, { nosuchkey: {} }).then( - () => fail("shouldn't succeed"), - (error) => - expect(error).toMatch(/query wasm contract failed: parsing hackatom::contract::QueryMsg/), - ); - - // invalid address throws an error - await client.queryContractSmart(noContract, { verifier: {} }).then( - () => fail("shouldn't succeed"), - (error) => expect(error).toMatch("not found"), - ); - }); - }); - }); -}); diff --git a/packages/cosmwasm/src/restclient.ts b/packages/cosmwasm/src/restclient.ts deleted file mode 100644 index d5406da6..00000000 --- a/packages/cosmwasm/src/restclient.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { fromBase64, fromUtf8, toHex, toUtf8 } from "@cosmjs/encoding"; -import { BroadcastMode, RestClient as BaseRestClient } from "@cosmjs/sdk38"; - -import { JsonObject, Model, parseWasmData, WasmData } from "./types"; - -// Currently all wasm query responses return json-encoded strings... -// later deprecate this and use the specific types for result -// (assuming it is inlined, no second parse needed) -type WasmResponse = WasmSuccess | WasmError; - -interface WasmSuccess { - readonly height: string; - readonly result: T; -} - -interface WasmError { - readonly error: string; -} - -export interface CodeInfo { - readonly id: number; - /** Bech32 account address */ - readonly creator: string; - /** Hex-encoded sha256 hash of the code stored here */ - readonly data_hash: string; - /** - * An URL to a .tar.gz archive of the source code of the contract, which can be used to reproducibly build the Wasm bytecode. - * - * @see https://github.com/CosmWasm/cosmwasm-verify - */ - readonly source?: string; - /** - * A docker image (including version) to reproducibly build the Wasm bytecode from the source code. - * - * @example ```cosmwasm/rust-optimizer:0.8.0``` - * @see https://github.com/CosmWasm/cosmwasm-verify - */ - readonly builder?: string; -} - -export interface CodeDetails extends CodeInfo { - /** Base64 encoded raw wasm data */ - readonly data: string; -} - -// This is list view, without contract info -export interface ContractInfo { - readonly address: string; - readonly code_id: number; - /** Bech32 account address */ - readonly creator: string; - /** Bech32-encoded admin address */ - readonly admin?: string; - readonly label: string; -} - -export interface ContractDetails extends ContractInfo { - /** Argument passed on initialization of the contract */ - readonly init_msg: object; -} - -interface SmartQueryResponse { - // base64 encoded response - readonly smart: string; -} - -/** Unfortunately, Cosmos SDK encodes empty arrays as null */ -type CosmosSdkArray = readonly T[] | null; - -function normalizeArray(backend: CosmosSdkArray): readonly T[] { - return backend || []; -} - -function isWasmError(resp: WasmResponse): resp is WasmError { - return (resp as WasmError).error !== undefined; -} - -function unwrapWasmResponse(response: WasmResponse): T { - if (isWasmError(response)) { - throw new Error(response.error); - } - return response.result; -} - -export class RestClient extends BaseRestClient { - /** - * Creates a new client to interact with a Cosmos SDK light client daemon. - * This class tries to be a direct mapping onto the API. Some basic decoding and normalizatin is done - * but things like caching are done at a higher level. - * - * When building apps, you should not need to use this class directly. If you do, this indicates a missing feature - * in higher level components. Feel free to raise an issue in this case. - * - * @param apiUrl The URL of a Cosmos SDK light client daemon API (sometimes called REST server or REST API) - * @param broadcastMode Defines at which point of the transaction processing the postTx method (i.e. transaction broadcasting) returns - */ - public constructor(apiUrl: string, broadcastMode = BroadcastMode.Block) { - super(apiUrl, broadcastMode); - } - - // The /wasm endpoints - - // wasm rest queries are listed here: https://github.com/cosmwasm/wasmd/blob/master/x/wasm/client/rest/query.go#L19-L27 - public async listCodeInfo(): Promise { - const path = `/wasm/code`; - const responseData = (await this.get(path)) as WasmResponse>; - return normalizeArray(unwrapWasmResponse(responseData)); - } - - // this will download the original wasm bytecode by code id - // throws error if no code with this id - public async getCode(id: number): Promise { - const path = `/wasm/code/${id}`; - const responseData = (await this.get(path)) as WasmResponse; - return unwrapWasmResponse(responseData); - } - - public async listContractsByCodeId(id: number): Promise { - const path = `/wasm/code/${id}/contracts`; - const responseData = (await this.get(path)) as WasmResponse>; - return normalizeArray(unwrapWasmResponse(responseData)); - } - - /** - * Returns null when contract was not found at this address. - */ - public async getContractInfo(address: string): Promise { - const path = `/wasm/contract/${address}`; - const response = (await this.get(path)) as WasmResponse; - return unwrapWasmResponse(response); - } - - // Returns all contract state. - // This is an empty array if no such contract, or contract has no data. - public async getAllContractState(address: string): Promise { - const path = `/wasm/contract/${address}/state`; - const responseData = (await this.get(path)) as WasmResponse>; - return normalizeArray(unwrapWasmResponse(responseData)).map(parseWasmData); - } - - // Returns the data at the key if present (unknown decoded json), - // or null if no data at this (contract address, key) pair - public async queryContractRaw(address: string, key: Uint8Array): Promise { - const hexKey = toHex(key); - const path = `/wasm/contract/${address}/raw/${hexKey}?encoding=hex`; - const responseData = (await this.get(path)) as WasmResponse; - const data = unwrapWasmResponse(responseData); - return data.length === 0 ? null : fromBase64(data[0].val); - } - - /** - * Makes a smart query on the contract and parses the reponse as JSON. - * Throws error if no such contract exists, the query format is invalid or the response is invalid. - */ - public async queryContractSmart(address: string, query: object): Promise { - const encoded = toHex(toUtf8(JSON.stringify(query))); - const path = `/wasm/contract/${address}/smart/${encoded}?encoding=hex`; - const responseData = (await this.get(path)) as WasmResponse; - const result = unwrapWasmResponse(responseData); - // By convention, smart queries must return a valid JSON document (see https://github.com/CosmWasm/cosmwasm/issues/144) - return JSON.parse(fromUtf8(fromBase64(result.smart))); - } -} diff --git a/packages/cosmwasm/types/index.d.ts b/packages/cosmwasm/types/index.d.ts index a56155bb..6a9a884b 100644 --- a/packages/cosmwasm/types/index.d.ts +++ b/packages/cosmwasm/types/index.d.ts @@ -1,7 +1,6 @@ import * as logs from "./logs"; export { logs }; export { setupWasmExtension, WasmExtension } from "./lcdapi/wasm"; -export { RestClient } from "./restclient"; export { Account, Block, diff --git a/packages/cosmwasm/types/restclient.d.ts b/packages/cosmwasm/types/restclient.d.ts deleted file mode 100644 index 43e4be1e..00000000 --- a/packages/cosmwasm/types/restclient.d.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { BroadcastMode, RestClient as BaseRestClient } from "@cosmjs/sdk38"; -import { JsonObject, Model } from "./types"; -export interface CodeInfo { - readonly id: number; - /** Bech32 account address */ - readonly creator: string; - /** Hex-encoded sha256 hash of the code stored here */ - readonly data_hash: string; - /** - * An URL to a .tar.gz archive of the source code of the contract, which can be used to reproducibly build the Wasm bytecode. - * - * @see https://github.com/CosmWasm/cosmwasm-verify - */ - readonly source?: string; - /** - * A docker image (including version) to reproducibly build the Wasm bytecode from the source code. - * - * @example ```cosmwasm/rust-optimizer:0.8.0``` - * @see https://github.com/CosmWasm/cosmwasm-verify - */ - readonly builder?: string; -} -export interface CodeDetails extends CodeInfo { - /** Base64 encoded raw wasm data */ - readonly data: string; -} -export interface ContractInfo { - readonly address: string; - readonly code_id: number; - /** Bech32 account address */ - readonly creator: string; - /** Bech32-encoded admin address */ - readonly admin?: string; - readonly label: string; -} -export interface ContractDetails extends ContractInfo { - /** Argument passed on initialization of the contract */ - readonly init_msg: object; -} -export declare class RestClient extends BaseRestClient { - /** - * Creates a new client to interact with a Cosmos SDK light client daemon. - * This class tries to be a direct mapping onto the API. Some basic decoding and normalizatin is done - * but things like caching are done at a higher level. - * - * When building apps, you should not need to use this class directly. If you do, this indicates a missing feature - * in higher level components. Feel free to raise an issue in this case. - * - * @param apiUrl The URL of a Cosmos SDK light client daemon API (sometimes called REST server or REST API) - * @param broadcastMode Defines at which point of the transaction processing the postTx method (i.e. transaction broadcasting) returns - */ - constructor(apiUrl: string, broadcastMode?: BroadcastMode); - listCodeInfo(): Promise; - getCode(id: number): Promise; - listContractsByCodeId(id: number): Promise; - /** - * Returns null when contract was not found at this address. - */ - getContractInfo(address: string): Promise; - getAllContractState(address: string): Promise; - queryContractRaw(address: string, key: Uint8Array): Promise; - /** - * Makes a smart query on the contract and parses the reponse as JSON. - * Throws error if no such contract exists, the query format is invalid or the response is invalid. - */ - queryContractSmart(address: string, query: object): Promise; -} diff --git a/packages/sdk38/src/index.ts b/packages/sdk38/src/index.ts index d6053e6e..7c7d6b96 100644 --- a/packages/sdk38/src/index.ts +++ b/packages/sdk38/src/index.ts @@ -37,7 +37,6 @@ export { SupplyExtension, TxsResponse, } from "./lcdapi"; -export { RestClient } from "./restclient"; export { isMsgDelegate, isMsgSend, Msg, MsgDelegate, MsgSend } from "./msgs"; export { Pen, Secp256k1Pen, makeCosmoshubPath } from "./pen"; export { decodeBech32Pubkey, encodeBech32Pubkey, encodeSecp256k1Pubkey } from "./pubkey"; diff --git a/packages/sdk38/src/restclient.spec.ts b/packages/sdk38/src/restclient.spec.ts deleted file mode 100644 index 14d18f61..00000000 --- a/packages/sdk38/src/restclient.spec.ts +++ /dev/null @@ -1,885 +0,0 @@ -/* eslint-disable @typescript-eslint/camelcase */ -import { assert, sleep } from "@cosmjs/utils"; -import { ReadonlyDate } from "readonly-date"; - -import { rawSecp256k1PubkeyToAddress } from "./address"; -import { isPostTxFailure } from "./cosmosclient"; -import { makeSignBytes } from "./encoding"; -import { TxsResponse } from "./lcdapi"; -import { parseLogs } from "./logs"; -import { MsgSend } from "./msgs"; -import { makeCosmoshubPath, Secp256k1Pen } from "./pen"; -import { encodeBech32Pubkey } from "./pubkey"; -import { RestClient } from "./restclient"; -import { SigningCosmosClient } from "./signingcosmosclient"; -import cosmoshub from "./testdata/cosmoshub.json"; -import { - faucet, - makeRandomAddress, - makeSignedTx, - nonNegativeIntegerMatcher, - pendingWithoutWasmd, - semverMatcher, - tendermintAddressMatcher, - tendermintIdMatcher, - tendermintOptionalIdMatcher, - tendermintShortHashMatcher, - unused, - wasmd, - wasmdEnabled, -} from "./testutils.spec"; -import { StdFee } from "./types"; - -describe("RestClient", () => { - const defaultRecipientAddress = makeRandomAddress(); - - it("can be constructed", () => { - const client = new RestClient(wasmd.endpoint); - expect(client).toBeTruthy(); - }); - - // The /auth endpoints - - describe("authAccounts", () => { - it("works for unused account without pubkey", async () => { - pendingWithoutWasmd(); - const client = new RestClient(wasmd.endpoint); - const { height, result } = await client.authAccounts(unused.address); - expect(height).toMatch(nonNegativeIntegerMatcher); - expect(result).toEqual({ - type: "cosmos-sdk/Account", - value: { - address: unused.address, - public_key: "", // not known to the chain - coins: [ - { - amount: "1000000000", - denom: "ucosm", - }, - { - amount: "1000000000", - denom: "ustake", - }, - ], - account_number: unused.accountNumber, - sequence: 0, - }, - }); - }); - - // This fails in the first test run if you forget to run `./scripts/wasmd/init.sh` - it("has correct pubkey for faucet", async () => { - pendingWithoutWasmd(); - const client = new RestClient(wasmd.endpoint); - const { result } = await client.authAccounts(faucet.address); - expect(result.value).toEqual( - jasmine.objectContaining({ - public_key: encodeBech32Pubkey(faucet.pubkey, "cosmospub"), - }), - ); - }); - - // This property is used by CosmWasmClient.getAccount - it("returns empty address for non-existent account", async () => { - pendingWithoutWasmd(); - const client = new RestClient(wasmd.endpoint); - const nonExistentAccount = makeRandomAddress(); - const { result } = await client.authAccounts(nonExistentAccount); - expect(result).toEqual({ - type: "cosmos-sdk/Account", - value: jasmine.objectContaining({ address: "" }), - }); - }); - }); - - // The /blocks endpoints - - describe("blocksLatest", () => { - it("works", async () => { - pendingWithoutWasmd(); - const client = new RestClient(wasmd.endpoint); - const response = await client.blocksLatest(); - - // id - expect(response.block_id.hash).toMatch(tendermintIdMatcher); - - // header - expect(response.block.header.version).toEqual({ block: "10", app: "0" }); - expect(parseInt(response.block.header.height, 10)).toBeGreaterThanOrEqual(1); - expect(response.block.header.chain_id).toEqual(wasmd.chainId); - expect(new ReadonlyDate(response.block.header.time).getTime()).toBeLessThan(ReadonlyDate.now()); - expect(new ReadonlyDate(response.block.header.time).getTime()).toBeGreaterThanOrEqual( - ReadonlyDate.now() - 5_000, - ); - expect(response.block.header.last_commit_hash).toMatch(tendermintIdMatcher); - expect(response.block.header.last_block_id.hash).toMatch(tendermintIdMatcher); - expect(response.block.header.data_hash).toMatch(tendermintOptionalIdMatcher); - expect(response.block.header.validators_hash).toMatch(tendermintIdMatcher); - expect(response.block.header.next_validators_hash).toMatch(tendermintIdMatcher); - expect(response.block.header.consensus_hash).toMatch(tendermintIdMatcher); - expect(response.block.header.app_hash).toMatch(tendermintIdMatcher); - expect(response.block.header.last_results_hash).toMatch(tendermintOptionalIdMatcher); - expect(response.block.header.evidence_hash).toMatch(tendermintOptionalIdMatcher); - expect(response.block.header.proposer_address).toMatch(tendermintAddressMatcher); - - // data - expect(response.block.data.txs === null || Array.isArray(response.block.data.txs)).toEqual(true); - }); - }); - - describe("blocks", () => { - it("works for block by height", async () => { - pendingWithoutWasmd(); - const client = new RestClient(wasmd.endpoint); - const height = parseInt((await client.blocksLatest()).block.header.height, 10); - const response = await client.blocks(height - 1); - - // id - expect(response.block_id.hash).toMatch(tendermintIdMatcher); - - // header - expect(response.block.header.version).toEqual({ block: "10", app: "0" }); - expect(response.block.header.height).toEqual(`${height - 1}`); - expect(response.block.header.chain_id).toEqual(wasmd.chainId); - expect(new ReadonlyDate(response.block.header.time).getTime()).toBeLessThan(ReadonlyDate.now()); - expect(new ReadonlyDate(response.block.header.time).getTime()).toBeGreaterThanOrEqual( - ReadonlyDate.now() - 5_000, - ); - expect(response.block.header.last_commit_hash).toMatch(tendermintIdMatcher); - expect(response.block.header.last_block_id.hash).toMatch(tendermintIdMatcher); - expect(response.block.header.data_hash).toMatch(tendermintOptionalIdMatcher); - expect(response.block.header.validators_hash).toMatch(tendermintIdMatcher); - expect(response.block.header.next_validators_hash).toMatch(tendermintIdMatcher); - expect(response.block.header.consensus_hash).toMatch(tendermintIdMatcher); - expect(response.block.header.app_hash).toMatch(tendermintIdMatcher); - expect(response.block.header.last_results_hash).toMatch(tendermintOptionalIdMatcher); - expect(response.block.header.evidence_hash).toMatch(tendermintOptionalIdMatcher); - expect(response.block.header.proposer_address).toMatch(tendermintAddressMatcher); - - // data - expect(response.block.data.txs === null || Array.isArray(response.block.data.txs)).toEqual(true); - }); - }); - - // The /node_info endpoint - - describe("nodeInfo", () => { - it("works", async () => { - pendingWithoutWasmd(); - const client = new RestClient(wasmd.endpoint); - const { node_info, application_version } = await client.nodeInfo(); - - expect(node_info).toEqual({ - protocol_version: { p2p: "7", block: "10", app: "0" }, - id: jasmine.stringMatching(tendermintShortHashMatcher), - listen_addr: "tcp://0.0.0.0:26656", - network: wasmd.chainId, - version: jasmine.stringMatching(/^0\.33\.[0-9]+$/), - channels: "4020212223303800", - moniker: wasmd.chainId, - other: { tx_index: "on", rpc_address: "tcp://0.0.0.0:26657" }, - }); - expect(application_version).toEqual({ - name: "wasm", - server_name: "wasmd", - client_name: "wasmcli", - version: jasmine.stringMatching(semverMatcher), - commit: jasmine.stringMatching(tendermintShortHashMatcher), - build_tags: "netgo,ledger,muslc", - go: jasmine.stringMatching(/^go version go1\.[0-9]+\.[0-9]+ linux\/amd64$/), - }); - }); - }); - - // The /txs endpoints - - describe("txById", () => { - let successful: - | { - readonly sender: string; - readonly recipient: string; - readonly hash: string; - } - | undefined; - let unsuccessful: - | { - readonly sender: string; - readonly recipient: string; - readonly hash: string; - } - | undefined; - - beforeAll(async () => { - if (wasmdEnabled()) { - const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic); - const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, (signBytes) => - pen.sign(signBytes), - ); - - { - const recipient = makeRandomAddress(); - const transferAmount = { - denom: "ucosm", - amount: "1234567", - }; - const result = await client.sendTokens(recipient, [transferAmount]); - successful = { - sender: faucet.address, - recipient: recipient, - hash: result.transactionHash, - }; - } - - { - const memo = "Sending more than I can afford"; - const recipient = makeRandomAddress(); - const transferAmount = [ - { - denom: "ucosm", - amount: "123456700000000", - }, - ]; - const sendMsg: MsgSend = { - type: "cosmos-sdk/MsgSend", - value: { - // eslint-disable-next-line @typescript-eslint/camelcase - from_address: faucet.address, - // eslint-disable-next-line @typescript-eslint/camelcase - to_address: recipient, - amount: transferAmount, - }, - }; - const fee = { - amount: [ - { - denom: "ucosm", - amount: "2000", - }, - ], - gas: "80000", // 80k - }; - const { accountNumber, sequence } = await client.getNonce(); - const chainId = await client.getChainId(); - const signBytes = makeSignBytes([sendMsg], fee, chainId, memo, accountNumber, sequence); - const signature = await pen.sign(signBytes); - const signedTx = { - msg: [sendMsg], - fee: fee, - memo: memo, - signatures: [signature], - }; - const transactionId = await client.getIdentifier({ type: "cosmos-sdk/StdTx", value: signedTx }); - const result = await client.postTx(signedTx); - assert(isPostTxFailure(result)); - unsuccessful = { - sender: faucet.address, - recipient: recipient, - hash: transactionId, - }; - } - - await sleep(75); // wait until transactions are indexed - } - }); - - it("works for successful transaction", async () => { - pendingWithoutWasmd(); - assert(successful); - const client = new RestClient(wasmd.endpoint); - const result = await client.txById(successful.hash); - expect(result.height).toBeGreaterThanOrEqual(1); - expect(result.txhash).toEqual(successful.hash); - expect(result.codespace).toBeUndefined(); - expect(result.code).toBeUndefined(); - const logs = parseLogs(result.logs); - expect(logs).toEqual([ - { - msg_index: 0, - log: "", - events: [ - { - type: "message", - attributes: [ - { key: "action", value: "send" }, - { key: "sender", value: successful.sender }, - { key: "module", value: "bank" }, - ], - }, - { - type: "transfer", - attributes: [ - { key: "recipient", value: successful.recipient }, - { key: "sender", value: successful.sender }, - { key: "amount", value: "1234567ucosm" }, - ], - }, - ], - }, - ]); - }); - - it("works for unsuccessful transaction", async () => { - pendingWithoutWasmd(); - assert(unsuccessful); - const client = new RestClient(wasmd.endpoint); - const result = await client.txById(unsuccessful.hash); - expect(result.height).toBeGreaterThanOrEqual(1); - expect(result.txhash).toEqual(unsuccessful.hash); - expect(result.codespace).toEqual("sdk"); - expect(result.code).toEqual(5); - expect(result.logs).toBeUndefined(); - expect(result.raw_log).toContain("insufficient funds"); - }); - }); - - describe("txsQuery", () => { - let posted: - | { - readonly sender: string; - readonly recipient: string; - readonly hash: string; - readonly height: number; - readonly tx: TxsResponse; - } - | undefined; - - beforeAll(async () => { - if (wasmdEnabled()) { - const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic); - const client = new SigningCosmosClient(wasmd.endpoint, faucet.address, (signBytes) => - pen.sign(signBytes), - ); - - const recipient = makeRandomAddress(); - const transferAmount = [ - { - denom: "ucosm", - amount: "1234567", - }, - ]; - const result = await client.sendTokens(recipient, transferAmount); - - await sleep(75); // wait until tx is indexed - const txDetails = await new RestClient(wasmd.endpoint).txById(result.transactionHash); - posted = { - sender: faucet.address, - recipient: recipient, - hash: result.transactionHash, - height: Number.parseInt(txDetails.height, 10), - tx: txDetails, - }; - } - }); - - it("can query transactions by height", async () => { - pendingWithoutWasmd(); - assert(posted); - const client = new RestClient(wasmd.endpoint); - const result = await client.txsQuery(`tx.height=${posted.height}&limit=26`); - expect(result).toEqual({ - count: jasmine.stringMatching(/^(1|2|3|4|5)$/), // 1-5 transactions as string - limit: "26", - page_number: "1", - page_total: "1", - total_count: jasmine.stringMatching(/^(1|2|3|4|5)$/), // 1-5 transactions as string - txs: jasmine.arrayContaining([posted.tx]), - }); - }); - - it("can query transactions by ID", async () => { - pendingWithoutWasmd(); - assert(posted); - const client = new RestClient(wasmd.endpoint); - const result = await client.txsQuery(`tx.hash=${posted.hash}&limit=26`); - expect(result).toEqual({ - count: "1", - limit: "26", - page_number: "1", - page_total: "1", - total_count: "1", - txs: [posted.tx], - }); - }); - - it("can query transactions by sender", async () => { - pendingWithoutWasmd(); - assert(posted); - const client = new RestClient(wasmd.endpoint); - const result = await client.txsQuery(`message.sender=${posted.sender}&limit=200`); - expect(parseInt(result.count, 10)).toBeGreaterThanOrEqual(1); - expect(parseInt(result.limit, 10)).toEqual(200); - expect(parseInt(result.page_number, 10)).toEqual(1); - expect(parseInt(result.page_total, 10)).toEqual(1); - expect(parseInt(result.total_count, 10)).toBeGreaterThanOrEqual(1); - expect(result.txs.length).toBeGreaterThanOrEqual(1); - expect(result.txs[result.txs.length - 1]).toEqual(posted.tx); - }); - - it("can query transactions by recipient", async () => { - pendingWithoutWasmd(); - assert(posted); - const client = new RestClient(wasmd.endpoint); - const result = await client.txsQuery(`transfer.recipient=${posted.recipient}&limit=200`); - expect(parseInt(result.count, 10)).toEqual(1); - expect(parseInt(result.limit, 10)).toEqual(200); - expect(parseInt(result.page_number, 10)).toEqual(1); - expect(parseInt(result.page_total, 10)).toEqual(1); - expect(parseInt(result.total_count, 10)).toEqual(1); - expect(result.txs.length).toBeGreaterThanOrEqual(1); - expect(result.txs[result.txs.length - 1]).toEqual(posted.tx); - }); - - it("can filter by tx.hash and tx.minheight", async () => { - pending("This combination is broken 🤷‍♂️. Handle client-side at higher level."); - pendingWithoutWasmd(); - assert(posted); - const client = new RestClient(wasmd.endpoint); - const hashQuery = `tx.hash=${posted.hash}`; - - { - const { count } = await client.txsQuery(`${hashQuery}&tx.minheight=0`); - expect(count).toEqual("1"); - } - - { - const { count } = await client.txsQuery(`${hashQuery}&tx.minheight=${posted.height - 1}`); - expect(count).toEqual("1"); - } - - { - const { count } = await client.txsQuery(`${hashQuery}&tx.minheight=${posted.height}`); - expect(count).toEqual("1"); - } - - { - const { count } = await client.txsQuery(`${hashQuery}&tx.minheight=${posted.height + 1}`); - expect(count).toEqual("0"); - } - }); - - it("can filter by recipient and tx.minheight", async () => { - pendingWithoutWasmd(); - assert(posted); - const client = new RestClient(wasmd.endpoint); - const recipientQuery = `transfer.recipient=${posted.recipient}`; - - { - const { count } = await client.txsQuery(`${recipientQuery}&tx.minheight=0`); - expect(count).toEqual("1"); - } - - { - const { count } = await client.txsQuery(`${recipientQuery}&tx.minheight=${posted.height - 1}`); - expect(count).toEqual("1"); - } - - { - const { count } = await client.txsQuery(`${recipientQuery}&tx.minheight=${posted.height}`); - expect(count).toEqual("1"); - } - - { - const { count } = await client.txsQuery(`${recipientQuery}&tx.minheight=${posted.height + 1}`); - expect(count).toEqual("0"); - } - }); - - it("can filter by recipient and tx.maxheight", async () => { - pendingWithoutWasmd(); - assert(posted); - const client = new RestClient(wasmd.endpoint); - const recipientQuery = `transfer.recipient=${posted.recipient}`; - - { - const { count } = await client.txsQuery(`${recipientQuery}&tx.maxheight=9999999999999`); - expect(count).toEqual("1"); - } - - { - const { count } = await client.txsQuery(`${recipientQuery}&tx.maxheight=${posted.height + 1}`); - expect(count).toEqual("1"); - } - - { - const { count } = await client.txsQuery(`${recipientQuery}&tx.maxheight=${posted.height}`); - expect(count).toEqual("1"); - } - - { - const { count } = await client.txsQuery(`${recipientQuery}&tx.maxheight=${posted.height - 1}`); - expect(count).toEqual("0"); - } - }); - }); - - describe("encodeTx", () => { - it("works for cosmoshub example", async () => { - pendingWithoutWasmd(); - const client = new RestClient(wasmd.endpoint); - const response = await client.encodeTx(cosmoshub.tx); - expect(response).toEqual( - jasmine.objectContaining({ - tx: cosmoshub.tx_data, - }), - ); - }); - }); - - describe("postTx", () => { - it("can send tokens", async () => { - pendingWithoutWasmd(); - const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic); - - const memo = "My first contract on chain"; - const theMsg: MsgSend = { - type: "cosmos-sdk/MsgSend", - value: { - from_address: faucet.address, - to_address: defaultRecipientAddress, - amount: [ - { - denom: "ucosm", - amount: "1234567", - }, - ], - }, - }; - - const fee: StdFee = { - amount: [ - { - amount: "5000", - denom: "ucosm", - }, - ], - gas: "890000", - }; - - const client = new RestClient(wasmd.endpoint); - const { account_number, sequence } = (await client.authAccounts(faucet.address)).result.value; - - const signBytes = makeSignBytes([theMsg], fee, wasmd.chainId, memo, account_number, sequence); - const signature = await pen.sign(signBytes); - const signedTx = makeSignedTx(theMsg, fee, memo, signature); - const result = await client.postTx(signedTx); - expect(result.code).toBeUndefined(); - expect(result).toEqual({ - height: jasmine.stringMatching(nonNegativeIntegerMatcher), - txhash: jasmine.stringMatching(tendermintIdMatcher), - // code is not set - raw_log: jasmine.stringMatching(/^\[.+\]$/i), - logs: jasmine.any(Array), - gas_wanted: jasmine.stringMatching(nonNegativeIntegerMatcher), - gas_used: jasmine.stringMatching(nonNegativeIntegerMatcher), - }); - }); - - it("can't send transaction with additional signatures", async () => { - pendingWithoutWasmd(); - const account1 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0)); - const account2 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1)); - const account3 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(2)); - const address1 = rawSecp256k1PubkeyToAddress(account1.pubkey, "cosmos"); - const address2 = rawSecp256k1PubkeyToAddress(account2.pubkey, "cosmos"); - const address3 = rawSecp256k1PubkeyToAddress(account3.pubkey, "cosmos"); - - const memo = "My first contract on chain"; - const theMsg: MsgSend = { - type: "cosmos-sdk/MsgSend", - value: { - from_address: address1, - to_address: defaultRecipientAddress, - amount: [ - { - denom: "ucosm", - amount: "1234567", - }, - ], - }, - }; - - const fee: StdFee = { - amount: [ - { - amount: "5000", - denom: "ucosm", - }, - ], - gas: "890000", - }; - - const client = new RestClient(wasmd.endpoint); - const { account_number: an1, sequence: sequence1 } = (await client.authAccounts(address1)).result.value; - const { account_number: an2, sequence: sequence2 } = (await client.authAccounts(address2)).result.value; - const { account_number: an3, sequence: sequence3 } = (await client.authAccounts(address3)).result.value; - - const signBytes1 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an1, sequence1); - const signBytes2 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an2, sequence2); - const signBytes3 = makeSignBytes([theMsg], fee, wasmd.chainId, memo, an3, sequence3); - const signature1 = await account1.sign(signBytes1); - const signature2 = await account2.sign(signBytes2); - const signature3 = await account3.sign(signBytes3); - const signedTx = { - msg: [theMsg], - fee: fee, - memo: memo, - signatures: [signature1, signature2, signature3], - }; - const postResult = await client.postTx(signedTx); - expect(postResult.code).toEqual(4); - expect(postResult.raw_log).toContain("wrong number of signers"); - }); - - it("can send multiple messages with one signature", async () => { - pendingWithoutWasmd(); - const account1 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0)); - const address1 = rawSecp256k1PubkeyToAddress(account1.pubkey, "cosmos"); - - const memo = "My first contract on chain"; - const msg1: MsgSend = { - type: "cosmos-sdk/MsgSend", - value: { - from_address: address1, - to_address: defaultRecipientAddress, - amount: [ - { - denom: "ucosm", - amount: "1234567", - }, - ], - }, - }; - const msg2: MsgSend = { - type: "cosmos-sdk/MsgSend", - value: { - from_address: address1, - to_address: defaultRecipientAddress, - amount: [ - { - denom: "ucosm", - amount: "7654321", - }, - ], - }, - }; - - const fee: StdFee = { - amount: [ - { - amount: "5000", - denom: "ucosm", - }, - ], - gas: "890000", - }; - - const client = new RestClient(wasmd.endpoint); - const { account_number, sequence } = (await client.authAccounts(address1)).result.value; - - const signBytes = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, account_number, sequence); - const signature1 = await account1.sign(signBytes); - const signedTx = { - msg: [msg1, msg2], - fee: fee, - memo: memo, - signatures: [signature1], - }; - const postResult = await client.postTx(signedTx); - expect(postResult.code).toBeUndefined(); - }); - - it("can send multiple messages with multiple signatures", async () => { - pendingWithoutWasmd(); - const account1 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0)); - const account2 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1)); - const address1 = rawSecp256k1PubkeyToAddress(account1.pubkey, "cosmos"); - const address2 = rawSecp256k1PubkeyToAddress(account2.pubkey, "cosmos"); - - const memo = "My first contract on chain"; - const msg1: MsgSend = { - type: "cosmos-sdk/MsgSend", - value: { - from_address: address1, - to_address: defaultRecipientAddress, - amount: [ - { - denom: "ucosm", - amount: "1234567", - }, - ], - }, - }; - const msg2: MsgSend = { - type: "cosmos-sdk/MsgSend", - value: { - from_address: address2, - to_address: defaultRecipientAddress, - amount: [ - { - denom: "ucosm", - amount: "7654321", - }, - ], - }, - }; - - const fee: StdFee = { - amount: [ - { - amount: "5000", - denom: "ucosm", - }, - ], - gas: "890000", - }; - - const client = new RestClient(wasmd.endpoint); - const { account_number: an1, sequence: sequence1 } = (await client.authAccounts(address1)).result.value; - const { account_number: an2, sequence: sequence2 } = (await client.authAccounts(address2)).result.value; - - const signBytes1 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1); - const signBytes2 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2); - const signature1 = await account1.sign(signBytes1); - const signature2 = await account2.sign(signBytes2); - const signedTx = { - msg: [msg2, msg1], - fee: fee, - memo: memo, - signatures: [signature2, signature1], - }; - const postResult = await client.postTx(signedTx); - expect(postResult.code).toBeUndefined(); - - await sleep(500); - const searched = await client.txsQuery(`tx.hash=${postResult.txhash}`); - expect(searched.txs.length).toEqual(1); - expect(searched.txs[0].tx.value.signatures).toEqual([signature2, signature1]); - }); - - it("can't send transaction with wrong signature order (1)", async () => { - pendingWithoutWasmd(); - const account1 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0)); - const account2 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1)); - const address1 = rawSecp256k1PubkeyToAddress(account1.pubkey, "cosmos"); - const address2 = rawSecp256k1PubkeyToAddress(account2.pubkey, "cosmos"); - - const memo = "My first contract on chain"; - const msg1: MsgSend = { - type: "cosmos-sdk/MsgSend", - value: { - from_address: address1, - to_address: defaultRecipientAddress, - amount: [ - { - denom: "ucosm", - amount: "1234567", - }, - ], - }, - }; - const msg2: MsgSend = { - type: "cosmos-sdk/MsgSend", - value: { - from_address: address2, - to_address: defaultRecipientAddress, - amount: [ - { - denom: "ucosm", - amount: "7654321", - }, - ], - }, - }; - - const fee: StdFee = { - amount: [ - { - amount: "5000", - denom: "ucosm", - }, - ], - gas: "890000", - }; - - const client = new RestClient(wasmd.endpoint); - const { account_number: an1, sequence: sequence1 } = (await client.authAccounts(address1)).result.value; - const { account_number: an2, sequence: sequence2 } = (await client.authAccounts(address2)).result.value; - - const signBytes1 = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, an1, sequence1); - const signBytes2 = makeSignBytes([msg1, msg2], fee, wasmd.chainId, memo, an2, sequence2); - const signature1 = await account1.sign(signBytes1); - const signature2 = await account2.sign(signBytes2); - const signedTx = { - msg: [msg1, msg2], - fee: fee, - memo: memo, - signatures: [signature2, signature1], - }; - const postResult = await client.postTx(signedTx); - expect(postResult.code).toEqual(8); - }); - - it("can't send transaction with wrong signature order (2)", async () => { - pendingWithoutWasmd(); - const account1 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(0)); - const account2 = await Secp256k1Pen.fromMnemonic(faucet.mnemonic, makeCosmoshubPath(1)); - const address1 = rawSecp256k1PubkeyToAddress(account1.pubkey, "cosmos"); - const address2 = rawSecp256k1PubkeyToAddress(account2.pubkey, "cosmos"); - - const memo = "My first contract on chain"; - const msg1: MsgSend = { - type: "cosmos-sdk/MsgSend", - value: { - from_address: address1, - to_address: defaultRecipientAddress, - amount: [ - { - denom: "ucosm", - amount: "1234567", - }, - ], - }, - }; - const msg2: MsgSend = { - type: "cosmos-sdk/MsgSend", - value: { - from_address: address2, - to_address: defaultRecipientAddress, - amount: [ - { - denom: "ucosm", - amount: "7654321", - }, - ], - }, - }; - - const fee: StdFee = { - amount: [ - { - amount: "5000", - denom: "ucosm", - }, - ], - gas: "890000", - }; - - const client = new RestClient(wasmd.endpoint); - const { account_number: an1, sequence: sequence1 } = (await client.authAccounts(address1)).result.value; - const { account_number: an2, sequence: sequence2 } = (await client.authAccounts(address2)).result.value; - - const signBytes1 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an1, sequence1); - const signBytes2 = makeSignBytes([msg2, msg1], fee, wasmd.chainId, memo, an2, sequence2); - const signature1 = await account1.sign(signBytes1); - const signature2 = await account2.sign(signBytes2); - const signedTx = { - msg: [msg2, msg1], - fee: fee, - memo: memo, - signatures: [signature1, signature2], - }; - const postResult = await client.postTx(signedTx); - expect(postResult.code).toEqual(8); - }); - }); -}); diff --git a/packages/sdk38/src/restclient.ts b/packages/sdk38/src/restclient.ts deleted file mode 100644 index 81d1d85f..00000000 --- a/packages/sdk38/src/restclient.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { isNonNullObject } from "@cosmjs/utils"; -import axios, { AxiosError, AxiosInstance } from "axios"; - -import { - AuthAccountsResponse, - BlockResponse, - BroadcastMode, - EncodeTxResponse, - NodeInfoResponse, - PostTxsResponse, - SearchTxsResponse, - TxsResponse, -} from "./lcdapi"; -import { CosmosSdkTx, StdTx } from "./types"; - -// We want to get message data from 500 errors -// https://stackoverflow.com/questions/56577124/how-to-handle-500-error-message-with-axios -// this should be chained to catch one error and throw a more informative one -function parseAxiosError(err: AxiosError): never { - // use the error message sent from server, not default 500 msg - if (err.response?.data) { - let errorText: string; - const data = err.response.data; - // expect { error: string }, but otherwise dump - if (data.error && typeof data.error === "string") { - errorText = data.error; - } else if (typeof data === "string") { - errorText = data; - } else { - errorText = JSON.stringify(data); - } - throw new Error(`${errorText} (HTTP ${err.response.status})`); - } else { - throw err; - } -} - -/** - * @deprecated use LcdClient. - */ -export class RestClient { - private readonly client: AxiosInstance; - private readonly broadcastMode: BroadcastMode; - - /** - * Creates a new client to interact with a Cosmos SDK light client daemon. - * This class tries to be a direct mapping onto the API. Some basic decoding and normalizatin is done - * but things like caching are done at a higher level. - * - * When building apps, you should not need to use this class directly. If you do, this indicates a missing feature - * in higher level components. Feel free to raise an issue in this case. - * - * @param apiUrl The URL of a Cosmos SDK light client daemon API (sometimes called REST server or REST API) - * @param broadcastMode Defines at which point of the transaction processing the postTx method (i.e. transaction broadcasting) returns - */ - public constructor(apiUrl: string, broadcastMode = BroadcastMode.Block) { - const headers = { - post: { "Content-Type": "application/json" }, - }; - this.client = axios.create({ - baseURL: apiUrl, - headers: headers, - }); - this.broadcastMode = broadcastMode; - } - - public async get(path: string): Promise { - const { data } = await this.client.get(path).catch(parseAxiosError); - if (data === null) { - throw new Error("Received null response from server"); - } - return data; - } - - public async post(path: string, params: any): Promise { - if (!isNonNullObject(params)) throw new Error("Got unexpected type of params. Expected object."); - const { data } = await this.client.post(path, params).catch(parseAxiosError); - if (data === null) { - throw new Error("Received null response from server"); - } - return data; - } - - // The /auth endpoints - - public async authAccounts(address: string): Promise { - const path = `/auth/accounts/${address}`; - const responseData = await this.get(path); - if (responseData.result.type !== "cosmos-sdk/Account") { - throw new Error("Unexpected response data format"); - } - return responseData as AuthAccountsResponse; - } - - // The /blocks endpoints - - public async blocksLatest(): Promise { - const responseData = await this.get("/blocks/latest"); - if (!responseData.block) { - throw new Error("Unexpected response data format"); - } - return responseData as BlockResponse; - } - - public async blocks(height: number): Promise { - const responseData = await this.get(`/blocks/${height}`); - if (!responseData.block) { - throw new Error("Unexpected response data format"); - } - return responseData as BlockResponse; - } - - // The /node_info endpoint - - public async nodeInfo(): Promise { - const responseData = await this.get("/node_info"); - if (!responseData.node_info) { - throw new Error("Unexpected response data format"); - } - return responseData as NodeInfoResponse; - } - - // The /txs endpoints - - public async txById(id: string): Promise { - const responseData = await this.get(`/txs/${id}`); - if (!responseData.tx) { - throw new Error("Unexpected response data format"); - } - return responseData as TxsResponse; - } - - public async txsQuery(query: string): Promise { - const responseData = await this.get(`/txs?${query}`); - if (!responseData.txs) { - throw new Error("Unexpected response data format"); - } - return responseData as SearchTxsResponse; - } - - /** returns the amino-encoding of the transaction performed by the server */ - public async encodeTx(tx: CosmosSdkTx): Promise { - const responseData = await this.post("/txs/encode", tx); - if (!responseData.tx) { - throw new Error("Unexpected response data format"); - } - return responseData as EncodeTxResponse; - } - - /** - * Broadcasts a signed transaction to into the transaction pool. - * Depending on the RestClient's broadcast mode, this might or might - * wait for checkTx or deliverTx to be executed before returning. - * - * @param tx a signed transaction as StdTx (i.e. not wrapped in type/value container) - */ - public async postTx(tx: StdTx): Promise { - const params = { - tx: tx, - mode: this.broadcastMode, - }; - const responseData = await this.post("/txs", params); - if (!responseData.txhash) { - throw new Error("Unexpected response data format"); - } - return responseData as PostTxsResponse; - } -} diff --git a/packages/sdk38/types/index.d.ts b/packages/sdk38/types/index.d.ts index 38123a21..ea017984 100644 --- a/packages/sdk38/types/index.d.ts +++ b/packages/sdk38/types/index.d.ts @@ -35,7 +35,6 @@ export { SupplyExtension, TxsResponse, } from "./lcdapi"; -export { RestClient } from "./restclient"; export { isMsgDelegate, isMsgSend, Msg, MsgDelegate, MsgSend } from "./msgs"; export { Pen, Secp256k1Pen, makeCosmoshubPath } from "./pen"; export { decodeBech32Pubkey, encodeBech32Pubkey, encodeSecp256k1Pubkey } from "./pubkey"; diff --git a/packages/sdk38/types/restclient.d.ts b/packages/sdk38/types/restclient.d.ts deleted file mode 100644 index 641b04c7..00000000 --- a/packages/sdk38/types/restclient.d.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { - AuthAccountsResponse, - BlockResponse, - BroadcastMode, - EncodeTxResponse, - NodeInfoResponse, - PostTxsResponse, - SearchTxsResponse, - TxsResponse, -} from "./lcdapi"; -import { CosmosSdkTx, StdTx } from "./types"; -/** - * @deprecated use LcdClient. - */ -export declare class RestClient { - private readonly client; - private readonly broadcastMode; - /** - * Creates a new client to interact with a Cosmos SDK light client daemon. - * This class tries to be a direct mapping onto the API. Some basic decoding and normalizatin is done - * but things like caching are done at a higher level. - * - * When building apps, you should not need to use this class directly. If you do, this indicates a missing feature - * in higher level components. Feel free to raise an issue in this case. - * - * @param apiUrl The URL of a Cosmos SDK light client daemon API (sometimes called REST server or REST API) - * @param broadcastMode Defines at which point of the transaction processing the postTx method (i.e. transaction broadcasting) returns - */ - constructor(apiUrl: string, broadcastMode?: BroadcastMode); - get(path: string): Promise; - post(path: string, params: any): Promise; - authAccounts(address: string): Promise; - blocksLatest(): Promise; - blocks(height: number): Promise; - nodeInfo(): Promise; - txById(id: string): Promise; - txsQuery(query: string): Promise; - /** returns the amino-encoding of the transaction performed by the server */ - encodeTx(tx: CosmosSdkTx): Promise; - /** - * Broadcasts a signed transaction to into the transaction pool. - * Depending on the RestClient's broadcast mode, this might or might - * wait for checkTx or deliverTx to be executed before returning. - * - * @param tx a signed transaction as StdTx (i.e. not wrapped in type/value container) - */ - postTx(tx: StdTx): Promise; -} From 7ebb9cbca71a24c60d0376af7a84ed43e6962a80 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 16:51:24 +0200 Subject: [PATCH 33/37] Remove spec types in sub-folders --- packages/cosmwasm/package.json | 2 +- packages/cosmwasm/types/lcdapi/wasm.spec.d.ts | 1 - packages/sdk38/package.json | 2 +- packages/sdk38/types/lcdapi/auth.spec.d.ts | 1 - packages/sdk38/types/lcdapi/lcdclient.spec.d.ts | 8 -------- packages/sdk38/types/lcdapi/supply.spec.d.ts | 1 - 6 files changed, 2 insertions(+), 13 deletions(-) delete mode 100644 packages/cosmwasm/types/lcdapi/wasm.spec.d.ts delete mode 100644 packages/sdk38/types/lcdapi/auth.spec.d.ts delete mode 100644 packages/sdk38/types/lcdapi/lcdclient.spec.d.ts delete mode 100644 packages/sdk38/types/lcdapi/supply.spec.d.ts diff --git a/packages/cosmwasm/package.json b/packages/cosmwasm/package.json index dac7b17b..e5b2ab65 100644 --- a/packages/cosmwasm/package.json +++ b/packages/cosmwasm/package.json @@ -26,7 +26,7 @@ "format-text": "prettier --write --prose-wrap always --print-width 80 \"./*.md\"", "lint": "eslint --max-warnings 0 \"**/*.{js,ts}\"", "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", + "move-types": "shx rm -rf ./types/* && shx mv build/types/* ./types && rm -rf ./types/testdata && shx rm -f ./types/*.spec.d.ts && shx rm ./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", diff --git a/packages/cosmwasm/types/lcdapi/wasm.spec.d.ts b/packages/cosmwasm/types/lcdapi/wasm.spec.d.ts deleted file mode 100644 index cb0ff5c3..00000000 --- a/packages/cosmwasm/types/lcdapi/wasm.spec.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/packages/sdk38/package.json b/packages/sdk38/package.json index 5d2b6087..bd5a1110 100644 --- a/packages/sdk38/package.json +++ b/packages/sdk38/package.json @@ -26,7 +26,7 @@ "format-text": "prettier --write --prose-wrap always --print-width 80 \"./*.md\"", "lint": "eslint --max-warnings 0 \"**/*.{js,ts}\"", "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", + "move-types": "shx rm -rf ./types/* && shx mv build/types/* ./types && rm -rf ./types/testdata && shx rm -f ./types/*.spec.d.ts && shx rm ./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", diff --git a/packages/sdk38/types/lcdapi/auth.spec.d.ts b/packages/sdk38/types/lcdapi/auth.spec.d.ts deleted file mode 100644 index cb0ff5c3..00000000 --- a/packages/sdk38/types/lcdapi/auth.spec.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/packages/sdk38/types/lcdapi/lcdclient.spec.d.ts b/packages/sdk38/types/lcdapi/lcdclient.spec.d.ts deleted file mode 100644 index 42096dd0..00000000 --- a/packages/sdk38/types/lcdapi/lcdclient.spec.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** Deployed as part of scripts/wasmd/init.sh */ -export declare const deployedErc20: { - codeId: number; - source: string; - builder: string; - checksum: string; - instances: string[]; -}; diff --git a/packages/sdk38/types/lcdapi/supply.spec.d.ts b/packages/sdk38/types/lcdapi/supply.spec.d.ts deleted file mode 100644 index cb0ff5c3..00000000 --- a/packages/sdk38/types/lcdapi/supply.spec.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; From 2cadeec789026e64e163dcd122694dd8d73c2fa1 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 17:19:47 +0200 Subject: [PATCH 34/37] Implement withExtensions with rest operator --- packages/sdk38/src/lcdapi/lcdclient.ts | 32 +++----------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/packages/sdk38/src/lcdapi/lcdclient.ts b/packages/sdk38/src/lcdapi/lcdclient.ts index 8be43afe..ded067f4 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.ts @@ -171,37 +171,12 @@ export class LcdClient { setupExtensionH: LcdExtensionSetup, ): LcdClient & A & B & C & D & E & F & G & H; - public static withExtensions< - A extends object, - B extends object, - C extends object, - D extends object, - E extends object, - F extends object, - G extends object, - H extends object - >( + public static withExtensions( options: LcdClientBaseOptions, - setupExtensionA?: LcdExtensionSetup, - setupExtensionB?: LcdExtensionSetup, - setupExtensionC?: LcdExtensionSetup, - setupExtensionD?: LcdExtensionSetup, - setupExtensionE?: LcdExtensionSetup, - setupExtensionF?: LcdExtensionSetup, - setupExtensionG?: LcdExtensionSetup, - setupExtensionH?: LcdExtensionSetup, + ...extensionSetups: Array> ): any { const client = new LcdClient(options.apiUrl, options.broadcastMode); - - const extensions = new Array(); - if (setupExtensionA) extensions.push(setupExtensionA(client)); - if (setupExtensionB) extensions.push(setupExtensionB(client)); - if (setupExtensionC) extensions.push(setupExtensionC(client)); - if (setupExtensionD) extensions.push(setupExtensionD(client)); - if (setupExtensionE) extensions.push(setupExtensionE(client)); - if (setupExtensionF) extensions.push(setupExtensionF(client)); - if (setupExtensionG) extensions.push(setupExtensionG(client)); - if (setupExtensionH) extensions.push(setupExtensionH(client)); + const extensions = extensionSetups.map((setupExtension) => setupExtension(client)); for (const extension of extensions) { assert(isNonNullObject(extension), `Extension must be a non-null object`); for (const [moduleKey, moduleValue] of Object.entries(extension)) { @@ -209,7 +184,6 @@ export class LcdClient { (client as any)[moduleKey] = Object.assign(current, moduleValue); } } - return client; } From 66df36c83bff9f83b26366d2f380ac11f8806153 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 17:22:13 +0200 Subject: [PATCH 35/37] Add assertion to moduleValue --- packages/sdk38/src/lcdapi/lcdclient.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/sdk38/src/lcdapi/lcdclient.ts b/packages/sdk38/src/lcdapi/lcdclient.ts index ded067f4..f9c5f21a 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.ts @@ -180,6 +180,10 @@ export class LcdClient { for (const extension of extensions) { assert(isNonNullObject(extension), `Extension must be a non-null object`); for (const [moduleKey, moduleValue] of Object.entries(extension)) { + assert( + isNonNullObject(moduleValue), + `Module must be a non-null object. Found type ${typeof moduleValue} for module "${moduleKey}".`, + ); const current = (client as any)[moduleKey] || {}; (client as any)[moduleKey] = Object.assign(current, moduleValue); } From 9d98b50afb2883ef697fb04540215ed76e0372b7 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 17:40:35 +0200 Subject: [PATCH 36/37] Fix module assignment --- packages/sdk38/src/lcdapi/lcdclient.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/sdk38/src/lcdapi/lcdclient.ts b/packages/sdk38/src/lcdapi/lcdclient.ts index f9c5f21a..f4e2f644 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.ts @@ -185,7 +185,10 @@ export class LcdClient { `Module must be a non-null object. Found type ${typeof moduleValue} for module "${moduleKey}".`, ); const current = (client as any)[moduleKey] || {}; - (client as any)[moduleKey] = Object.assign(current, moduleValue); + (client as any)[moduleKey] = { + ...current, + ...moduleValue, + }; } } return client; From 0c0f015a32d76cfb56b41e4ff00c84cec8191c9e Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Tue, 7 Jul 2020 17:46:50 +0200 Subject: [PATCH 37/37] Fix typos --- packages/cosmwasm/src/lcdapi/wasm.ts | 2 +- packages/cosmwasm/types/lcdapi/wasm.d.ts | 2 +- packages/sdk38/src/lcdapi/base.ts | 4 ++-- packages/sdk38/src/lcdapi/index.ts | 2 +- packages/sdk38/src/lcdapi/lcdclient.spec.ts | 8 ++++---- packages/sdk38/src/lcdapi/lcdclient.ts | 2 +- packages/sdk38/src/lcdapi/supply.ts | 8 ++++---- packages/sdk38/types/lcdapi/base.d.ts | 4 ++-- packages/sdk38/types/lcdapi/index.d.ts | 2 +- packages/sdk38/types/lcdapi/lcdclient.d.ts | 2 +- packages/sdk38/types/lcdapi/supply.d.ts | 8 ++++---- 11 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/cosmwasm/src/lcdapi/wasm.ts b/packages/cosmwasm/src/lcdapi/wasm.ts index b948184b..f0b70f65 100644 --- a/packages/cosmwasm/src/lcdapi/wasm.ts +++ b/packages/cosmwasm/src/lcdapi/wasm.ts @@ -106,7 +106,7 @@ export interface WasmExtension { readonly queryContractRaw: (address: string, key: Uint8Array) => Promise; /** - * Makes a smart query on the contract and parses the reponse as JSON. + * Makes a smart query on the contract and parses the response as JSON. * Throws error if no such contract exists, the query format is invalid or the response is invalid. */ readonly queryContractSmart: (address: string, query: object) => Promise; diff --git a/packages/cosmwasm/types/lcdapi/wasm.d.ts b/packages/cosmwasm/types/lcdapi/wasm.d.ts index bf01a01e..4f3b06a1 100644 --- a/packages/cosmwasm/types/lcdapi/wasm.d.ts +++ b/packages/cosmwasm/types/lcdapi/wasm.d.ts @@ -65,7 +65,7 @@ export interface WasmExtension { */ readonly queryContractRaw: (address: string, key: Uint8Array) => Promise; /** - * Makes a smart query on the contract and parses the reponse as JSON. + * Makes a smart query on the contract and parses the response as JSON. * Throws error if no such contract exists, the query format is invalid or the response is invalid. */ readonly queryContractSmart: (address: string, query: object) => Promise; diff --git a/packages/sdk38/src/lcdapi/base.ts b/packages/sdk38/src/lcdapi/base.ts index 45f46e0f..9b13b20d 100644 --- a/packages/sdk38/src/lcdapi/base.ts +++ b/packages/sdk38/src/lcdapi/base.ts @@ -8,13 +8,13 @@ import { CosmosSdkTx } from "../types"; export enum BroadcastMode { /** Return after tx commit */ Block = "block", - /** Return afer CheckTx */ + /** Return after CheckTx */ Sync = "sync", /** Return right away */ Async = "async", } -/** A reponse from the /txs/encode endpoint */ +/** A response from the /txs/encode endpoint */ export interface EncodeTxResponse { /** base64-encoded amino-binary encoded representation */ readonly tx: string; diff --git a/packages/sdk38/src/lcdapi/index.ts b/packages/sdk38/src/lcdapi/index.ts index bc39d863..b2a25ab0 100644 --- a/packages/sdk38/src/lcdapi/index.ts +++ b/packages/sdk38/src/lcdapi/index.ts @@ -3,7 +3,7 @@ // export { AuthExtension, AuthAccountsResponse, setupAuthExtension } from "./auth"; -export { setupSupplyExtension, SupplyExtension, TotalSupplyAllReponse, TotalSupplyReponse } from "./supply"; +export { setupSupplyExtension, SupplyExtension, TotalSupplyAllResponse, TotalSupplyResponse } from "./supply"; // // Base types diff --git a/packages/sdk38/src/lcdapi/lcdclient.spec.ts b/packages/sdk38/src/lcdapi/lcdclient.spec.ts index a048cfa3..627075e1 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.spec.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.spec.ts @@ -117,7 +117,7 @@ describe("LcdClient", () => { it("works for two extensions", async () => { pendingWithoutWasmd(); - interface TotalSupplyAllReponse { + interface TotalSupplyAllResponse { readonly height: string; readonly result: LcdApiArray; } @@ -126,9 +126,9 @@ describe("LcdClient", () => { function setupSupplyExtension(base: LcdClient) { return { supply: { - totalAll: async (): Promise => { + totalAll: async (): Promise => { const path = `/supply/total`; - return (await base.get(path)) as TotalSupplyAllReponse; + return (await base.get(path)) as TotalSupplyAllResponse; }, }, }; @@ -161,7 +161,7 @@ describe("LcdClient", () => { }); }); - it("can merge two extension into the same module", async () => { + it("can merge two extensions into the same module", async () => { pendingWithoutWasmd(); // eslint-disable-next-line @typescript-eslint/explicit-function-return-type diff --git a/packages/sdk38/src/lcdapi/lcdclient.ts b/packages/sdk38/src/lcdapi/lcdclient.ts index f4e2f644..c2233651 100644 --- a/packages/sdk38/src/lcdapi/lcdclient.ts +++ b/packages/sdk38/src/lcdapi/lcdclient.ts @@ -292,7 +292,7 @@ export class LcdClient { } /** - * Broadcasts a signed transaction to into the transaction pool. + * Broadcasts a signed transaction to the transaction pool. * Depending on the client's broadcast mode, this might or might * wait for checkTx or deliverTx to be executed before returning. * diff --git a/packages/sdk38/src/lcdapi/supply.ts b/packages/sdk38/src/lcdapi/supply.ts index 06443134..7ddfc883 100644 --- a/packages/sdk38/src/lcdapi/supply.ts +++ b/packages/sdk38/src/lcdapi/supply.ts @@ -1,12 +1,12 @@ import { Coin } from "../coins"; import { LcdApiArray, LcdClient } from "./lcdclient"; -export interface TotalSupplyAllReponse { +export interface TotalSupplyAllResponse { readonly height: string; readonly result: LcdApiArray; } -export interface TotalSupplyReponse { +export interface TotalSupplyResponse { readonly height: string; /** The amount */ readonly result: string; @@ -14,8 +14,8 @@ export interface TotalSupplyReponse { export interface SupplyExtension { readonly supply: { - readonly totalAll: () => Promise; - readonly total: (denom: string) => Promise; + readonly totalAll: () => Promise; + readonly total: (denom: string) => Promise; }; } diff --git a/packages/sdk38/types/lcdapi/base.d.ts b/packages/sdk38/types/lcdapi/base.d.ts index d7927b0f..8771acf4 100644 --- a/packages/sdk38/types/lcdapi/base.d.ts +++ b/packages/sdk38/types/lcdapi/base.d.ts @@ -7,12 +7,12 @@ import { CosmosSdkTx } from "../types"; export declare enum BroadcastMode { /** Return after tx commit */ Block = "block", - /** Return afer CheckTx */ + /** Return after CheckTx */ Sync = "sync", /** Return right away */ Async = "async", } -/** A reponse from the /txs/encode endpoint */ +/** A response from the /txs/encode endpoint */ export interface EncodeTxResponse { /** base64-encoded amino-binary encoded representation */ readonly tx: string; diff --git a/packages/sdk38/types/lcdapi/index.d.ts b/packages/sdk38/types/lcdapi/index.d.ts index dc0274a9..ca9ac056 100644 --- a/packages/sdk38/types/lcdapi/index.d.ts +++ b/packages/sdk38/types/lcdapi/index.d.ts @@ -1,5 +1,5 @@ export { AuthExtension, AuthAccountsResponse, setupAuthExtension } from "./auth"; -export { setupSupplyExtension, SupplyExtension, TotalSupplyAllReponse, TotalSupplyReponse } from "./supply"; +export { setupSupplyExtension, SupplyExtension, TotalSupplyAllResponse, TotalSupplyResponse } from "./supply"; export { BlockResponse, BroadcastMode, diff --git a/packages/sdk38/types/lcdapi/lcdclient.d.ts b/packages/sdk38/types/lcdapi/lcdclient.d.ts index 009e556e..ce67cc38 100644 --- a/packages/sdk38/types/lcdapi/lcdclient.d.ts +++ b/packages/sdk38/types/lcdapi/lcdclient.d.ts @@ -153,7 +153,7 @@ export declare class LcdClient { /** returns the amino-encoding of the transaction performed by the server */ encodeTx(tx: CosmosSdkTx): Promise; /** - * Broadcasts a signed transaction to into the transaction pool. + * Broadcasts a signed transaction to the transaction pool. * Depending on the client's broadcast mode, this might or might * wait for checkTx or deliverTx to be executed before returning. * diff --git a/packages/sdk38/types/lcdapi/supply.d.ts b/packages/sdk38/types/lcdapi/supply.d.ts index e9bf1d2c..5d9fe774 100644 --- a/packages/sdk38/types/lcdapi/supply.d.ts +++ b/packages/sdk38/types/lcdapi/supply.d.ts @@ -1,18 +1,18 @@ import { Coin } from "../coins"; import { LcdApiArray, LcdClient } from "./lcdclient"; -export interface TotalSupplyAllReponse { +export interface TotalSupplyAllResponse { readonly height: string; readonly result: LcdApiArray; } -export interface TotalSupplyReponse { +export interface TotalSupplyResponse { readonly height: string; /** The amount */ readonly result: string; } export interface SupplyExtension { readonly supply: { - readonly totalAll: () => Promise; - readonly total: (denom: string) => Promise; + readonly totalAll: () => Promise; + readonly total: (denom: string) => Promise; }; } export declare function setupSupplyExtension(base: LcdClient): SupplyExtension;

= (base: LcdClient) => P; export interface LcdClientBaseOptions { readonly apiUrl: string; @@ -65,142 +65,142 @@ function parseAxiosError(err: AxiosError): never { */ export class LcdClient { /** Constructs an LCD client with 0 modules */ - public static withModules(options: LcdClientBaseOptions): LcdClient; + public static withExtensions(options: LcdClientBaseOptions): LcdClient; /** Constructs an LCD client with 1 module */ - public static withModules( + public static withExtensions( options: LcdClientBaseOptions, - setupModuleA: LcdModuleSetup, + setupModuleA: LcdExtensionSetup, ): LcdClient & A; /** Constructs an LCD client with 2 modules */ - public static withModules( + public static withExtensions( options: LcdClientBaseOptions, - setupModuleA: LcdModuleSetup, - setupModuleB: LcdModuleSetup, + setupModuleA: LcdExtensionSetup, + setupModuleB: LcdExtensionSetup, ): LcdClient & A & B; /** Constructs an LCD client with 3 modules */ - public static withModules( + public static withExtensions( options: LcdClientBaseOptions, - setupModuleA: LcdModuleSetup, - setupModuleB: LcdModuleSetup, - setupModuleC: LcdModuleSetup, + setupModuleA: LcdExtensionSetup, + setupModuleB: LcdExtensionSetup, + setupModuleC: LcdExtensionSetup, ): LcdClient & A & B & C; /** Constructs an LCD client with 4 modules */ - public static withModules< - A extends LcdModule, - B extends LcdModule, - C extends LcdModule, - D extends LcdModule + public static withExtensions< + A extends LcdExtension, + B extends LcdExtension, + C extends LcdExtension, + D extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdModuleSetup, - setupModuleB: LcdModuleSetup, - setupModuleC: LcdModuleSetup, - setupModuleD: LcdModuleSetup, + setupModuleA: LcdExtensionSetup, + setupModuleB: LcdExtensionSetup, + setupModuleC: LcdExtensionSetup, + setupModuleD: LcdExtensionSetup, ): LcdClient & A & B & C & D; /** Constructs an LCD client with 5 modules */ - public static withModules< - A extends LcdModule, - B extends LcdModule, - C extends LcdModule, - D extends LcdModule, - E extends LcdModule + public static withExtensions< + A extends LcdExtension, + B extends LcdExtension, + C extends LcdExtension, + D extends LcdExtension, + E extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdModuleSetup, - setupModuleB: LcdModuleSetup, - setupModuleC: LcdModuleSetup, - setupModuleD: LcdModuleSetup, - setupModuleE: LcdModuleSetup, + setupModuleA: LcdExtensionSetup, + setupModuleB: LcdExtensionSetup, + setupModuleC: LcdExtensionSetup, + setupModuleD: LcdExtensionSetup, + setupModuleE: LcdExtensionSetup, ): LcdClient & A & B & C & D & E; /** Constructs an LCD client with 6 modules */ - public static withModules< - A extends LcdModule, - B extends LcdModule, - C extends LcdModule, - D extends LcdModule, - E extends LcdModule, - F extends LcdModule + public static withExtensions< + A extends LcdExtension, + B extends LcdExtension, + C extends LcdExtension, + D extends LcdExtension, + E extends LcdExtension, + F extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdModuleSetup, - setupModuleB: LcdModuleSetup, - setupModuleC: LcdModuleSetup, - setupModuleD: LcdModuleSetup, - setupModuleE: LcdModuleSetup, - setupModuleF: LcdModuleSetup, + setupModuleA: LcdExtensionSetup, + setupModuleB: LcdExtensionSetup, + setupModuleC: LcdExtensionSetup, + setupModuleD: LcdExtensionSetup, + setupModuleE: LcdExtensionSetup, + setupModuleF: LcdExtensionSetup, ): LcdClient & A & B & C & D & E & F; /** Constructs an LCD client with 7 modules */ - public static withModules< - A extends LcdModule, - B extends LcdModule, - C extends LcdModule, - D extends LcdModule, - E extends LcdModule, - F extends LcdModule, - G extends LcdModule + public static withExtensions< + A extends LcdExtension, + B extends LcdExtension, + C extends LcdExtension, + D extends LcdExtension, + E extends LcdExtension, + F extends LcdExtension, + G extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdModuleSetup, - setupModuleB: LcdModuleSetup, - setupModuleC: LcdModuleSetup, - setupModuleD: LcdModuleSetup, - setupModuleE: LcdModuleSetup, - setupModuleF: LcdModuleSetup, - setupModuleG: LcdModuleSetup, + setupModuleA: LcdExtensionSetup, + setupModuleB: LcdExtensionSetup, + setupModuleC: LcdExtensionSetup, + setupModuleD: LcdExtensionSetup, + setupModuleE: LcdExtensionSetup, + setupModuleF: LcdExtensionSetup, + setupModuleG: LcdExtensionSetup, ): LcdClient & A & B & C & D & E & F & G; /** Constructs an LCD client with 8 modules */ - public static withModules< - A extends LcdModule, - B extends LcdModule, - C extends LcdModule, - D extends LcdModule, - E extends LcdModule, - F extends LcdModule, - G extends LcdModule, - H extends LcdModule + public static withExtensions< + A extends LcdExtension, + B extends LcdExtension, + C extends LcdExtension, + D extends LcdExtension, + E extends LcdExtension, + F extends LcdExtension, + G extends LcdExtension, + H extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA: LcdModuleSetup, - setupModuleB: LcdModuleSetup, - setupModuleC: LcdModuleSetup, - setupModuleD: LcdModuleSetup, - setupModuleE: LcdModuleSetup, - setupModuleF: LcdModuleSetup, - setupModuleG: LcdModuleSetup, - setupModuleH: LcdModuleSetup, + setupModuleA: LcdExtensionSetup, + setupModuleB: LcdExtensionSetup, + setupModuleC: LcdExtensionSetup, + setupModuleD: LcdExtensionSetup, + setupModuleE: LcdExtensionSetup, + setupModuleF: LcdExtensionSetup, + setupModuleG: LcdExtensionSetup, + setupModuleH: LcdExtensionSetup, ): LcdClient & A & B & C & D & E & F & G & H; - public static withModules< - A extends LcdModule, - B extends LcdModule, - C extends LcdModule, - D extends LcdModule, - E extends LcdModule, - F extends LcdModule, - G extends LcdModule, - H extends LcdModule + public static withExtensions< + A extends LcdExtension, + B extends LcdExtension, + C extends LcdExtension, + D extends LcdExtension, + E extends LcdExtension, + F extends LcdExtension, + G extends LcdExtension, + H extends LcdExtension >( options: LcdClientBaseOptions, - setupModuleA?: LcdModuleSetup, - setupModuleB?: LcdModuleSetup, - setupModuleC?: LcdModuleSetup, - setupModuleD?: LcdModuleSetup, - setupModuleE?: LcdModuleSetup, - setupModuleF?: LcdModuleSetup, - setupModuleG?: LcdModuleSetup, - setupModuleH?: LcdModuleSetup, + setupModuleA?: LcdExtensionSetup, + setupModuleB?: LcdExtensionSetup, + setupModuleC?: LcdExtensionSetup, + setupModuleD?: LcdExtensionSetup, + setupModuleE?: LcdExtensionSetup, + setupModuleF?: LcdExtensionSetup, + setupModuleG?: LcdExtensionSetup, + setupModuleH?: LcdExtensionSetup, ): any { const client = new LcdClient(options.apiUrl, options.broadcastMode); - const modules = new Array(); + const modules = new Array(); if (setupModuleA) modules.push(setupModuleA(client)); if (setupModuleB) modules.push(setupModuleB(client)); if (setupModuleC) modules.push(setupModuleC(client)); diff --git a/packages/sdk38/src/lcdapi/supply.spec.ts b/packages/sdk38/src/lcdapi/supply.spec.ts index 59b03009..ca95f5d8 100644 --- a/packages/sdk38/src/lcdapi/supply.spec.ts +++ b/packages/sdk38/src/lcdapi/supply.spec.ts @@ -1,13 +1,13 @@ import { pendingWithoutWasmd, wasmd } from "../testutils.spec"; import { LcdClient } from "./lcdclient"; -import { setupSupplyModule } from "./supply"; +import { setupSupplyExtension } from "./supply"; -describe("supply", () => { +describe("SupplyExtension", () => { describe("totalAll", () => { it("works", async () => { pendingWithoutWasmd(); - const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupSupplyModule); + const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }, setupSupplyExtension); const supply = await client.supply.totalAll(); expect(supply).toEqual({ height: jasmine.stringMatching(/^[0-9]+$/), @@ -29,7 +29,7 @@ describe("supply", () => { it("works", async () => { pendingWithoutWasmd(); - const client = LcdClient.withModules({ apiUrl: wasmd.endpoint }, setupSupplyModule); + const client = LcdClient.withExtensions({ apiUrl: wasmd.endpoint }, setupSupplyExtension); const supply = await client.supply.total("ucosm"); expect(supply).toEqual({ height: jasmine.stringMatching(/^[0-9]+$/), diff --git a/packages/sdk38/src/lcdapi/supply.ts b/packages/sdk38/src/lcdapi/supply.ts index efbd6833..a0bfb77a 100644 --- a/packages/sdk38/src/lcdapi/supply.ts +++ b/packages/sdk38/src/lcdapi/supply.ts @@ -1,5 +1,5 @@ import { Coin } from "../coins"; -import { LcdApiArray, LcdClient, LcdModule } from "./lcdclient"; +import { LcdApiArray, LcdClient, LcdExtension } from "./lcdclient"; export interface TotalSupplyAllReponse { readonly height: string; @@ -12,14 +12,14 @@ export interface TotalSupplyReponse { readonly result: string; } -export interface SupplyModule extends LcdModule { +export interface SupplyExtension extends LcdExtension { readonly supply: { readonly totalAll: () => Promise; readonly total: (denom: string) => Promise; }; } -export function setupSupplyModule(base: LcdClient): SupplyModule { +export function setupSupplyExtension(base: LcdClient): SupplyExtension { return { supply: { totalAll: async () => { diff --git a/packages/sdk38/types/cosmosclient.d.ts b/packages/sdk38/types/cosmosclient.d.ts index 3232ba75..5ce6b60c 100644 --- a/packages/sdk38/types/cosmosclient.d.ts +++ b/packages/sdk38/types/cosmosclient.d.ts @@ -1,5 +1,5 @@ import { Coin } from "./coins"; -import { AuthModule, BroadcastMode, LcdClient } from "./lcdapi"; +import { AuthExtension, BroadcastMode, LcdClient } from "./lcdapi"; import { Log } from "./logs"; import { CosmosSdkTx, PubKey, StdTx } from "./types"; export interface GetNonceResult { @@ -94,10 +94,10 @@ export interface Block { } /** Use for testing only */ export interface PrivateCosmWasmClient { - readonly lcdClient: LcdClient & AuthModule; + readonly lcdClient: LcdClient & AuthExtension; } export declare class CosmosClient { - protected readonly lcdClient: LcdClient & AuthModule; + protected readonly lcdClient: LcdClient & AuthExtension; /** Any address the chain considers valid (valid bech32 with proper prefix) */ protected anyValidAddress: string | undefined; private chainId; diff --git a/packages/sdk38/types/index.d.ts b/packages/sdk38/types/index.d.ts index d94febb4..fe93970c 100644 --- a/packages/sdk38/types/index.d.ts +++ b/packages/sdk38/types/index.d.ts @@ -20,20 +20,20 @@ export { export { makeSignBytes } from "./encoding"; export { AuthAccountsResponse, - AuthModule, + AuthExtension, BlockResponse, BroadcastMode, EncodeTxResponse, LcdApiArray, + LcdExtension, LcdClient, - LcdModule, NodeInfoResponse, normalizeLcdApiArray, PostTxsResponse, SearchTxsResponse, - setupAuthModule, - setupSupplyModule, - SupplyModule, + setupAuthExtension, + setupSupplyExtension, + SupplyExtension, TxsResponse, } from "./lcdapi"; export { RestClient } from "./restclient"; diff --git a/packages/sdk38/types/lcdapi/auth.d.ts b/packages/sdk38/types/lcdapi/auth.d.ts index 19945fc7..23ecb851 100644 --- a/packages/sdk38/types/lcdapi/auth.d.ts +++ b/packages/sdk38/types/lcdapi/auth.d.ts @@ -1,5 +1,5 @@ import { Coin } from "../coins"; -import { LcdClient, LcdModule } from "./lcdclient"; +import { LcdClient, LcdExtension } from "./lcdclient"; export interface CosmosSdkAccount { /** Bech32 account address */ readonly address: string; @@ -16,9 +16,9 @@ export interface AuthAccountsResponse { readonly value: CosmosSdkAccount; }; } -export interface AuthModule extends LcdModule { +export interface AuthExtension extends LcdExtension { readonly auth: { readonly account: (address: string) => Promise; }; } -export declare function setupAuthModule(base: LcdClient): AuthModule; +export declare function setupAuthExtension(base: LcdClient): AuthExtension; diff --git a/packages/sdk38/types/lcdapi/index.d.ts b/packages/sdk38/types/lcdapi/index.d.ts index ab6fc2a3..ad1eb321 100644 --- a/packages/sdk38/types/lcdapi/index.d.ts +++ b/packages/sdk38/types/lcdapi/index.d.ts @@ -1,5 +1,5 @@ -export { AuthModule, AuthAccountsResponse, setupAuthModule } from "./auth"; -export { setupSupplyModule, SupplyModule, TotalSupplyAllReponse, TotalSupplyReponse } from "./supply"; +export { AuthExtension, AuthAccountsResponse, setupAuthExtension } from "./auth"; +export { setupSupplyExtension, SupplyExtension, TotalSupplyAllReponse, TotalSupplyReponse } from "./supply"; export { BlockResponse, BroadcastMode, @@ -9,4 +9,4 @@ export { SearchTxsResponse, TxsResponse, } from "./base"; -export { LcdApiArray, LcdClient, LcdModule, normalizeLcdApiArray } from "./lcdclient"; +export { LcdApiArray, LcdClient, LcdExtension, normalizeLcdApiArray } from "./lcdclient"; diff --git a/packages/sdk38/types/lcdapi/lcdclient.d.ts b/packages/sdk38/types/lcdapi/lcdclient.d.ts index f0aaaeef..5b7616f6 100644 --- a/packages/sdk38/types/lcdapi/lcdclient.d.ts +++ b/packages/sdk38/types/lcdapi/lcdclient.d.ts @@ -11,8 +11,8 @@ import { /** Unfortunately, Cosmos SDK encodes empty arrays as null */ export declare type LcdApiArray = readonly T[] | null; export declare function normalizeLcdApiArray(backend: LcdApiArray): readonly T[]; -export declare type LcdModule = Record any>>; -declare type LcdModuleSetup = (base: LcdClient) => M; +export declare type LcdExtension = Record any>>; +declare type LcdExtensionSetup