Merge pull request #124 from confio/test-0.7

Test wasmd 0.7
This commit is contained in:
Ethan Frey 2020-02-28 21:28:24 +01:00 committed by GitHub
commit bd05fc3f67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 237 additions and 181 deletions

View File

@ -1,5 +1,5 @@
{
"version": "0.0.8",
"version": "0.7.0-alpha.2",
"useWorkspaces": true,
"npmClient": "yarn"
}

View File

@ -1,6 +1,6 @@
{
"name": "@cosmwasm/bcp",
"version": "0.0.8",
"version": "0.7.0-alpha.2",
"description": "Transaction codec and client to communicate with any wasmd blockchain",
"author": "Ethan Frey <ethanfrey@users.noreply.github.com>",
"license": "Apache-2.0",
@ -38,7 +38,7 @@
"pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js"
},
"dependencies": {
"@cosmwasm/sdk": "^0.0.8",
"@cosmwasm/sdk": "^0.7.0-alpha.2",
"@iov/bcp": "^2.1.0",
"@iov/crypto": "^2.1.0",
"@iov/encoding": "^2.1.0",

View File

@ -425,7 +425,7 @@ describe("CosmWasmConnection", () => {
assert(isConfirmedTransaction(getResponse), "Expected transaction to succeed");
assert(getResponse.log, "Log must be available");
const [firstLog] = JSON.parse(getResponse.log);
expect(firstLog.events.length).toEqual(1);
expect(firstLog.events.length).toEqual(2);
const { transaction, signatures } = getResponse;
assert(isSendTransaction(transaction), "Expected send transaction");

View File

@ -1,6 +1,6 @@
{
"name": "@cosmwasm/cli",
"version": "0.0.8",
"version": "0.7.0-alpha.2",
"description": "Command line interface",
"contributors": [
"IOV SAS <admin@iov.one>",
@ -36,7 +36,7 @@
"!**/testdata/"
],
"dependencies": {
"@cosmwasm/sdk": "^0.0.8",
"@cosmwasm/sdk": "^0.7.0-alpha.2",
"@iov/crypto": "^2.1.0",
"@iov/encoding": "^2.1.0",
"@iov/utils": "^2.0.2",

View File

@ -1,6 +1,6 @@
{
"name": "@cosmwasm/faucet",
"version": "0.0.8",
"version": "0.7.0-alpha.2",
"description": "The faucet",
"author": "Ethan Frey <ethanfrey@users.noreply.github.com>",
"license": "Apache-2.0",
@ -35,10 +35,10 @@
"test": "yarn build-or-skip && yarn test-node"
},
"dependencies": {
"@cosmwasm/bcp": "^0.0.8",
"@cosmwasm/bcp": "^0.7.0-alpha.2",
"@iov/bcp": "^2.1.0",
"@iov/crypto": "^2.1.0",
"@iov/encoding":"^2.1.0",
"@iov/encoding": "^2.1.0",
"@iov/keycontrol": "^2.1.0",
"@iov/utils": "^2.0.2",
"@koa/cors": "^3.0.0",

View File

@ -1,6 +1,6 @@
{
"name": "@cosmwasm/sdk",
"version": "0.0.8",
"version": "0.7.0-alpha.2",
"description": "CosmWasm SDK",
"author": "Ethan Frey <ethanfrey@users.noreply.github.com>",
"license": "Apache-2.0",

View File

@ -4,7 +4,7 @@ import { Bech32, Encoding } from "@iov/encoding";
import { assert, sleep } from "@iov/utils";
import { ReadonlyDate } from "readonly-date";
import { CosmWasmClient } from "./cosmwasmclient";
import { Code, CosmWasmClient } from "./cosmwasmclient";
import { makeSignBytes } from "./encoding";
import { findAttribute } from "./logs";
import { Secp256k1Pen } from "./pen";
@ -20,7 +20,7 @@ import {
} from "./testutils.spec";
import { CosmosSdkTx, MsgSend, StdFee } from "./types";
const { fromAscii, fromHex, fromUtf8, toAscii } = Encoding;
const { fromAscii, fromHex, fromUtf8, toAscii, toBase64 } = Encoding;
const httpUrl = "http://localhost:1317";
@ -406,7 +406,7 @@ describe("CosmWasmClient", () => {
const [first] = result;
expect(first).toEqual({
id: 1,
checksum: "b26861a6aa9858585ed905a590272735bd4fe8177c708940236224e8c9ff73ca",
checksum: "aff8c8873d79d2153a8b9066a0683fec3c903669267eb806ffa831dcd4b3daae",
source: undefined,
builder: undefined,
creator: faucet.address,
@ -419,8 +419,19 @@ describe("CosmWasmClient", () => {
pendingWithoutWasmd();
const client = new CosmWasmClient(httpUrl);
const result = await client.getCodeDetails(1);
const checksum = new Sha256(result.wasm).digest();
expect(checksum).toEqual(fromHex("b26861a6aa9858585ed905a590272735bd4fe8177c708940236224e8c9ff73ca"));
const expectedInfo: Code = {
id: 1,
checksum: "aff8c8873d79d2153a8b9066a0683fec3c903669267eb806ffa831dcd4b3daae",
source: undefined,
builder: undefined,
creator: faucet.address,
};
// check info
expect(result).toEqual(jasmine.objectContaining(expectedInfo));
// check data
expect(new Sha256(result.data).digest()).toEqual(fromHex(expectedInfo.checksum));
});
});
@ -430,67 +441,24 @@ describe("CosmWasmClient", () => {
const client = new CosmWasmClient(httpUrl);
const result = await client.getContracts(1);
expect(result.length).toBeGreaterThanOrEqual(3);
const [jade, hash, isa] = result;
const [hash, isa, jade] = result;
expect(hash).toEqual({
address: "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5",
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",
},
],
},
label: "HASH",
});
expect(isa).toEqual({
address: "cosmos1hqrdl6wstt8qzshwc6mrumpjk9338k0lr4dqxd",
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",
},
],
},
label: "ISA",
});
expect(jade).toEqual({
address: "cosmos18r5szma8hm93pvx6lwpjwyxruw27e0k5uw835c",
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
},
],
},
label: "JADE",
});
});
});
@ -501,8 +469,10 @@ describe("CosmWasmClient", () => {
const client = new CosmWasmClient(httpUrl);
const hash = await client.getContract("cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5");
expect(hash).toEqual({
address: "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5",
codeId: 1,
creator: faucet.address,
label: "HASH",
initMsg: {
decimals: 5,
name: "Hash token",
@ -538,7 +508,7 @@ describe("CosmWasmClient", () => {
const client = new SigningCosmWasmClient(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);
const contractAddress = await client.instantiate(codeId, initMsg, "random hackatom");
contract = { initMsg: initMsg, address: contractAddress };
}
});
@ -551,9 +521,9 @@ describe("CosmWasmClient", () => {
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),
verifier: toBase64(Bech32.decode(contract.initMsg.verifier).data),
beneficiary: toBase64(Bech32.decode(contract.initMsg.beneficiary).data),
funder: toBase64(Bech32.decode(faucet.address).data),
});
});
@ -589,7 +559,7 @@ describe("CosmWasmClient", () => {
const client = new SigningCosmWasmClient(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);
const contractAddress = await client.instantiate(codeId, initMsg, "a different hackatom");
contract = { initMsg: initMsg, address: contractAddress };
}
});

