diff --git a/packages/bcp/src/cosmwasmconnection.ts b/packages/bcp/src/cosmwasmconnection.ts index 5640c72f..fbeedbf2 100644 --- a/packages/bcp/src/cosmwasmconnection.ts +++ b/packages/bcp/src/cosmwasmconnection.ts @@ -41,7 +41,7 @@ import { Caip5 } from "./caip5"; import { decodeAmount, parseTxsResponse } from "./decode"; import { accountToNonce, BankToken, Erc20Token } from "./types"; -const { toHex } = Encoding; +const { fromAscii, toHex } = Encoding; interface ChainData { readonly chainId: ChainId; @@ -168,9 +168,8 @@ export class CosmWasmConnection implements BlockchainConnection { this.erc20Tokens.map( async (erc20): Promise => { const queryMsg = { balance: { address: address } }; - const response = JSON.parse( - await this.restClient.queryContractSmart(erc20.contractAddress, queryMsg), - ); + const smart = await this.restClient.queryContractSmart(erc20.contractAddress, queryMsg); + const response = JSON.parse(fromAscii(smart)); const normalizedBalance = new BN(response.balance).toString(); return { fractionalDigits: erc20.fractionalDigits, diff --git a/packages/sdk/src/restclient.spec.ts b/packages/sdk/src/restclient.spec.ts index 53312d5e..3cb01de3 100644 --- a/packages/sdk/src/restclient.spec.ts +++ b/packages/sdk/src/restclient.spec.ts @@ -23,7 +23,7 @@ import { StdTx, } from "./types"; -const { fromBase64, fromHex, toAscii, toBase64, toHex } = Encoding; +const { fromAscii, fromBase64, fromHex, toAscii, toBase64, toHex } = Encoding; const httpUrl = "http://localhost:1317"; const defaultNetworkId = "testing"; @@ -496,9 +496,10 @@ describe("RestClient", () => { const state = await client.getAllContractState(contractAddress!); expect(state.length).toEqual(1); const data = state[0]; - expect(data.key.toLowerCase()).toEqual(toHex(expectedKey)); - expect((data.val as any).verifier).toBeDefined(); - expect((data.val as any).beneficiary).toBeDefined(); + 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); @@ -509,10 +510,11 @@ describe("RestClient", () => { pendingWithoutCosmos(); // query by one key - const model = await client.queryContractRaw(contractAddress!, expectedKey); - expect(model).not.toBeNull(); - expect((model as any).verifier).toBeDefined(); - expect((model as any).beneficiary).toBeDefined(); + 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")); @@ -528,7 +530,7 @@ describe("RestClient", () => { // we can query the verifier properly const verifier = await client.queryContractSmart(contractAddress!, { verifier: {} }); - expect(verifier).toEqual(faucet.address); + expect(fromAscii(verifier)).toEqual(faucet.address); // invalid query syntax throws an error await client.queryContractSmart(contractAddress!, { nosuchkey: {} }).then( diff --git a/packages/sdk/src/restclient.ts b/packages/sdk/src/restclient.ts index 14809dae..14857bdb 100644 --- a/packages/sdk/src/restclient.ts +++ b/packages/sdk/src/restclient.ts @@ -1,7 +1,17 @@ import { Encoding } from "@iov/encoding"; import axios, { AxiosError, AxiosInstance } from "axios"; -import { AminoTx, CodeInfo, ContractInfo, CosmosSdkAccount, isAminoStdTx, StdTx, WasmData } from "./types"; +import { + AminoTx, + CodeInfo, + ContractInfo, + CosmosSdkAccount, + isAminoStdTx, + Model, + parseWasmData, + StdTx, + WasmData, +} from "./types"; const { fromBase64, fromUtf8, toHex, toUtf8 } = Encoding; @@ -97,6 +107,11 @@ interface GetCodeResult { readonly code: string; } +interface SmartQueryResponse { + // base64 encoded response + readonly smart: string; +} + type RestClientResponse = | NodeInfoResponse | BlocksResponse @@ -306,32 +321,31 @@ export class RestClient { // Returns all contract state. // This is an empty array if no such contract, or contract has no data. - public async getAllContractState(address: string): Promise { + public async getAllContractState(address: string): Promise { const path = `/wasm/contract/${address}/state`; - const responseData = await this.get(path); - return parseWasmResponse(responseData as WasmResponse); + const responseData = (await this.get(path)) as WasmResponse; + const r = unwrapWasmResponse(responseData); + return r ? r.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 { + 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); - const data: readonly WasmData[] = parseWasmResponse(responseData as WasmResponse); - return data.length === 0 ? null : data[0].val; + 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, returns response verbatim (json.RawMessage) // Throws error if no such contract or invalid query format - public async queryContractSmart(address: string, query: object): Promise { + 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; - if (isWasmError(responseData)) { - throw new Error(responseData.error); - } + const responseData = (await this.get(path)) as WasmResponse; + const result = unwrapWasmResponse(responseData); // no extra parse here for now, see https://github.com/confio/cosmwasm/issues/144 - return responseData.result; + return fromBase64(result.smart); } } diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index d9ac9998..c7fcfe0f 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -1,3 +1,7 @@ +import { Encoding } from "@iov/encoding"; + +const { fromBase64, fromHex } = Encoding; + // We will move all needed *interfaces* from amino-js here // This means bcp can just import them from here (if needed at all) export interface Tx { @@ -190,6 +194,19 @@ export interface ContractInfo { export interface WasmData { // key is hex-encoded readonly key: string; - // value can be any decoded json, often an object but can be anything - readonly val: unknown; + // value is base64 encoded + readonly val: string; +} + +// Model is a parsed WasmData object +export interface Model { + readonly key: Uint8Array; + readonly val: Uint8Array; +} + +export function parseWasmData({ key, val }: WasmData): Model { + return { + key: fromHex(key), + val: fromBase64(val), + }; } diff --git a/packages/sdk/types/restclient.d.ts b/packages/sdk/types/restclient.d.ts index ad5f735d..b83f731a 100644 --- a/packages/sdk/types/restclient.d.ts +++ b/packages/sdk/types/restclient.d.ts @@ -1,4 +1,4 @@ -import { AminoTx, CodeInfo, ContractInfo, CosmosSdkAccount, StdTx, WasmData } from "./types"; +import { AminoTx, CodeInfo, ContractInfo, CosmosSdkAccount, Model, StdTx } from "./types"; interface NodeInfo { readonly network: string; } @@ -102,8 +102,8 @@ export declare class RestClient { listContractAddresses(): Promise; listContractsByCodeId(id: number): Promise; getContractInfo(address: string): Promise; - getAllContractState(address: string): Promise; - queryContractRaw(address: string, key: Uint8Array): Promise; - queryContractSmart(address: string, query: object): Promise; + getAllContractState(address: string): Promise; + queryContractRaw(address: string, key: Uint8Array): Promise; + queryContractSmart(address: string, query: object): Promise; } export {}; diff --git a/packages/sdk/types/types.d.ts b/packages/sdk/types/types.d.ts index 20b65f4d..9d61f398 100644 --- a/packages/sdk/types/types.d.ts +++ b/packages/sdk/types/types.d.ts @@ -141,6 +141,11 @@ export interface ContractInfo { } export interface WasmData { readonly key: string; - readonly val: unknown; + readonly val: string; } +export interface Model { + readonly key: Uint8Array; + readonly val: Uint8Array; +} +export declare function parseWasmData({ key, val }: WasmData): Model; export {}; diff --git a/scripts/cosm/env b/scripts/cosm/env index b24084da..70eb1944 100644 --- a/scripts/cosm/env +++ b/scripts/cosm/env @@ -1,5 +1,5 @@ # Choose from https://hub.docker.com/r/cosmwasm/wasmd-demo/tags REPOSITORY="cosmwasm/wasmd-demo" -VERSION="v0.0.5" +VERSION="v0.6.0" CONTAINER_NAME="wasmd"