Upgrade CosmWasm to 0.9 (#245)

* Upgrade chain to v0.9.0-alpha4

* Update expected build_tags

* Update wasmd init contracts

* Update hackatom test contract

* Update CosmWasm message descriptions

* Make some test code more compact

* Pull out InstantiateOptions

* Add admin field to ContractInfo

* Allow instantiating with admin

* Remove some noise

* Add SigningCosmWasmClient.updateAdmin

* Create return type ChangeAdminResult

* Add SigningCosmWasmClient.clearAdmin

* Add SigningCosmWasmClient.migrate

* Move message type testers close to type

* Export MsgUpdateAdmin/isMsgUpdateAdmin

* Fix typo in privillage

* Update some test code

* Test hackatom result data

* Add compatibility table

* Update wasmd to v0.9.0-beta

* Upgrade test contracts
This commit is contained in:
Simon Warta 2020-06-28 08:28:43 +02:00 committed by GitHub
parent 9f7b126044
commit 77980b60f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 547 additions and 102 deletions

View File

@ -4,6 +4,13 @@
An SDK to build CosmWasm clients. An SDK to build CosmWasm clients.
## Compatibility
| CosmJS | CosmWasm | x/wasm |
| ------ | -------- | ------ |
| 0.21 | 0.9 | 0.9 |
| 0.20 | 0.8 | 0.8 |
## License ## License
This package is part of the cosmjs repository, licensed under the Apache License This package is part of the cosmjs repository, licensed under the Apache License

View File

@ -23,17 +23,25 @@ export {
export { export {
ExecuteResult, ExecuteResult,
FeeTable, FeeTable,
InstantiateOptions,
InstantiateResult, InstantiateResult,
MigrateResult,
SigningCallback, SigningCallback,
SigningCosmWasmClient, SigningCosmWasmClient,
UploadMeta, UploadMeta,
UploadResult, UploadResult,
} from "./signingcosmwasmclient"; } from "./signingcosmwasmclient";
export { export {
isMsgClearAdmin,
isMsgExecuteContract, isMsgExecuteContract,
isMsgInstantiateContract, isMsgInstantiateContract,
isMsgMigrateContract,
isMsgUpdateAdmin,
isMsgStoreCode, isMsgStoreCode,
MsgStoreCode, MsgClearAdmin,
MsgExecuteContract, MsgExecuteContract,
MsgInstantiateContract, MsgInstantiateContract,
MsgMigrateContract,
MsgUpdateAdmin,
MsgStoreCode,
} from "./msgs"; } from "./msgs";

View File

@ -1,9 +1,10 @@
import { Coin, Msg } from "@cosmjs/sdk38"; import { Coin, Msg } from "@cosmjs/sdk38";
/** /**
* Uploads Wam code to the chain * Uploads Wasm code to the chain.
* A numeric, auto-incrementing code ID will be generated as a result of the execution of this message.
* *
* @see https://github.com/cosmwasm/wasmd/blob/9842678d89/x/wasm/internal/types/msg.go#L17 * @see https://github.com/CosmWasm/wasmd/blob/v0.9.0-alpha4/x/wasm/internal/types/msg.go#L34
*/ */
export interface MsgStoreCode extends Msg { export interface MsgStoreCode extends Msg {
readonly type: "wasm/store-code"; readonly type: "wasm/store-code";
@ -19,10 +20,15 @@ export interface MsgStoreCode extends Msg {
}; };
} }
export function isMsgStoreCode(msg: Msg): msg is MsgStoreCode {
return (msg as MsgStoreCode).type === "wasm/store-code";
}
/** /**
* Creates an instance of contract that was uploaded before. * Creates an instance of contract that was uploaded before.
* This will trigger a call to the "init" export.
* *
* @see https://github.com/cosmwasm/wasmd/blob/9842678d89/x/wasm/internal/types/msg.go#L73 * @see https://github.com/CosmWasm/wasmd/blob/v0.9.0-alpha4/x/wasm/internal/types/msg.go#L104
*/ */
export interface MsgInstantiateContract extends Msg { export interface MsgInstantiateContract extends Msg {
readonly type: "wasm/instantiate"; readonly type: "wasm/instantiate";
@ -36,13 +42,60 @@ export interface MsgInstantiateContract extends Msg {
/** Init message as JavaScript object */ /** Init message as JavaScript object */
readonly init_msg: any; readonly init_msg: any;
readonly init_funds: ReadonlyArray<Coin>; readonly init_funds: ReadonlyArray<Coin>;
/** Bech32-encoded admin address */
readonly admin?: string;
}; };
} }
export function isMsgInstantiateContract(msg: Msg): msg is MsgInstantiateContract {
return (msg as MsgInstantiateContract).type === "wasm/instantiate";
}
/** /**
* Creates an instance of contract that was uploaded before. * Update the admin of a contract
* *
* @see https://github.com/cosmwasm/wasmd/blob/9842678d89/x/wasm/internal/types/msg.go#L103 * @see https://github.com/CosmWasm/wasmd/blob/v0.9.0-beta/x/wasm/internal/types/msg.go#L231
*/
export interface MsgUpdateAdmin extends Msg {
readonly type: "wasm/update-contract-admin";
readonly value: {
/** Bech32-encoded sender address. This must be the old admin. */
readonly sender: string;
/** Bech32-encoded contract address to be updated */
readonly contract: string;
/** Bech32-encoded address of the new admin */
readonly new_admin: string;
};
}
export function isMsgUpdateAdmin(msg: Msg): msg is MsgUpdateAdmin {
return (msg as MsgUpdateAdmin).type === "wasm/update-contract-admin";
}
/**
* Clears the admin of a contract, making it immutable.
*
* @see https://github.com/CosmWasm/wasmd/blob/v0.9.0-beta/x/wasm/internal/types/msg.go#L269
*/
export interface MsgClearAdmin extends Msg {
readonly type: "wasm/clear-contract-admin";
readonly value: {
/** Bech32-encoded sender address. This must be the old admin. */
readonly sender: string;
/** Bech32-encoded contract address to be updated */
readonly contract: string;
};
}
export function isMsgClearAdmin(msg: Msg): msg is MsgClearAdmin {
return (msg as MsgClearAdmin).type === "wasm/clear-contract-admin";
}
/**
* Execute a smart contract.
* This will trigger a call to the "handle" export.
*
* @see https://github.com/CosmWasm/wasmd/blob/v0.9.0-alpha4/x/wasm/internal/types/msg.go#L158
*/ */
export interface MsgExecuteContract extends Msg { export interface MsgExecuteContract extends Msg {
readonly type: "wasm/execute"; readonly type: "wasm/execute";
@ -57,14 +110,29 @@ export interface MsgExecuteContract extends Msg {
}; };
} }
export function isMsgStoreCode(msg: Msg): msg is MsgStoreCode {
return (msg as MsgStoreCode).type === "wasm/store-code";
}
export function isMsgInstantiateContract(msg: Msg): msg is MsgInstantiateContract {
return (msg as MsgInstantiateContract).type === "wasm/instantiate";
}
export function isMsgExecuteContract(msg: Msg): msg is MsgExecuteContract { export function isMsgExecuteContract(msg: Msg): msg is MsgExecuteContract {
return (msg as MsgExecuteContract).type === "wasm/execute"; return (msg as MsgExecuteContract).type === "wasm/execute";
} }
/**
* Migrates a contract to a new Wasm code.
*
* @see https://github.com/CosmWasm/wasmd/blob/v0.9.0-alpha4/x/wasm/internal/types/msg.go#L195
*/
export interface MsgMigrateContract extends Msg {
readonly type: "wasm/migrate";
readonly value: {
/** Bech32 account address */
readonly sender: string;
/** Bech32 account address */
readonly contract: string;
/** The new code */
readonly code_id: string;
/** Migrate message as JavaScript object */
readonly msg: any;
};
}
export function isMsgMigrateContract(msg: Msg): msg is MsgMigrateContract {
return (msg as MsgMigrateContract).type === "wasm/migrate";
}