View File

@ -58,16 +58,20 @@ export interface Code {
readonly builder?: string;
}
export interface CodeDetails {
export interface CodeDetails extends Code {
/** The original wasm bytes */
readonly wasm: Uint8Array;
readonly data: Uint8Array;
}
export interface Contract {
// TODO: add contract address (https://github.com/cosmwasm/wasmd/issues/75)
readonly address: string;
readonly codeId: number;
/** Bech32 account address */
readonly creator: string;
readonly label: string;
}
export interface ContractDetails extends Contract {
/** Argument passed on initialization of the contract */
readonly initMsg: object;
}
@ -192,36 +196,53 @@ export class CosmWasmClient {
public async getCodes(): Promise<readonly Code[]> {
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,
}));
return result.map(
(entry): Code => ({
id: entry.id,
creator: entry.creator,
checksum: Encoding.toHex(Encoding.fromHex(entry.code_hash)),
source: entry.source || undefined,
builder: entry.builder || undefined,
}),
);
}
public async getCodeDetails(codeId: number): Promise<CodeDetails> {
const result = await this.restClient.getCode(codeId);
// TODO: implement as one request when https://github.com/cosmwasm/wasmd/issues/90 is done
const [codeInfos, getCodeResult] = await Promise.all([this.getCodes(), this.restClient.getCode(codeId)]);
const codeInfo = codeInfos.find(code => code.id === codeId);
if (!codeInfo) throw new Error("No code info found");
return {
wasm: result,
...codeInfo,
data: getCodeResult,
};
}
public async getContracts(codeId: number): Promise<readonly Contract[]> {
const result = await this.restClient.listContractsByCodeId(codeId);
return result.map(r => ({
codeId: r.code_id,
creator: r.creator,
initMsg: r.init_msg,
}));
return result.map(
(entry): Contract => ({
address: entry.address,
codeId: entry.code_id,
creator: entry.creator,
label: entry.label,
}),
);
}
public async getContract(address: string): Promise<Contract> {
/**
* Throws an error if no contract was found at the address
*/
public async getContract(address: string): Promise<ContractDetails> {
const result = await this.restClient.getContractInfo(address);
if (!result) throw new Error(`No contract found at address "${address}"`);
return {
address: result.address,
codeId: result.code_id,
creator: result.creator,
label: result.label,
initMsg: result.init_msg,
};
}
@ -234,7 +255,7 @@ export class CosmWasmClient {
*/
public async queryContractRaw(address: string, key: Uint8Array): Promise<Uint8Array | null> {
// just test contract existence
const _info = await this.restClient.getContractInfo(address);
const _info = await this.getContract(address);
return this.restClient.queryContractRaw(address, key);
}
@ -250,7 +271,7 @@ export class CosmWasmClient {
return await this.restClient.queryContractSmart(address, queryMsg);
} catch (error) {
if (error instanceof Error) {
if (error.message === "not found: contract") {
if (error.message.startsWith("not found: contract")) {
throw new Error(`No contract found at address "${address}"`);
} else {
throw error;

View File

@ -1,13 +1,22 @@
/* eslint-disable @typescript-eslint/camelcase */
import { isNonNullObject } from "@iov/encoding";
const supportedEventTypes: readonly string[] = ["message", "transfer", "wasm"];
export type SupportedEventType = "message" | "transfer" | "wasm";
export function isSupportedEventType(data: any): data is SupportedEventType {
if (typeof data !== "string") return false;
return supportedEventTypes.includes(data);
}
export interface Attribute {
readonly key: string;
readonly value: string;
}
export interface Event {
readonly type: "message" | "transfer";
readonly type: SupportedEventType;
readonly attributes: readonly Attribute[];
}
@ -34,7 +43,9 @@ export function parseAttribute(input: unknown): Attribute {
export function parseEvent(input: unknown): Event {
if (!isNonNullObject(input)) throw new Error("Event must be a non-null object");
const { type, attributes } = input as any;
if (type !== "message" && type !== "transfer") throw new Error("Event must be of type message or transfer");
if (!isSupportedEventType(type)) {
throw new Error(`Event type must be one of ${supportedEventTypes.join(", ")}; got ${type}`);
}
if (!Array.isArray(attributes)) throw new Error("Event's attributes must be an array");
return {
type: type,

View File

@ -12,6 +12,7 @@ import { PostTxsResponse, RestClient, TxsResponse } from "./restclient";
import { SigningCosmWasmClient } from "./signingcosmwasmclient";
import cosmoshub from "./testdata/cosmoshub.json";
import {
bech32AddressMatcher,
getRandomizedHackatom,
makeRandomAddress,
pendingWithoutWasmd,
@ -108,6 +109,7 @@ async function instantiateContract(
value: {
sender: faucet.address,
code_id: codeId.toString(),
label: "my escrow",
init_msg: {
verifier: faucet.address,
beneficiary: beneficiaryAddress,
@ -143,7 +145,7 @@ async function executeContract(
value: {
sender: faucet.address,
contract: contractAddress,
msg: {},
msg: { release: {} },
sent_funds: [],
},
};
@ -580,9 +582,15 @@ describe("RestClient", () => {
{
const result = await executeContract(client, pen, contractAddress);
expect(result.code).toBeFalsy();
// console.log("Raw log:", result.raw_log);
const [firstLog] = parseLogs(result.logs);
expect(firstLog.log).toEqual(`released funds to ${beneficiaryAddress}`);
// 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;
@ -662,9 +670,13 @@ describe("RestClient", () => {
}
// create new instance and compare before and after
const existingContracts = await client.listContractAddresses();
const existingContractsByCode = await client.listContractsByCodeId(codeId);
existingContractsByCode.forEach(ctc => expect(ctc.code_id).toEqual(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();
@ -672,32 +684,27 @@ describe("RestClient", () => {
const contractAddressAttr = findAttribute(logs, "message", "contract_address");
const myAddress = contractAddressAttr.value;
// ensure we were added to the list
const newContracts = await client.listContractAddresses();
expect(newContracts.length).toEqual(existingContracts.length + 1);
// note: we are NOT guaranteed to be added to the end
const diff = newContracts.filter(x => !existingContracts.includes(x));
expect(diff.length).toEqual(1);
const lastContract = diff[0];
expect(lastContract).toEqual(myAddress);
// also by codeID list
const newContractsByCode = await client.listContractsByCodeId(codeId);
newContractsByCode.forEach(ctc => expect(ctc.code_id).toEqual(codeId));
expect(newContractsByCode.length).toEqual(existingContractsByCode.length + 1);
const newContract = newContractsByCode[newContractsByCode.length - 1];
expect(newContract).toEqual(
jasmine.objectContaining({
code_id: codeId,
creator: faucet.address,
label: "my escrow",
}),
);
// check out info
const myInfo = await client.getContractInfo(myAddress);
assert(myInfo);
expect(myInfo.code_id).toEqual(codeId);
expect(myInfo.creator).toEqual(faucet.address);
expect((myInfo.init_msg as any).beneficiary).toEqual(beneficiaryAddress);
// make sure random addresses don't give useful info
const nonExistentAddress = makeRandomAddress();
await client
.getContractInfo(nonExistentAddress)
.then(() => fail("this shouldn't succeed"))
.catch(error => expect(error).toMatch(`No contract found at address "${nonExistentAddress}"`));
expect(await client.getContractInfo(nonExistentAddress)).toBeNull();
});
describe("contract state", () => {

View File

@ -133,10 +133,16 @@ export interface CodeInfo {
readonly builder?: string;
}
// This is list view, without contract info
export interface ContractInfo {
readonly address: string;
readonly code_id: number;
/** Bech32 account address */
readonly creator: string;
readonly label: string;
}
export interface ContractDetails extends ContractInfo {
/** Argument passed on initialization of the contract */
readonly init_msg: object;
}
@ -160,6 +166,9 @@ type RestClientResponse =
| PostTxsResponse
| EncodeTxResponse
| WasmResponse<string>
| WasmResponse<CodeInfo[]>
| WasmResponse<ContractInfo[] | null>
| WasmResponse<ContractDetails>
| WasmResponse<GetCodeResult>;
/**
@ -187,28 +196,23 @@ function unwrapWasmResponse<T>(response: WasmResponse<T>): T {
return response.result;
}
function parseWasmResponse(response: WasmResponse<string>): any {
if (isWasmError(response)) {
throw new Error(response.error);
}
return JSON.parse(response.result);
}
// 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 parseAxios500error(err: AxiosError): never {
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) {
throw new Error(data.error);
if (data.error && typeof data.error === "string") {
errorText = data.error;
} else if (typeof data === "string") {
throw new Error(data);
errorText = data;
} else {
throw new Error(JSON.stringify(data));
errorText = JSON.stringify(data);
}
throw new Error(`${errorText} (HTTP ${err.response.status})`);
} else {
throw err;
}
@ -230,7 +234,7 @@ export class RestClient {
}
public async get(path: string): Promise<RestClientResponse> {
const { data } = await this.client.get(path).catch(parseAxios500error);
const { data } = await this.client.get(path).catch(parseAxiosError);
if (data === null) {
throw new Error("Received null response from server");
}
@ -238,7 +242,7 @@ export class RestClient {
}
public async post(path: string, params: PostTxsParams): Promise<RestClientResponse> {
const { data } = await this.client.post(path, params).catch(parseAxios500error);
const { data } = await this.client.post(path, params).catch(parseAxiosError);
if (data === null) {
throw new Error("Received null response from server");
}
@ -335,9 +339,8 @@ export class RestClient {
// 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<readonly CodeInfo[]> {
const path = `/wasm/code`;
const responseData = await this.get(path);
// answer may be null (empty array)
return parseWasmResponse(responseData as WasmResponse) || [];
const responseData = (await this.get(path)) as WasmResponse<CodeInfo[]>;
return unwrapWasmResponse(responseData);
}
// this will download the original wasm bytecode by code id
@ -349,32 +352,32 @@ export class RestClient {
return fromBase64(code);
}
public async listContractAddresses(): Promise<readonly string[]> {
const path = `/wasm/contract`;
const responseData = await this.get(path);
// answer may be null (go's encoding of empty array)
const addresses: string[] | null = parseWasmResponse(responseData as WasmResponse);
return addresses || [];
}
public async listContractsByCodeId(id: number): Promise<readonly ContractInfo[]> {
const path = `/wasm/code/${id}/contracts`;
const responseData = await this.get(path);
// answer may be null (go's encoding of empty array)
const contracts: ContractInfo[] | null = parseWasmResponse(responseData as WasmResponse);
return contracts || [];
const responseData = (await this.get(path)) as WasmResponse<ContractInfo[] | null>;
return unwrapWasmResponse(responseData) || [];
}
// throws error if no contract at this address
public async getContractInfo(address: string): Promise<ContractInfo> {
/**
* Returns null when contract was not found at this address.
*/
public async getContractInfo(address: string): Promise<ContractDetails | null> {
const path = `/wasm/contract/${address}`;
const responseData = await this.get(path);
// 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 found at address "${address}"`);
try {
const response = (await this.get(path)) as WasmResponse<ContractDetails>;
return unwrapWasmResponse(response);
} catch (error) {
if (error instanceof Error) {
if (error.message.startsWith("unknown address:")) {
return null;
} else {
throw error;
}
} else {
throw error;
}
}
return info;
}
// Returns all contract state.

View File

@ -76,7 +76,8 @@ describe("SigningCosmWasmClient", () => {
verifier: faucet.address,
beneficiary: beneficiaryAddress,
},
"Let's see",
"My cool label",
"Let's see if the memo is used",
transferAmount,
);
@ -91,14 +92,22 @@ describe("SigningCosmWasmClient", () => {
const client = new SigningCosmWasmClient(httpUrl, faucet.address, signBytes => pen.sign(signBytes));
const { codeId } = await client.upload(getRandomizedHackatom());
const contractAddress1 = await client.instantiate(codeId, {
verifier: faucet.address,
beneficiary: makeRandomAddress(),
});
const contractAddress2 = await client.instantiate(codeId, {
verifier: faucet.address,
beneficiary: makeRandomAddress(),
});
const contractAddress1 = await client.instantiate(
codeId,
{
verifier: faucet.address,
beneficiary: makeRandomAddress(),
},
"contract 1",
);
const contractAddress2 = await client.instantiate(
codeId,
{
verifier: faucet.address,
beneficiary: makeRandomAddress(),
},
"contract 2",
);
expect(contractAddress1).not.toEqual(contractAddress2);
});
});
@ -128,14 +137,20 @@ describe("SigningCosmWasmClient", () => {
verifier: faucet.address,
beneficiary: beneficiaryAddress,
},
"amazing random contract",
undefined,
transferAmount,
);
// execute
const result = await client.execute(contractAddress, {}, undefined);
const [firstLog] = result.logs;
expect(firstLog.log).toEqual(`released funds to ${beneficiaryAddress}`);
const result = await client.execute(contractAddress, { release: {} }, undefined);
const wasmEvent = result.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 rest = new RestClient(httpUrl);

View File

@ -134,6 +134,7 @@ export class SigningCosmWasmClient extends CosmWasmClient {
public async instantiate(
codeId: number,
initMsg: object,
label: string,
memo = "",
transferAmount?: readonly Coin[],
): Promise<string> {
@ -143,6 +144,7 @@ export class SigningCosmWasmClient extends CosmWasmClient {
sender: this.senderAddress,
// eslint-disable-next-line @typescript-eslint/camelcase
code_id: codeId.toString(),
label: label,
// eslint-disable-next-line @typescript-eslint/camelcase
init_msg: initMsg,
// eslint-disable-next-line @typescript-eslint/camelcase

File diff suppressed because one or more lines are too long

View File

@ -27,17 +27,18 @@ export function leb128Encode(uint: number): Uint8Array {
export function getRandomizedHackatom(): Uint8Array {
const data = Encoding.fromBase64(hackatom.data);
// The return value of the export function cosmwasm_api_0_6 is unused and
// can be randomized for testing.
//
// Find position of mutable bytes as follows:
// $ wasm-objdump -d contract.wasm | grep -F "cosmwasm_api_0_6" -A 1
// 00e67c func[149] <cosmwasm_api_0_6>:
// 00e67d: 41 83 0c | i32.const 1539
// 0136d2 func[198] <cosmwasm_api_0_6>:
// 0136d3: 41 83 0c | i32.const 1539
//
// In the last line, the addresses 00e67d-00e67f hold a one byte instruction
// (https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#constants-described-here)
// and a two byte value (leb128 encoded 1539)
// In the last line, the addresses [0136d3, 0136d3+1, 0136d3+2] hold a one byte instruction
// and a two byte value (leb128 encoded 1539). See also
// https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md#constants-described-here.
// Any unsigned integer from 128 to 16383 is encoded to two leb128 bytes
const min = 128;
@ -45,8 +46,8 @@ export function getRandomizedHackatom(): Uint8Array {
const random = Math.floor(Math.random() * (max - min)) + min;
const bytes = leb128Encode(random);
data[0x00e67d + 1] = bytes[0];
data[0x00e67d + 2] = bytes[1];
data[0x0136d3 + 1] = bytes[0];
data[0x0136d3 + 2] = bytes[1];
return data;
}
@ -59,6 +60,9 @@ export const tendermintIdMatcher = /^[0-9A-F]{64}$/;
export const tendermintOptionalIdMatcher = /^([0-9A-F]{64}|)$/;
export const tendermintAddressMatcher = /^[0-9A-F]{40}$/;
// https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32
export const bech32AddressMatcher = /^[\x21-\x7e]{1,83}1[02-9ac-hj-np-z]{38}$/;
export function wasmdEnabled(): boolean {
return !!process.env.WASMD_ENABLED;
}

View File

@ -70,6 +70,8 @@ export interface MsgInstantiateContract extends MsgTemplate {
readonly sender: string;
/** ID of the Wasm code that was uploaded before */
readonly code_id: string;
/** Human-readable label for this contract */
readonly label: string;
/** Init message as JavaScript object */
readonly init_msg: object;
readonly init_funds: ReadonlyArray<Coin>;

View File

@ -34,14 +34,18 @@ export interface Code {
readonly source?: string;
readonly builder?: string;
}
export interface CodeDetails {
export interface CodeDetails extends Code {
/** The original wasm bytes */
readonly wasm: Uint8Array;
readonly data: Uint8Array;
}
export interface Contract {
readonly address: string;
readonly codeId: number;
/** Bech32 account address */
readonly creator: string;
readonly label: string;
}
export interface ContractDetails extends Contract {
/** Argument passed on initialization of the contract */
readonly initMsg: object;
}
@ -73,7 +77,10 @@ export declare class CosmWasmClient {
getCodes(): Promise<readonly Code[]>;
getCodeDetails(codeId: number): Promise<CodeDetails>;
getContracts(codeId: number): Promise<readonly Contract[]>;
getContract(address: string): Promise<Contract>;
/**
* Throws an error if no contract was found at the address
*/
getContract(address: string): Promise<ContractDetails>;
/**
* Returns the data at the key if present (raw contract dependent storage data)
* or null if no data at this key.

View File

@ -1,9 +1,11 @@
export declare type SupportedEventType = "message" | "transfer" | "wasm";
export declare function isSupportedEventType(data: any): data is SupportedEventType;
export interface Attribute {
readonly key: string;
readonly value: string;
}
export interface Event {
readonly type: "message" | "transfer";
readonly type: SupportedEventType;
readonly attributes: readonly Attribute[];
}
export interface Log {

View File

@ -102,9 +102,13 @@ export interface CodeInfo {
readonly builder?: string;
}
export interface ContractInfo {
readonly address: string;
readonly code_id: number;
/** Bech32 account address */
readonly creator: string;
readonly label: string;
}
export interface ContractDetails extends ContractInfo {
/** Argument passed on initialization of the contract */
readonly init_msg: object;
}
@ -120,6 +124,9 @@ declare type RestClientResponse =
| PostTxsResponse
| EncodeTxResponse
| WasmResponse<string>
| WasmResponse<CodeInfo[]>
| WasmResponse<ContractInfo[] | null>
| WasmResponse<ContractDetails>
| WasmResponse<GetCodeResult>;
/**
* The mode used to send transaction
@ -158,9 +165,11 @@ export declare class RestClient {
postTx(tx: StdTx): Promise<PostTxsResponse>;
listCodeInfo(): Promise<readonly CodeInfo[]>;
getCode(id: number): Promise<Uint8Array>;
listContractAddresses(): Promise<readonly string[]>;
listContractsByCodeId(id: number): Promise<readonly ContractInfo[]>;
getContractInfo(address: string): Promise<ContractInfo>;
/**
* Returns null when contract was not found at this address.
*/
getContractInfo(address: string): Promise<ContractDetails | null>;
getAllContractState(address: string): Promise<readonly Model[]>;
queryContractRaw(address: string, key: Uint8Array): Promise<Uint8Array | null>;
queryContractSmart(address: string, query: object): Promise<Uint8Array>;

View File

@ -44,6 +44,7 @@ export declare class SigningCosmWasmClient extends CosmWasmClient {
instantiate(
codeId: number,
initMsg: object,
label: string,
memo?: string,
transferAmount?: readonly Coin[],
): Promise<string>;

View File

@ -55,6 +55,8 @@ export interface MsgInstantiateContract extends MsgTemplate {
readonly sender: string;
/** ID of the Wasm code that was uploaded before */
readonly code_id: string;
/** Human-readable label for this contract */
readonly label: string;
/** Init message as JavaScript object */
readonly init_msg: object;
readonly init_funds: ReadonlyArray<Coin>;

View File

@ -1,2 +1,2 @@
b26861a6aa9858585ed905a590272735bd4fe8177c708940236224e8c9ff73ca cw-erc20.wasm
66128c5c0cd38e6b908b03d41eba8c9e9befb79b34683e334d7a8bbe74d5df41 cw-nameservice.wasm
aff8c8873d79d2153a8b9066a0683fec3c903669267eb806ffa831dcd4b3daae cw-erc20.wasm
8427fcbf9e6c15d0e0dea0afe75640d997a6854d03c3aa1c8bdf1a9787a35681 cw-nameservice.wasm

View File

@ -77,7 +77,7 @@ async function main() {
for (const initMsg of [initMsgHash, initMsgIsa, initMsgJade]) {
const memo = `Create an ERC20 instance for ${initMsg.symbol}`;
const contractAddress = await client.instantiate(uploadReceipt.codeId, initMsg, memo);
const contractAddress = await client.instantiate(uploadReceipt.codeId, initMsg, initMsg.symbol, memo);
console.info(`Contract instantiated for ${initMsg.symbol} at ${contractAddress}`);
}
}

View File

@ -36,7 +36,7 @@ async function main() {
for (const initMsg of [initFree, initLuxury]) {
const memo = `Create an nameservice instance for ${initMsg.name}`;
const contractAddress = await client.instantiate(uploadReceipt.codeId, initMsg, memo);
const contractAddress = await client.instantiate(uploadReceipt.codeId, initMsg, initMsg.name, memo);
console.info(`Contract instantiated for ${initMsg.name} at ${contractAddress}`);
}
}

View File

@ -1,5 +1,5 @@
# Choose from https://hub.docker.com/r/cosmwasm/wasmd-demo/tags
REPOSITORY="cosmwasm/wasmd-demo"
VERSION="v0.6.1"
VERSION="v0.7.0-rc1"
CONTAINER_NAME="wasmd"