mirror of
https://github.com/cosmos/cosmjs.git
synced 2025-03-11 14:09:15 +00:00
Merge pull request #87 from confio/CosmWasmClient-queries
Add query support to CosmWasmClient
This commit is contained in:
commit
aaab4b89ab
@ -160,8 +160,7 @@ export class CosmWasmConnection implements BlockchainConnection {
|
||||
this.erc20Tokens.map(
|
||||
async (erc20): Promise<Amount> => {
|
||||
const queryMsg = { balance: { address: address } };
|
||||
// tslint:disable-next-line: deprecation
|
||||
const smart = await this.restClient.queryContractSmart(erc20.contractAddress, queryMsg);
|
||||
const smart = await this.cosmWasmClient.queryContractSmart(erc20.contractAddress, queryMsg);
|
||||
const response = JSON.parse(fromAscii(smart));
|
||||
const normalizedBalance = new BN(response.balance).toString();
|
||||
return {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { Bech32, Encoding } from "@iov/encoding";
|
||||
import { assert } from "@iov/utils";
|
||||
|
||||
import { CosmWasmClient } from "./cosmwasmclient";
|
||||
@ -9,6 +10,8 @@ import cosmoshub from "./testdata/cosmoshub.json";
|
||||
import { getRandomizedHackatom, makeRandomAddress } from "./testutils.spec";
|
||||
import { Coin, CosmosSdkTx, MsgSend, StdFee } from "./types";
|
||||
|
||||
const { fromAscii, fromUtf8, toAscii } = Encoding;
|
||||
|
||||
const httpUrl = "http://localhost:1317";
|
||||
|
||||
function cosmosEnabled(): boolean {
|
||||
@ -35,6 +38,14 @@ const unusedAccount = {
|
||||
address: "cosmos1cjsxept9rkggzxztslae9ndgpdyt2408lk850u",
|
||||
};
|
||||
|
||||
interface HackatomInstance {
|
||||
readonly initMsg: {
|
||||
readonly verifier: string;
|
||||
readonly beneficiary: string;
|
||||
};
|
||||
readonly address: string;
|
||||
}
|
||||
|
||||
describe("CosmWasmClient", () => {
|
||||
describe("makeReadOnly", () => {
|
||||
it("can be constructed", () => {
|
||||
@ -359,4 +370,104 @@ describe("CosmWasmClient", () => {
|
||||
expect(contractBalance).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("queryContractRaw", () => {
|
||||
const configKey = toAscii("config");
|
||||
const otherKey = toAscii("this_does_not_exist");
|
||||
let contract: HackatomInstance | undefined;
|
||||
|
||||
beforeAll(async () => {
|
||||
if (cosmosEnabled()) {
|
||||
pendingWithoutCosmos();
|
||||
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
|
||||
const client = CosmWasmClient.makeWritable(httpUrl, faucet.address, signBytes => pen.sign(signBytes));
|
||||
const codeId = await client.upload(getRandomizedHackatom());
|
||||
const initMsg = { verifier: makeRandomAddress(), beneficiary: makeRandomAddress() };
|
||||
const contractAddress = await client.instantiate(codeId, initMsg);
|
||||
contract = { initMsg: initMsg, address: contractAddress };
|
||||
}
|
||||
});
|
||||
|
||||
it("can query existing key", async () => {
|
||||
pendingWithoutCosmos();
|
||||
assert(contract);
|
||||
|
||||
const client = CosmWasmClient.makeReadOnly(httpUrl);
|
||||
const raw = await client.queryContractRaw(contract.address, configKey);
|
||||
assert(raw, "must get result");
|
||||
expect(JSON.parse(fromUtf8(raw))).toEqual({
|
||||
verifier: Array.from(Bech32.decode(contract.initMsg.verifier).data),
|
||||
beneficiary: Array.from(Bech32.decode(contract.initMsg.beneficiary).data),
|
||||
funder: Array.from(Bech32.decode(faucet.address).data),
|
||||
});
|
||||
});
|
||||
|
||||
it("can query non-existent key", async () => {
|
||||
pendingWithoutCosmos();
|
||||
assert(contract);
|
||||
|
||||
const client = CosmWasmClient.makeReadOnly(httpUrl);
|
||||
const raw = await client.queryContractRaw(contract.address, otherKey);
|
||||
expect(raw).toBeNull();
|
||||
});
|
||||
|
||||
it("errors for non-existent contract", async () => {
|
||||
pendingWithoutCosmos();
|
||||
assert(contract);
|
||||
|
||||
const nonExistentAddress = makeRandomAddress();
|
||||
const client = CosmWasmClient.makeReadOnly(httpUrl);
|
||||
await client.queryContractRaw(nonExistentAddress, configKey).then(
|
||||
() => fail("must not succeed"),
|
||||
error => expect(error).toMatch(`No contract found at address "${nonExistentAddress}"`),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("queryContractSmart", () => {
|
||||
let contract: HackatomInstance | undefined;
|
||||
|
||||
beforeAll(async () => {
|
||||
if (cosmosEnabled()) {
|
||||
pendingWithoutCosmos();
|
||||
const pen = await Secp256k1Pen.fromMnemonic(faucet.mnemonic);
|
||||
const client = CosmWasmClient.makeWritable(httpUrl, faucet.address, signBytes => pen.sign(signBytes));
|
||||
const codeId = await client.upload(getRandomizedHackatom());
|
||||
const initMsg = { verifier: makeRandomAddress(), beneficiary: makeRandomAddress() };
|
||||
const contractAddress = await client.instantiate(codeId, initMsg);
|
||||
contract = { initMsg: initMsg, address: contractAddress };
|
||||
}
|
||||
});
|
||||
|
||||
it("works", async () => {
|
||||
pendingWithoutCosmos();
|
||||
assert(contract);
|
||||
|
||||
const client = CosmWasmClient.makeReadOnly(httpUrl);
|
||||
const verifier = await client.queryContractSmart(contract.address, { verifier: {} });
|
||||
expect(fromAscii(verifier)).toEqual(contract.initMsg.verifier);
|
||||
});
|
||||
|
||||
it("errors for malformed query message", async () => {
|
||||
pendingWithoutCosmos();
|
||||
assert(contract);
|
||||
|
||||
const client = CosmWasmClient.makeReadOnly(httpUrl);
|
||||
await client.queryContractSmart(contract.address, { broken: {} }).then(
|
||||
() => fail("must not succeed"),
|
||||
error => expect(error).toMatch(/Error parsing QueryMsg/i),
|
||||
);
|
||||
});
|
||||
|
||||
it("errors for non-existent contract", async () => {
|
||||
pendingWithoutCosmos();
|
||||
|
||||
const nonExistentAddress = makeRandomAddress();
|
||||
const client = CosmWasmClient.makeReadOnly(httpUrl);
|
||||
await client.queryContractSmart(nonExistentAddress, { verifier: {} }).then(
|
||||
() => fail("must not succeed"),
|
||||
error => expect(error).toMatch(`No contract found at address "${nonExistentAddress}"`),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -294,4 +294,39 @@ export class CosmWasmClient {
|
||||
logs: result.logs,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data at the key if present (raw contract dependent storage data)
|
||||
* or null if no data at this key.
|
||||
*
|
||||
* Promise is rejected when contract does not exist.
|
||||
*/
|
||||
public async queryContractRaw(address: string, key: Uint8Array): Promise<Uint8Array | null> {
|
||||
// just test contract existence
|
||||
const _info = await this.restClient.getContractInfo(address);
|
||||
|
||||
return this.restClient.queryContractRaw(address, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a "smart query" on the contract, returns raw data
|
||||
*
|
||||
* Promise is rejected when contract does not exist.
|
||||
* Promise is rejected for invalid query format.
|
||||
*/
|
||||
public async queryContractSmart(address: string, queryMsg: object): Promise<Uint8Array> {
|
||||
try {
|
||||
return await this.restClient.queryContractSmart(address, queryMsg);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
if (error.message === "not found: contract") {
|
||||
throw new Error(`No contract found at address "${address}"`);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -430,10 +430,11 @@ describe("RestClient", () => {
|
||||
expect((myInfo.init_msg as any).beneficiary).toEqual(beneficiaryAddress);
|
||||
|
||||
// make sure random addresses don't give useful info
|
||||
const nonExistentAddress = makeRandomAddress();
|
||||
await client
|
||||
.getContractInfo(beneficiaryAddress)
|
||||
.getContractInfo(nonExistentAddress)
|
||||
.then(() => fail("this shouldn't succeed"))
|
||||
.catch(error => expect(error).toMatch(`No contract with address ${beneficiaryAddress}`));
|
||||
.catch(error => expect(error).toMatch(`No contract found at address "${nonExistentAddress}"`));
|
||||
});
|
||||
|
||||
describe("contract state", () => {
|
||||
|
@ -311,7 +311,7 @@ export class RestClient {
|
||||
// rest server returns null if no data for the address
|
||||
const info: ContractInfo | null = parseWasmResponse(responseData as WasmResponse);
|
||||
if (!info) {
|
||||
throw new Error(`No contract with address ${address}`);
|
||||
throw new Error(`No contract found at address "${address}"`);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
14
packages/sdk/types/cosmwasmclient.d.ts
vendored
14
packages/sdk/types/cosmwasmclient.d.ts
vendored
@ -62,4 +62,18 @@ export declare class CosmWasmClient {
|
||||
memo?: string,
|
||||
transferAmount?: readonly Coin[],
|
||||
): Promise<ExecuteResult>;
|
||||
/**
|
||||
* Returns the data at the key if present (raw contract dependent storage data)
|
||||
* or null if no data at this key.
|
||||
*
|
||||
* Promise is rejected when contract does not exist.
|
||||
*/
|
||||
queryContractRaw(address: string, key: Uint8Array): Promise<Uint8Array | null>;
|
||||
/**
|
||||
* Makes a "smart query" on the contract, returns raw data
|
||||
*
|
||||
* Promise is rejected when contract does not exist.
|
||||
* Promise is rejected for invalid query format.
|
||||
*/
|
||||
queryContractSmart(address: string, queryMsg: object): Promise<Uint8Array>;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user