diff --git a/packages/sdk/src/cosmwasmclient.spec.ts b/packages/sdk/src/cosmwasmclient.spec.ts index 34e0143f..865e8e42 100644 --- a/packages/sdk/src/cosmwasmclient.spec.ts +++ b/packages/sdk/src/cosmwasmclient.spec.ts @@ -1,4 +1,5 @@ /* eslint-disable @typescript-eslint/camelcase */ +import { Sha256 } from "@iov/crypto"; import { Bech32, Encoding } from "@iov/encoding"; import { assert, sleep } from "@iov/utils"; import { ReadonlyDate } from "readonly-date"; @@ -19,7 +20,7 @@ import { } from "./testutils.spec"; import { CosmosSdkTx, MsgSend, StdFee } from "./types"; -const { fromAscii, fromUtf8, toAscii } = Encoding; +const { fromAscii, fromHex, fromUtf8, toAscii } = Encoding; const httpUrl = "http://localhost:1317"; @@ -33,10 +34,14 @@ const faucet = { address: "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6", }; -const unusedAccount = { +const unused = { address: "cosmos1cjsxept9rkggzxztslae9ndgpdyt2408lk850u", }; +const guest = { + address: "cosmos17d0jcz59jf68g52vq38tuuncmwwjk42u6mcxej", +}; + interface HackatomInstance { readonly initMsg: { readonly verifier: string; @@ -65,7 +70,7 @@ describe("CosmWasmClient", () => { it("works", async () => { pendingWithoutWasmd(); const client = new CosmWasmClient(httpUrl); - expect(await client.getNonce(unusedAccount.address)).toEqual({ + expect(await client.getNonce(unused.address)).toEqual({ accountNumber: 5, sequence: 0, }); @@ -86,8 +91,8 @@ describe("CosmWasmClient", () => { it("works", async () => { pendingWithoutWasmd(); const client = new CosmWasmClient(httpUrl); - expect(await client.getAccount(unusedAccount.address)).toEqual({ - address: unusedAccount.address, + expect(await client.getAccount(unused.address)).toEqual({ + address: unused.address, account_number: 5, sequence: 0, public_key: "", @@ -392,6 +397,135 @@ describe("CosmWasmClient", () => { }); }); + describe("getCodes", () => { + it("works", async () => { + pendingWithoutWasmd(); + const client = new CosmWasmClient(httpUrl); + const result = await client.getCodes(); + expect(result.length).toBeGreaterThanOrEqual(1); + const [first] = result; + expect(first).toEqual({ + id: 1, + checksum: "b26861a6aa9858585ed905a590272735bd4fe8177c708940236224e8c9ff73ca", + source: undefined, + builder: undefined, + creator: faucet.address, + }); + }); + }); + + describe("getCodeDetails", () => { + it("works", async () => { + pendingWithoutWasmd(); + const client = new CosmWasmClient(httpUrl); + const result = await client.getCodeDetails(1); + const checksum = new Sha256(result.wasm).digest(); + expect(checksum).toEqual(fromHex("b26861a6aa9858585ed905a590272735bd4fe8177c708940236224e8c9ff73ca")); + }); + }); + + describe("getContracts", () => { + it("works", async () => { + pendingWithoutWasmd(); + const client = new CosmWasmClient(httpUrl); + const result = await client.getContracts(1); + expect(result.length).toBeGreaterThanOrEqual(3); + const [jade, hash, isa] = result; + expect(hash).toEqual({ + codeId: 1, + creator: faucet.address, + initMsg: { + decimals: 5, + name: "Hash token", + symbol: "HASH", + initial_balances: [ + { + address: faucet.address, + amount: "11", + }, + { + address: unused.address, + amount: "12812345", + }, + { + address: guest.address, + amount: "22004000000", + }, + ], + }, + }); + expect(isa).toEqual({ + codeId: 1, + creator: faucet.address, + initMsg: { + decimals: 0, + name: "Isa Token", + symbol: "ISA", + initial_balances: [ + { + address: faucet.address, + amount: "999999999", + }, + { + address: unused.address, + amount: "42", + }, + ], + }, + }); + expect(jade).toEqual({ + codeId: 1, + creator: faucet.address, + initMsg: { + decimals: 18, + name: "Jade Token", + symbol: "JADE", + initial_balances: [ + { + address: faucet.address, + amount: "189189189000000000000000000", // 189189189 JADE + }, + { + address: guest.address, + amount: "189500000000000000000", // 189.5 JADE + }, + ], + }, + }); + }); + }); + + describe("getContract", () => { + it("works for HASH instance", async () => { + pendingWithoutWasmd(); + const client = new CosmWasmClient(httpUrl); + const hash = await client.getContract("cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5"); + expect(hash).toEqual({ + codeId: 1, + creator: faucet.address, + initMsg: { + decimals: 5, + name: "Hash token", + symbol: "HASH", + initial_balances: [ + { + address: faucet.address, + amount: "11", + }, + { + address: unused.address, + amount: "12812345", + }, + { + address: guest.address, + amount: "22004000000", + }, + ], + }, + }); + }); + }); + describe("queryContractRaw", () => { const configKey = toAscii("config"); const otherKey = toAscii("this_does_not_exist"); diff --git a/packages/sdk/src/cosmwasmclient.ts b/packages/sdk/src/cosmwasmclient.ts index 313bbbb2..650fd606 100644 --- a/packages/sdk/src/cosmwasmclient.ts +++ b/packages/sdk/src/cosmwasmclient.ts @@ -48,6 +48,30 @@ export interface SearchTxFilter { readonly maxHeight?: number; } +export interface Code { + readonly id: number; + /** Bech32 account address */ + readonly creator: string; + /** Hex-encoded sha256 hash of the code stored here */ + readonly checksum: string; + readonly source?: string; + readonly builder?: string; +} + +export interface CodeDetails { + /** The original wasm bytes */ + readonly wasm: Uint8Array; +} + +export interface Contract { + // TODO: add contract address (https://github.com/cosmwasm/wasmd/issues/75) + readonly codeId: number; + /** Bech32 account address */ + readonly creator: string; + /** Argument passed on initialization of the contract */ + readonly initMsg: object; +} + export class CosmWasmClient { protected readonly restClient: RestClient; @@ -166,6 +190,42 @@ export class CosmWasmClient { }; } + public async getCodes(): Promise { + const result = await this.restClient.listCodeInfo(); + return result.map(r => ({ + id: r.id, + creator: r.creator, + checksum: Encoding.toHex(Encoding.fromHex(r.code_hash)), + source: r.source || undefined, + builder: r.builder || undefined, + })); + } + + public async getCodeDetails(codeId: number): Promise { + const result = await this.restClient.getCode(codeId); + return { + wasm: result, + }; + } + + public async getContracts(codeId: number): Promise { + const result = await this.restClient.listContractsByCodeId(codeId); + return result.map(r => ({ + codeId: r.code_id, + creator: r.creator, + initMsg: r.init_msg, + })); + } + + public async getContract(address: string): Promise { + const result = await this.restClient.getContractInfo(address); + return { + codeId: result.code_id, + creator: result.creator, + initMsg: result.init_msg, + }; + } + /** * Returns the data at the key if present (raw contract dependent storage data) * or null if no data at this key. diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 850a3294..2f343f2f 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -7,6 +7,9 @@ export { unmarshalTx } from "./decoding"; export { makeSignBytes, marshalTx } from "./encoding"; export { BroadcastMode, RestClient, TxsResponse } from "./restclient"; export { + Code, + CodeDetails, + Contract, CosmWasmClient, GetNonceResult, PostTxResult, diff --git a/packages/sdk/src/restclient.ts b/packages/sdk/src/restclient.ts index 2387692f..a62a4cfe 100644 --- a/packages/sdk/src/restclient.ts +++ b/packages/sdk/src/restclient.ts @@ -1,16 +1,7 @@ import { Encoding } from "@iov/encoding"; import axios, { AxiosError, AxiosInstance } from "axios"; -import { - CodeInfo, - ContractInfo, - CosmosSdkAccount, - CosmosSdkTx, - Model, - parseWasmData, - StdTx, - WasmData, -} from "./types"; +import { CosmosSdkAccount, CosmosSdkTx, Model, parseWasmData, StdTx, WasmData } from "./types"; const { fromBase64, toHex, toUtf8 } = Encoding; @@ -131,6 +122,25 @@ interface EncodeTxResponse { readonly tx: string; } +export interface CodeInfo { + readonly id: number; + /** Bech32 account address */ + readonly creator: string; + /** Hex-encoded sha256 hash of the code stored here */ + readonly code_hash: string; + // TODO: these are not supported in current wasmd + readonly source?: string; + readonly builder?: string; +} + +export interface ContractInfo { + readonly code_id: number; + /** Bech32 account address */ + readonly creator: string; + /** Argument passed on initialization of the contract */ + readonly init_msg: object; +} + interface GetCodeResult { // base64 encoded wasm readonly code: string; diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 75ab00a5..46e08026 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -160,30 +160,6 @@ export interface CosmosSdkAccount { readonly sequence: number; } -export interface CodeInfo { - readonly id: number; - /** Bech32 account address */ - readonly creator: string; - /** Hex-encoded sha256 hash of the code stored here */ - readonly code_hash: string; - // TODO: these are not supported in current wasmd - readonly source?: string; - readonly builder?: string; -} - -export interface CodeDetails { - // TODO: this should be base64 encoded string with content - not in current stack - readonly code: string; -} - -export interface ContractInfo { - readonly code_id: number; - /** Bech32 account address */ - readonly creator: string; - /** Argument passed on initialization of the contract */ - readonly init_msg: object; -} - export interface WasmData { // key is hex-encoded readonly key: string; diff --git a/packages/sdk/types/cosmwasmclient.d.ts b/packages/sdk/types/cosmwasmclient.d.ts index 5cdc1c6f..f6ff12e5 100644 --- a/packages/sdk/types/cosmwasmclient.d.ts +++ b/packages/sdk/types/cosmwasmclient.d.ts @@ -25,6 +25,26 @@ export interface SearchTxFilter { readonly minHeight?: number; readonly maxHeight?: number; } +export interface Code { + readonly id: number; + /** Bech32 account address */ + readonly creator: string; + /** Hex-encoded sha256 hash of the code stored here */ + readonly checksum: string; + readonly source?: string; + readonly builder?: string; +} +export interface CodeDetails { + /** The original wasm bytes */ + readonly wasm: Uint8Array; +} +export interface Contract { + readonly codeId: number; + /** Bech32 account address */ + readonly creator: string; + /** Argument passed on initialization of the contract */ + readonly initMsg: object; +} export declare class CosmWasmClient { protected readonly restClient: RestClient; constructor(url: string, broadcastMode?: BroadcastMode); @@ -50,6 +70,10 @@ export declare class CosmWasmClient { getBlock(height?: number): Promise; searchTx(query: SearchTxQuery, filter?: SearchTxFilter): Promise; postTx(tx: StdTx): Promise; + getCodes(): Promise; + getCodeDetails(codeId: number): Promise; + getContracts(codeId: number): Promise; + getContract(address: string): Promise; /** * Returns the data at the key if present (raw contract dependent storage data) * or null if no data at this key. diff --git a/packages/sdk/types/index.d.ts b/packages/sdk/types/index.d.ts index 2d158036..6a0b7d20 100644 --- a/packages/sdk/types/index.d.ts +++ b/packages/sdk/types/index.d.ts @@ -6,6 +6,9 @@ export { unmarshalTx } from "./decoding"; export { makeSignBytes, marshalTx } from "./encoding"; export { BroadcastMode, RestClient, TxsResponse } from "./restclient"; export { + Code, + CodeDetails, + Contract, CosmWasmClient, GetNonceResult, PostTxResult, diff --git a/packages/sdk/types/restclient.d.ts b/packages/sdk/types/restclient.d.ts index e5f96309..9c9ac63d 100644 --- a/packages/sdk/types/restclient.d.ts +++ b/packages/sdk/types/restclient.d.ts @@ -1,4 +1,4 @@ -import { CodeInfo, ContractInfo, CosmosSdkAccount, CosmosSdkTx, Model, StdTx } from "./types"; +import { CosmosSdkAccount, CosmosSdkTx, Model, StdTx } from "./types"; interface NodeInfo { readonly network: string; } @@ -92,6 +92,22 @@ export interface PostTxsResponse { interface EncodeTxResponse { readonly tx: string; } +export interface CodeInfo { + readonly id: number; + /** Bech32 account address */ + readonly creator: string; + /** Hex-encoded sha256 hash of the code stored here */ + readonly code_hash: string; + readonly source?: string; + readonly builder?: string; +} +export interface ContractInfo { + readonly code_id: number; + /** Bech32 account address */ + readonly creator: string; + /** Argument passed on initialization of the contract */ + readonly init_msg: object; +} interface GetCodeResult { readonly code: string; } diff --git a/packages/sdk/types/types.d.ts b/packages/sdk/types/types.d.ts index 2cf95184..37649887 100644 --- a/packages/sdk/types/types.d.ts +++ b/packages/sdk/types/types.d.ts @@ -116,25 +116,6 @@ export interface CosmosSdkAccount { readonly account_number: number; readonly sequence: number; } -export interface CodeInfo { - readonly id: number; - /** Bech32 account address */ - readonly creator: string; - /** Hex-encoded sha256 hash of the code stored here */ - readonly code_hash: string; - readonly source?: string; - readonly builder?: string; -} -export interface CodeDetails { - readonly code: string; -} -export interface ContractInfo { - readonly code_id: number; - /** Bech32 account address */ - readonly creator: string; - /** Argument passed on initialization of the contract */ - readonly init_msg: object; -} export interface WasmData { readonly key: string; readonly val: string;