View File

@ -3,6 +3,8 @@ import { Sha256 } from "@cosmjs/crypto";
import { Bech32, fromAscii, fromBase64, fromHex, toAscii, toBase64, toHex } from "@cosmjs/encoding"; import { Bech32, fromAscii, fromBase64, fromHex, toAscii, toBase64, toHex } from "@cosmjs/encoding";
import { import {
Coin, Coin,
coin,
coins,
makeSignBytes, makeSignBytes,
Msg, Msg,
Pen, Pen,
@ -119,6 +121,7 @@ async function executeContract(
client: RestClient, client: RestClient,
pen: Pen, pen: Pen,
contractAddress: string, contractAddress: string,
msg: object,
): Promise<PostTxsResponse> { ): Promise<PostTxsResponse> {
const memo = "Time for action"; const memo = "Time for action";
const theMsg: MsgExecuteContract = { const theMsg: MsgExecuteContract = {
@ -126,17 +129,12 @@ async function executeContract(
value: { value: {
sender: alice.address0, sender: alice.address0,
contract: contractAddress, contract: contractAddress,
msg: { release: {} }, msg: msg,
sent_funds: [], sent_funds: [],
}, },
}; };
const fee: StdFee = { const fee: StdFee = {
amount: [ amount: coins(5000000, "ucosm"),
{
amount: "5000000",
denom: "ucosm",
},
],
gas: "89000000", gas: "89000000",
}; };
@ -261,16 +259,7 @@ describe("RestClient", () => {
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic); const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new RestClient(wasmd.endpoint); const client = new RestClient(wasmd.endpoint);
const transferAmount: readonly Coin[] = [ const transferAmount = [coin(1234, "ucosm"), coin(321, "ustake")];
{
amount: "1234",
denom: "ucosm",
},
{
amount: "321",
denom: "ustake",
},
];
const beneficiaryAddress = makeRandomAddress(); const beneficiaryAddress = makeRandomAddress();
let codeId: number; let codeId: number;
@ -308,7 +297,8 @@ describe("RestClient", () => {
// execute // execute
{ {
const result = await executeContract(client, pen, contractAddress); const result = await executeContract(client, pen, contractAddress, { release: {} });
expect(result.data).toEqual("F00BAA");
expect(result.code).toBeFalsy(); expect(result.code).toBeFalsy();
// console.log("Raw log:", result.logs); // console.log("Raw log:", result.logs);
const logs = parseLogs(result.logs); const logs = parseLogs(result.logs);
@ -424,9 +414,16 @@ describe("RestClient", () => {
// check out info // check out info
const myInfo = await client.getContractInfo(myAddress); const myInfo = await client.getContractInfo(myAddress);
assert(myInfo); assert(myInfo);
expect(myInfo.code_id).toEqual(codeId); expect(myInfo).toEqual(
expect(myInfo.creator).toEqual(alice.address0); jasmine.objectContaining({
expect((myInfo.init_msg as any).beneficiary).toEqual(beneficiaryAddress); 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 // make sure random addresses don't give useful info
const nonExistentAddress = makeRandomAddress(); const nonExistentAddress = makeRandomAddress();

View File

@ -39,6 +39,8 @@ export interface ContractInfo {
readonly code_id: number; readonly code_id: number;
/** Bech32 account address */ /** Bech32 account address */
readonly creator: string; readonly creator: string;
/** Bech32-encoded admin address */
readonly admin?: string;
readonly label: string; readonly label: string;
} }

View File

@ -1,12 +1,12 @@
import { Sha256 } from "@cosmjs/crypto"; import { Sha256 } from "@cosmjs/crypto";
import { toHex } from "@cosmjs/encoding"; import { toHex } from "@cosmjs/encoding";
import { Coin, Secp256k1Pen } from "@cosmjs/sdk38"; import { coin, coins, Secp256k1Pen } from "@cosmjs/sdk38";
import { assert } from "@cosmjs/utils"; import { assert } from "@cosmjs/utils";
import { isPostTxFailure, PrivateCosmWasmClient } from "./cosmwasmclient"; import { isPostTxFailure, PrivateCosmWasmClient } from "./cosmwasmclient";
import { RestClient } from "./restclient"; import { RestClient } from "./restclient";
import { SigningCosmWasmClient, UploadMeta } from "./signingcosmwasmclient"; import { SigningCosmWasmClient, UploadMeta } from "./signingcosmwasmclient";
import { alice, getHackatom, makeRandomAddress, pendingWithoutWasmd } from "./testutils.spec"; import { alice, getHackatom, makeRandomAddress, pendingWithoutWasmd, unused } from "./testutils.spec";
const httpUrl = "http://localhost:1317"; const httpUrl = "http://localhost:1317";
@ -82,16 +82,7 @@ describe("SigningCosmWasmClient", () => {
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes)); const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const { codeId } = await client.upload(getHackatom().data); const { codeId } = await client.upload(getHackatom().data);
const transferAmount: readonly Coin[] = [ const transferAmount = [coin(1234, "ucosm"), coin(321, "ustake")];
{
amount: "1234",
denom: "ucosm",
},
{
amount: "321",
denom: "ustake",
},
];
const beneficiaryAddress = makeRandomAddress(); const beneficiaryAddress = makeRandomAddress();
const { contractAddress } = await client.instantiate( const { contractAddress } = await client.instantiate(
codeId, codeId,
@ -100,8 +91,10 @@ describe("SigningCosmWasmClient", () => {
beneficiary: beneficiaryAddress, beneficiary: beneficiaryAddress,
}, },
"My cool label", "My cool label",
"Let's see if the memo is used", {
transferAmount, memo: "Let's see if the memo is used",
transferAmount,
},
); );
const rest = new RestClient(httpUrl); const rest = new RestClient(httpUrl);
@ -109,6 +102,29 @@ describe("SigningCosmWasmClient", () => {
expect(balance).toEqual(transferAmount); expect(balance).toEqual(transferAmount);
}); });
it("works with admin", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const { codeId } = await client.upload(getHackatom().data);
const beneficiaryAddress = makeRandomAddress();
const { contractAddress } = await client.instantiate(
codeId,
{
verifier: alice.address0,
beneficiary: beneficiaryAddress,
},
"My cool label",
{ admin: unused.address },
);
const rest = new RestClient(httpUrl);
const contract = await rest.getContractInfo(contractAddress);
assert(contract);
expect(contract.admin).toEqual(unused.address);
});
it("can instantiate one code multiple times", async () => { it("can instantiate one code multiple times", async () => {
pendingWithoutWasmd(); pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic); const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
@ -135,6 +151,111 @@ describe("SigningCosmWasmClient", () => {
}); });
}); });
describe("updateAdmin", () => {
it("can update an admin", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const { codeId } = await client.upload(getHackatom().data);
const beneficiaryAddress = makeRandomAddress();
const { contractAddress } = await client.instantiate(
codeId,
{
verifier: alice.address0,
beneficiary: beneficiaryAddress,
},
"My cool label",
{
admin: alice.address0,
},
);
const rest = new RestClient(httpUrl);
const state1 = await rest.getContractInfo(contractAddress);
assert(state1);
expect(state1.admin).toEqual(alice.address0);
await client.updateAdmin(contractAddress, unused.address);
const state2 = await rest.getContractInfo(contractAddress);
assert(state2);
expect(state2.admin).toEqual(unused.address);
});
});
describe("clearAdmin", () => {
it("can clear an admin", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const { codeId } = await client.upload(getHackatom().data);
const beneficiaryAddress = makeRandomAddress();
const { contractAddress } = await client.instantiate(
codeId,
{
verifier: alice.address0,
beneficiary: beneficiaryAddress,
},
"My cool label",
{
admin: alice.address0,
},
);
const rest = new RestClient(httpUrl);
const state1 = await rest.getContractInfo(contractAddress);
assert(state1);
expect(state1.admin).toEqual(alice.address0);
await client.clearAdmin(contractAddress);
const state2 = await rest.getContractInfo(contractAddress);
assert(state2);
expect(state2.admin).toBeUndefined();
});
});
describe("migrate", () => {
it("can can migrate from one code ID to another", async () => {
pendingWithoutWasmd();
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
const { codeId: codeId1 } = await client.upload(getHackatom().data);
const { codeId: codeId2 } = await client.upload(getHackatom().data);
const beneficiaryAddress = makeRandomAddress();
const { contractAddress } = await client.instantiate(
codeId1,
{
verifier: alice.address0,
beneficiary: beneficiaryAddress,
},
"My cool label",
{
admin: alice.address0,
},
);
const rest = new RestClient(httpUrl);
const state1 = await rest.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);
assert(state2);
expect(state2).toEqual({
...state1,
// eslint-disable-next-line @typescript-eslint/camelcase
code_id: codeId2,
});
});
});
describe("execute", () => { describe("execute", () => {
it("works", async () => { it("works", async () => {
pendingWithoutWasmd(); pendingWithoutWasmd();
@ -143,16 +264,7 @@ describe("SigningCosmWasmClient", () => {
const { codeId } = await client.upload(getHackatom().data); const { codeId } = await client.upload(getHackatom().data);
// instantiate // instantiate
const transferAmount: readonly Coin[] = [ const transferAmount = [coin(233444, "ucosm"), coin(5454, "ustake")];
{
amount: "233444",
denom: "ucosm",
},
{
amount: "5454",
denom: "ustake",
},
];
const beneficiaryAddress = makeRandomAddress(); const beneficiaryAddress = makeRandomAddress();
const { contractAddress } = await client.instantiate( const { contractAddress } = await client.instantiate(
codeId, codeId,
@ -161,8 +273,9 @@ describe("SigningCosmWasmClient", () => {
beneficiary: beneficiaryAddress, beneficiary: beneficiaryAddress,
}, },
"amazing random contract", "amazing random contract",
undefined, {
transferAmount, transferAmount,
},
); );
// execute // execute
@ -191,12 +304,7 @@ describe("SigningCosmWasmClient", () => {
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes)); const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes));
// instantiate // instantiate
const transferAmount: readonly Coin[] = [ const transferAmount = coins(7890, "ucosm");
{
amount: "7890",
denom: "ucosm",
},
];
const beneficiaryAddress = makeRandomAddress(); const beneficiaryAddress = makeRandomAddress();
// no tokens here // no tokens here

View File

@ -23,7 +23,14 @@ import {
PostTxResult, PostTxResult,
} from "./cosmwasmclient"; } from "./cosmwasmclient";
import { findAttribute, Log } from "./logs"; import { findAttribute, Log } from "./logs";
import { MsgExecuteContract, MsgInstantiateContract, MsgStoreCode } from "./msgs"; import {
MsgClearAdmin,
MsgExecuteContract,
MsgInstantiateContract,
MsgMigrateContract,
MsgStoreCode,
MsgUpdateAdmin,
} from "./msgs";
export interface SigningCallback { export interface SigningCallback {
(signBytes: Uint8Array): Promise<StdSignature>; (signBytes: Uint8Array): Promise<StdSignature>;
@ -33,7 +40,10 @@ export interface FeeTable {
readonly upload: StdFee; readonly upload: StdFee;
readonly init: StdFee; readonly init: StdFee;
readonly exec: StdFee; readonly exec: StdFee;
readonly migrate: StdFee;
readonly send: StdFee; readonly send: StdFee;
/** Paid when setting the contract admin to a new address or unsetting it */
readonly changeAdmin: StdFee;
} }
function prepareBuilder(buider: string | undefined): string { function prepareBuilder(buider: string | undefined): string {
@ -54,6 +64,10 @@ const defaultFees: FeeTable = {
amount: coins(12500, "ucosm"), amount: coins(12500, "ucosm"),
gas: "500000", // 500k gas: "500000", // 500k
}, },
migrate: {
amount: coins(5000, "ucosm"),
gas: "200000", // 200k
},
exec: { exec: {
amount: coins(5000, "ucosm"), amount: coins(5000, "ucosm"),
gas: "200000", // 200k gas: "200000", // 200k
@ -62,6 +76,10 @@ const defaultFees: FeeTable = {
amount: coins(2000, "ucosm"), amount: coins(2000, "ucosm"),
gas: "80000", // 80k gas: "80000", // 80k
}, },
changeAdmin: {
amount: coins(2000, "ucosm"),
gas: "80000", // 80k
},
}; };
export interface UploadMeta { export interface UploadMeta {
@ -87,6 +105,20 @@ export interface UploadResult {
readonly transactionHash: string; readonly transactionHash: string;
} }
/**
* The options of an .instantiate() call.
* All properties are optional.
*/
export interface InstantiateOptions {
readonly memo?: string;
readonly transferAmount?: readonly Coin[];
/**
* A bech32 encoded address of an admin account.
* Caution: an admin has the privilege to upgrade a contract. If this is not desired, do not set this value.
*/
readonly admin?: string;
}
export interface InstantiateResult { export interface InstantiateResult {
/** The address of the newly instantiated contract */ /** The address of the newly instantiated contract */
readonly contractAddress: string; readonly contractAddress: string;
@ -95,6 +127,21 @@ export interface InstantiateResult {
readonly transactionHash: string; readonly transactionHash: string;
} }
/**
* Result type of updateAdmin and clearAdmin
*/
export interface ChangeAdminResult {
readonly logs: readonly Log[];
/** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */
readonly transactionHash: string;
}
export interface MigrateResult {
readonly logs: readonly Log[];
/** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */
readonly transactionHash: string;
}
export interface ExecuteResult { export interface ExecuteResult {
readonly logs: readonly Log[]; readonly logs: readonly Log[];
/** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */
@ -194,8 +241,7 @@ export class SigningCosmWasmClient extends CosmWasmClient {
codeId: number, codeId: number,
initMsg: object, initMsg: object,
label: string, label: string,
memo = "", options: InstantiateOptions = {},
transferAmount?: readonly Coin[],
): Promise<InstantiateResult> { ): Promise<InstantiateResult> {
const instantiateMsg: MsgInstantiateContract = { const instantiateMsg: MsgInstantiateContract = {
type: "wasm/instantiate", type: "wasm/instantiate",
@ -207,9 +253,11 @@ export class SigningCosmWasmClient extends CosmWasmClient {
// eslint-disable-next-line @typescript-eslint/camelcase // eslint-disable-next-line @typescript-eslint/camelcase
init_msg: initMsg, init_msg: initMsg,
// eslint-disable-next-line @typescript-eslint/camelcase // eslint-disable-next-line @typescript-eslint/camelcase
init_funds: transferAmount || [], init_funds: options.transferAmount || [],
admin: options.admin,
}, },
}; };
const memo = options.memo || "";
const fee = this.fees.init; const fee = this.fees.init;
const { accountNumber, sequence } = await this.getNonce(); const { accountNumber, sequence } = await this.getNonce();
const chainId = await this.getChainId(); const chainId = await this.getChainId();
@ -235,6 +283,106 @@ export class SigningCosmWasmClient extends CosmWasmClient {
}; };
} }
public async updateAdmin(contractAddress: string, newAdmin: string, memo = ""): Promise<ChangeAdminResult> {
const updateAdminMsg: MsgUpdateAdmin = {
type: "wasm/update-contract-admin",
value: {
sender: this.senderAddress,
contract: contractAddress,
// eslint-disable-next-line @typescript-eslint/camelcase
new_admin: newAdmin,
},
};
const fee = this.fees.changeAdmin;
const { accountNumber, sequence } = await this.getNonce();
const chainId = await this.getChainId();
const signBytes = makeSignBytes([updateAdminMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await this.signCallback(signBytes);
const signedTx: StdTx = {
msg: [updateAdminMsg],
fee: fee,
memo: memo,
signatures: [signature],
};
const result = await this.postTx(signedTx);
if (isPostTxFailure(result)) {
throw new Error(createPostTxErrorMessage(result));
}
return {
logs: result.logs,
transactionHash: result.transactionHash,
};
}
public async clearAdmin(contractAddress: string, memo = ""): Promise<ChangeAdminResult> {
const clearAdminMsg: MsgClearAdmin = {
type: "wasm/clear-contract-admin",
value: {
sender: this.senderAddress,
contract: contractAddress,
},
};
const fee = this.fees.changeAdmin;
const { accountNumber, sequence } = await this.getNonce();
const chainId = await this.getChainId();
const signBytes = makeSignBytes([clearAdminMsg], fee, chainId, memo, accountNumber, sequence);
const signature = await this.signCallback(signBytes);
const signedTx: StdTx = {
msg: [clearAdminMsg],
fee: fee,
memo: memo,
signatures: [signature],
};
const result = await this.postTx(signedTx);
if (isPostTxFailure(result)) {
throw new Error(createPostTxErrorMessage(result));
}
return {
logs: result.logs,
transactionHash: result.transactionHash,
};
}
public async migrate(
contractAddress: string,
codeId: number,
migrateMsg: object,
memo = "",
): Promise<MigrateResult> {
const msg: MsgMigrateContract = {
type: "wasm/migrate",
value: {
sender: this.senderAddress,
contract: contractAddress,
// eslint-disable-next-line @typescript-eslint/camelcase
code_id: new Uint53(codeId).toString(),
msg: migrateMsg,
},
};
const fee = this.fees.migrate;
const { accountNumber, sequence } = await this.getNonce();
const chainId = await this.getChainId();
const signBytes = makeSignBytes([msg], fee, chainId, memo, accountNumber, sequence);
const signature = await this.signCallback(signBytes);
const signedTx: StdTx = {
msg: [msg],
fee: fee,
memo: memo,
signatures: [signature],
};
const result = await this.postTx(signedTx);
if (isPostTxFailure(result)) {
throw new Error(createPostTxErrorMessage(result));
}
return {
logs: result.logs,
transactionHash: result.transactionHash,
};
}
public async execute( public async execute(
contractAddress: string, contractAddress: string,
handleMsg: object, handleMsg: object,

File diff suppressed because one or more lines are too long

View File

@ -31,9 +31,9 @@ export const bech32AddressMatcher = /^[\x21-\x7e]{1,83}1[02-9ac-hj-np-z]{38}$/;
/** Deployed as part of scripts/wasmd/init.sh */ /** Deployed as part of scripts/wasmd/init.sh */
export const deployedErc20 = { export const deployedErc20 = {
codeId: 1, codeId: 1,
source: "https://crates.io/api/v1/crates/cw-erc20/0.4.0/download", source: "https://crates.io/api/v1/crates/cw-erc20/0.5.1/download",
builder: "cosmwasm/rust-optimizer:0.8.0", builder: "cosmwasm/rust-optimizer:0.8.0",
checksum: "41b3bafd7f9a3870bbfb0a0620508df564c52499cdcdc67bf9df72262f3958a6", checksum: "3e97bf88bd960fee5e5959c77b972eb2927690bc10160792741b174f105ec0c5",
instances: [ instances: [
"cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5", // HASH "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5", // HASH
"cosmos1hqrdl6wstt8qzshwc6mrumpjk9338k0lr4dqxd", // ISA "cosmos1hqrdl6wstt8qzshwc6mrumpjk9338k0lr4dqxd", // ISA

View File

@ -22,17 +22,25 @@ export {
export { export {
ExecuteResult, ExecuteResult,
FeeTable, FeeTable,
InstantiateOptions,
InstantiateResult, InstantiateResult,
MigrateResult,
SigningCallback, SigningCallback,
SigningCosmWasmClient, SigningCosmWasmClient,
UploadMeta, UploadMeta,
UploadResult, UploadResult,
} from "./signingcosmwasmclient"; } from "./signingcosmwasmclient";
export { export {
isMsgClearAdmin,
isMsgExecuteContract, isMsgExecuteContract,
isMsgInstantiateContract, isMsgInstantiateContract,
isMsgMigrateContract,
isMsgUpdateAdmin,
isMsgStoreCode, isMsgStoreCode,
MsgStoreCode, MsgClearAdmin,
MsgExecuteContract, MsgExecuteContract,
MsgInstantiateContract, MsgInstantiateContract,
MsgMigrateContract,
MsgUpdateAdmin,
MsgStoreCode,
} from "./msgs"; } from "./msgs";

View File

@ -1,8 +1,9 @@
import { Coin, Msg } from "@cosmjs/sdk38"; import { Coin, Msg } from "@cosmjs/sdk38";
/** /**
* Uploads Wam code to the chain * Uploads Wasm code to the chain.
* A numeric, auto-incrementing code ID will be generated as a result of the execution of this message.
* *
* @see https://github.com/cosmwasm/wasmd/blob/9842678d89/x/wasm/internal/types/msg.go#L17 * @see https://github.com/CosmWasm/wasmd/blob/v0.9.0-alpha4/x/wasm/internal/types/msg.go#L34
*/ */
export interface MsgStoreCode extends Msg { export interface MsgStoreCode extends Msg {
readonly type: "wasm/store-code"; readonly type: "wasm/store-code";
@ -17,10 +18,12 @@ export interface MsgStoreCode extends Msg {
readonly builder: string; readonly builder: string;
}; };
} }
export declare function isMsgStoreCode(msg: Msg): msg is MsgStoreCode;
/** /**
* Creates an instance of contract that was uploaded before. * Creates an instance of contract that was uploaded before.
* This will trigger a call to the "init" export.
* *
* @see https://github.com/cosmwasm/wasmd/blob/9842678d89/x/wasm/internal/types/msg.go#L73 * @see https://github.com/CosmWasm/wasmd/blob/v0.9.0-alpha4/x/wasm/internal/types/msg.go#L104
*/ */
export interface MsgInstantiateContract extends Msg { export interface MsgInstantiateContract extends Msg {
readonly type: "wasm/instantiate"; readonly type: "wasm/instantiate";
@ -34,12 +37,48 @@ export interface MsgInstantiateContract extends Msg {
/** Init message as JavaScript object */ /** Init message as JavaScript object */
readonly init_msg: any; readonly init_msg: any;
readonly init_funds: ReadonlyArray<Coin>; readonly init_funds: ReadonlyArray<Coin>;
/** Bech32-encoded admin address */
readonly admin?: string;
}; };
} }
export declare function isMsgInstantiateContract(msg: Msg): msg is MsgInstantiateContract;
/** /**
* Creates an instance of contract that was uploaded before. * Update the admin of a contract
* *
* @see https://github.com/cosmwasm/wasmd/blob/9842678d89/x/wasm/internal/types/msg.go#L103 * @see https://github.com/CosmWasm/wasmd/blob/v0.9.0-beta/x/wasm/internal/types/msg.go#L231
*/
export interface MsgUpdateAdmin extends Msg {
readonly type: "wasm/update-contract-admin";
readonly value: {
/** Bech32-encoded sender address. This must be the old admin. */
readonly sender: string;
/** Bech32-encoded contract address to be updated */
readonly contract: string;
/** Bech32-encoded address of the new admin */
readonly new_admin: string;
};
}
export declare function isMsgUpdateAdmin(msg: Msg): msg is MsgUpdateAdmin;
/**
* Clears the admin of a contract, making it immutable.
*
* @see https://github.com/CosmWasm/wasmd/blob/v0.9.0-beta/x/wasm/internal/types/msg.go#L269
*/
export interface MsgClearAdmin extends Msg {
readonly type: "wasm/clear-contract-admin";
readonly value: {
/** Bech32-encoded sender address. This must be the old admin. */
readonly sender: string;
/** Bech32-encoded contract address to be updated */
readonly contract: string;
};
}
export declare function isMsgClearAdmin(msg: Msg): msg is MsgClearAdmin;
/**
* Execute a smart contract.
* This will trigger a call to the "handle" export.
*
* @see https://github.com/CosmWasm/wasmd/blob/v0.9.0-alpha4/x/wasm/internal/types/msg.go#L158
*/ */
export interface MsgExecuteContract extends Msg { export interface MsgExecuteContract extends Msg {
readonly type: "wasm/execute"; readonly type: "wasm/execute";
@ -53,6 +92,23 @@ export interface MsgExecuteContract extends Msg {
readonly sent_funds: ReadonlyArray<Coin>; readonly sent_funds: ReadonlyArray<Coin>;
}; };
} }
export declare function isMsgStoreCode(msg: Msg): msg is MsgStoreCode;
export declare function isMsgInstantiateContract(msg: Msg): msg is MsgInstantiateContract;
export declare function isMsgExecuteContract(msg: Msg): msg is MsgExecuteContract; export declare function isMsgExecuteContract(msg: Msg): msg is MsgExecuteContract;
/**
* Migrates a contract to a new Wasm code.
*
* @see https://github.com/CosmWasm/wasmd/blob/v0.9.0-alpha4/x/wasm/internal/types/msg.go#L195
*/
export interface MsgMigrateContract extends Msg {
readonly type: "wasm/migrate";
readonly value: {
/** Bech32 account address */
readonly sender: string;
/** Bech32 account address */
readonly contract: string;
/** The new code */
readonly code_id: string;
/** Migrate message as JavaScript object */
readonly msg: any;
};
}
export declare function isMsgMigrateContract(msg: Msg): msg is MsgMigrateContract;

View File

@ -18,6 +18,8 @@ export interface ContractInfo {
readonly code_id: number; readonly code_id: number;
/** Bech32 account address */ /** Bech32 account address */
readonly creator: string; readonly creator: string;
/** Bech32-encoded admin address */
readonly admin?: string;
readonly label: string; readonly label: string;
} }
export interface ContractDetails extends ContractInfo { export interface ContractDetails extends ContractInfo {

View File

@ -8,7 +8,10 @@ export interface FeeTable {
readonly upload: StdFee; readonly upload: StdFee;
readonly init: StdFee; readonly init: StdFee;
readonly exec: StdFee; readonly exec: StdFee;
readonly migrate: StdFee;
readonly send: StdFee; readonly send: StdFee;
/** Paid when setting the contract admin to a new address or unsetting it */
readonly changeAdmin: StdFee;
} }
export interface UploadMeta { export interface UploadMeta {
/** The source URL */ /** The source URL */
@ -31,6 +34,19 @@ export interface UploadResult {
/** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */
readonly transactionHash: string; readonly transactionHash: string;
} }
/**
* The options of an .instantiate() call.
* All properties are optional.
*/
export interface InstantiateOptions {
readonly memo?: string;
readonly transferAmount?: readonly Coin[];
/**
* A bech32 encoded address of an admin account.
* Caution: an admin has the privilege to upgrade a contract. If this is not desired, do not set this value.
*/
readonly admin?: string;
}
export interface InstantiateResult { export interface InstantiateResult {
/** The address of the newly instantiated contract */ /** The address of the newly instantiated contract */
readonly contractAddress: string; readonly contractAddress: string;
@ -38,6 +54,19 @@ export interface InstantiateResult {
/** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */
readonly transactionHash: string; readonly transactionHash: string;
} }
/**
* Result type of updateAdmin and clearAdmin
*/
export interface ChangeAdminResult {
readonly logs: readonly Log[];
/** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */
readonly transactionHash: string;
}
export interface MigrateResult {
readonly logs: readonly Log[];
/** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */
readonly transactionHash: string;
}
export interface ExecuteResult { export interface ExecuteResult {
readonly logs: readonly Log[]; readonly logs: readonly Log[];
/** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */ /** Transaction hash (might be used as transaction ID). Guaranteed to be non-empty upper-case hex */
@ -74,9 +103,11 @@ export declare class SigningCosmWasmClient extends CosmWasmClient {
codeId: number, codeId: number,
initMsg: object, initMsg: object,
label: string, label: string,
memo?: string, options?: InstantiateOptions,
transferAmount?: readonly Coin[],
): Promise<InstantiateResult>; ): Promise<InstantiateResult>;
updateAdmin(contractAddress: string, newAdmin: string, memo?: string): Promise<ChangeAdminResult>;
clearAdmin(contractAddress: string, memo?: string): Promise<ChangeAdminResult>;
migrate(contractAddress: string, codeId: number, migrateMsg: object, memo?: string): Promise<MigrateResult>;
execute( execute(
contractAddress: string, contractAddress: string,
handleMsg: object, handleMsg: object,

View File

@ -192,7 +192,7 @@ describe("RestClient", () => {
client_name: "wasmcli", client_name: "wasmcli",
version: jasmine.stringMatching(semverMatcher), version: jasmine.stringMatching(semverMatcher),
commit: jasmine.stringMatching(tendermintShortHashMatcher), commit: jasmine.stringMatching(tendermintShortHashMatcher),
build_tags: "netgo,ledger", build_tags: "netgo,ledger,muslc",
go: jasmine.stringMatching(/^go version go1\.[0-9]+\.[0-9]+ linux\/amd64$/), go: jasmine.stringMatching(/^go version go1\.[0-9]+\.[0-9]+ linux\/amd64$/),
}); });
}); });

View File

@ -1,3 +1,3 @@
41b3bafd7f9a3870bbfb0a0620508df564c52499cdcdc67bf9df72262f3958a6 cw-erc20.wasm 3e97bf88bd960fee5e5959c77b972eb2927690bc10160792741b174f105ec0c5 cw-erc20.wasm
f4bba4289d150c0348ec42444fa142edd22ce5a024ad3314405c70314f3eb0fc cw-nameservice.wasm 851aa8bbc76bc2326a38b99e1432bb06a8ed36442a68e9e676d10ed8beedd1d1 cw-nameservice.wasm
0f08a890443dbf644f61a7dc3aa7b2a03e9d142dd1b718aa8b7f8a80b886bff1 staking.wasm 44397b14c9ec35b3188d16b5ed46de2fb6397d7bf2d1f2755a9970054aa7abb0 staking.wasm

Binary file not shown.

View File

@ -22,7 +22,7 @@ const guest = {
}; };
const codeMeta = { const codeMeta = {
source: "https://crates.io/api/v1/crates/cw-erc20/0.4.0/download", source: "https://crates.io/api/v1/crates/cw-erc20/0.5.1/download",
builder: "cosmwasm/rust-optimizer:0.8.0", builder: "cosmwasm/rust-optimizer:0.8.0",
}; };
@ -133,8 +133,9 @@ async function main() {
console.info(`Upload succeeded. Receipt: ${JSON.stringify(uploadReceipt)}`); console.info(`Upload succeeded. Receipt: ${JSON.stringify(uploadReceipt)}`);
for (const initMsg of [initMsgHash, initMsgIsa, initMsgJade]) { for (const initMsg of [initMsgHash, initMsgIsa, initMsgJade]) {
const memo = `Create an ERC20 instance for ${initMsg.symbol}`; const { contractAddress } = await client.instantiate(uploadReceipt.codeId, initMsg, initMsg.symbol, {
const { contractAddress } = await client.instantiate(uploadReceipt.codeId, initMsg, initMsg.symbol, memo); memo: `Create an ERC20 instance for ${initMsg.symbol}`,
});
console.info(`Contract instantiated for ${initMsg.symbol} at ${contractAddress}`); console.info(`Contract instantiated for ${initMsg.symbol} at ${contractAddress}`);
} }
} }

View File

@ -12,7 +12,7 @@ const alice = {
}; };
const codeMeta = { const codeMeta = {
source: "https://crates.io/api/v1/crates/cw-nameservice/0.4.0/download", source: "https://crates.io/api/v1/crates/cw-nameservice/0.5.1/download",
builder: "cosmwasm/rust-optimizer:0.8.0", builder: "cosmwasm/rust-optimizer:0.8.0",
}; };
@ -44,8 +44,9 @@ async function main() {
console.info(`Upload succeeded. Receipt: ${JSON.stringify(uploadReceipt)}`); console.info(`Upload succeeded. Receipt: ${JSON.stringify(uploadReceipt)}`);
for (const { label, initMsg } of [free, luxury]) { for (const { label, initMsg } of [free, luxury]) {
const memo = `Create an nameservice instance "${label}"`; const { contractAddress } = await client.instantiate(uploadReceipt.codeId, initMsg, label, {
const { contractAddress } = await client.instantiate(uploadReceipt.codeId, initMsg, label, memo); memo: `Create an nameservice instance "${label}"`,
});
console.info(`Contract "${label}" instantiated at ${contractAddress}`); console.info(`Contract "${label}" instantiated at ${contractAddress}`);
} }
} }

View File

@ -2,7 +2,7 @@
/* eslint-disable @typescript-eslint/camelcase */ /* eslint-disable @typescript-eslint/camelcase */
const { SigningCosmWasmClient } = require("@cosmjs/cosmwasm"); const { SigningCosmWasmClient } = require("@cosmjs/cosmwasm");
const { Secp256k1Pen } = require("@cosmjs/sdk38"); const { coins, Secp256k1Pen } = require("@cosmjs/sdk38");
const fs = require("fs"); const fs = require("fs");
const httpUrl = "http://localhost:1317"; const httpUrl = "http://localhost:1317";
@ -30,17 +30,25 @@ const bounty = {
}, },
}; };
const fees = {
upload: {
amount: coins(25000, "ucosm"),
gas: "1500000", // 1.5 million
},
};
async function main() { async function main() {
const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic); const pen = await Secp256k1Pen.fromMnemonic(alice.mnemonic);
const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes)); const client = new SigningCosmWasmClient(httpUrl, alice.address0, (signBytes) => pen.sign(signBytes), fees);
const wasm = fs.readFileSync(__dirname + "/contracts/staking.wasm"); const wasm = fs.readFileSync(__dirname + "/contracts/staking.wasm");
const uploadReceipt = await client.upload(wasm, codeMeta, "Upload Staking code"); const uploadReceipt = await client.upload(wasm, codeMeta, "Upload Staking code");
console.info(`Upload succeeded. Receipt: ${JSON.stringify(uploadReceipt)}`); console.info(`Upload succeeded. Receipt: ${JSON.stringify(uploadReceipt)}`);
for (const { label, initMsg } of [bounty]) { for (const { label, initMsg } of [bounty]) {
const memo = `Create an staking instance "${label}"`; const { contractAddress } = await client.instantiate(uploadReceipt.codeId, initMsg, label, {
const { contractAddress } = await client.instantiate(uploadReceipt.codeId, initMsg, label, memo); memo: `Create an staking instance "${label}"`,
});
console.info(`Contract "${label}" instantiated at ${contractAddress}`); console.info(`Contract "${label}" instantiated at ${contractAddress}`);
} }
} }

View File

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