mirror of
https://github.com/cosmos/cosmjs.git
synced 2025-03-10 21:49:15 +00:00
Add support for contract instantiation
This commit is contained in:
parent
f7d63b7037
commit
517b65f58d
@ -311,7 +311,7 @@ export class CosmWasmConnection implements BlockchainConnection {
|
||||
let senderAddress: string;
|
||||
if (types.isMsgSend(firstMsg)) {
|
||||
senderAddress = firstMsg.value.from_address;
|
||||
} else if (types.isMsgStoreCode(firstMsg)) {
|
||||
} else if (types.isMsgStoreCode(firstMsg) || types.isMsgInstantiateContract(firstMsg)) {
|
||||
senderAddress = firstMsg.value.sender;
|
||||
} else {
|
||||
throw new Error(`Got unsupported type of message: ${firstMsg.type}`);
|
||||
|
@ -7,6 +7,11 @@ describe("logs", () => {
|
||||
const attr = parseAttribute({ key: "a", value: "b" });
|
||||
expect(attr).toEqual({ key: "a", value: "b" });
|
||||
});
|
||||
|
||||
it("works for unset value", () => {
|
||||
const attr = parseAttribute({ key: "amount" });
|
||||
expect(attr).toEqual({ key: "amount", value: undefined });
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseEvent", () => {
|
||||
@ -40,6 +45,37 @@ describe("logs", () => {
|
||||
const event = parseEvent(original);
|
||||
expect(event).toEqual(original);
|
||||
});
|
||||
|
||||
it("works for transfer event", () => {
|
||||
const original = {
|
||||
type: "transfer",
|
||||
attributes: [
|
||||
{
|
||||
key: "recipient",
|
||||
value: "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5",
|
||||
},
|
||||
{
|
||||
key: "amount",
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
const expected = {
|
||||
type: "transfer",
|
||||
attributes: [
|
||||
{
|
||||
key: "recipient",
|
||||
value: "cosmos18vd8fpwxzck93qlwghaj6arh4p7c5n89uzcee5",
|
||||
},
|
||||
{
|
||||
key: "amount",
|
||||
value: undefined,
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
|
||||
const event = parseEvent(original);
|
||||
expect(event).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseLog", () => {
|
||||
|
@ -3,11 +3,11 @@ import { isNonNullObject } from "@iov/encoding";
|
||||
|
||||
export interface Attribute {
|
||||
readonly key: string;
|
||||
readonly value: string;
|
||||
readonly value: string | undefined;
|
||||
}
|
||||
|
||||
export interface Event {
|
||||
readonly type: "message";
|
||||
readonly type: "message" | "transfer";
|
||||
readonly attributes: readonly Attribute[];
|
||||
}
|
||||
|
||||
@ -20,9 +20,11 @@ export interface Log {
|
||||
export function parseAttribute(input: unknown): Attribute {
|
||||
if (!isNonNullObject(input)) throw new Error("Attribute must be a non-null object");
|
||||
const { key, value } = input as any;
|
||||
if (typeof key !== "string" || typeof value !== "string") {
|
||||
throw new Error("Attribute is not a key/value pair");
|
||||
if (typeof key !== "string" || !key) throw new Error("Attribute's key must be a non-empty string");
|
||||
if (typeof value !== "string" && typeof value !== "undefined") {
|
||||
throw new Error("Attribute's value must be a string or unset");
|
||||
}
|
||||
|
||||
return {
|
||||
key: key,
|
||||
value: value,
|
||||
@ -32,7 +34,7 @@ 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") throw new Error("Event must be of type message");
|
||||
if (type !== "message" && type !== "transfer") throw new Error("Event must be of type message or transfer");
|
||||
if (!Array.isArray(attributes)) throw new Error("Event's attributes must be an array");
|
||||
return {
|
||||
type: type,
|
||||
|
@ -8,7 +8,7 @@ import { Log, parseLogs } from "./logs";
|
||||
import { RestClient } from "./restclient";
|
||||
import contract from "./testdata/contract.json";
|
||||
import data from "./testdata/cosmoshub.json";
|
||||
import { MsgSend, MsgStoreCode, StdFee, StdTx } from "./types";
|
||||
import { MsgInstantiateContract, MsgSend, MsgStoreCode, StdFee, StdTx } from "./types";
|
||||
|
||||
const { fromBase64 } = Encoding;
|
||||
|
||||
@ -117,51 +117,126 @@ describe("RestClient", () => {
|
||||
expect(result.code).toBeFalsy();
|
||||
});
|
||||
|
||||
it("can upload wasm", async () => {
|
||||
it("can upload and instantiate wasm", async () => {
|
||||
pendingWithoutCosmos();
|
||||
const wallet = Secp256k1HdWallet.fromMnemonic(faucetMnemonic);
|
||||
const signer = await wallet.createIdentity("abc" as ChainId, faucetPath);
|
||||
const client = new RestClient(httpUrl);
|
||||
|
||||
const memo = "My first contract on chain";
|
||||
const theMsg: MsgStoreCode = {
|
||||
type: "wasm/store-code",
|
||||
value: {
|
||||
sender: faucetAddress,
|
||||
wasm_byte_code: contract.data,
|
||||
source: "https://github.com/confio/cosmwasm/raw/0.7/lib/vm/testdata/contract_0.6.wasm",
|
||||
builder: "cosmwasm-opt:0.6.2",
|
||||
},
|
||||
};
|
||||
const fee: StdFee = {
|
||||
amount: [
|
||||
// upload
|
||||
{
|
||||
const memo = "My first contract on chain";
|
||||
const theMsg: MsgStoreCode = {
|
||||
type: "wasm/store-code",
|
||||
value: {
|
||||
sender: faucetAddress,
|
||||
wasm_byte_code: contract.data,
|
||||
source: "https://github.com/confio/cosmwasm/raw/0.7/lib/vm/testdata/contract_0.6.wasm",
|
||||
builder: "cosmwasm-opt:0.6.2",
|
||||
},
|
||||
};
|
||||
const fee: StdFee = {
|
||||
amount: [
|
||||
{
|
||||
amount: "5000000",
|
||||
denom: "ucosm",
|
||||
},
|
||||
],
|
||||
gas: "89000000",
|
||||
};
|
||||
|
||||
const account = (await client.authAccounts(faucetAddress)).result.value;
|
||||
const signBytes = makeSignBytes([theMsg], fee, defaultNetworkId, memo, account) as SignableBytes;
|
||||
const rawSignature = await wallet.createTransactionSignature(signer, signBytes, PrehashType.Sha256);
|
||||
const signature = encodeSecp256k1Signature(signer.pubkey.data, rawSignature);
|
||||
|
||||
const signedTx: StdTx = {
|
||||
msg: [theMsg],
|
||||
fee: fee,
|
||||
memo: memo,
|
||||
signatures: [signature],
|
||||
};
|
||||
|
||||
const postableBytes = marshalTx(signedTx);
|
||||
const result = await client.postTx(postableBytes);
|
||||
// console.log("Raw log:", result.raw_log);
|
||||
expect(result.code).toBeFalsy();
|
||||
const [firstLog] = parseSuccess(result.raw_log);
|
||||
const codeIdAttr = firstLog.events[0].attributes.find(attr => attr.key === "code_id");
|
||||
expect(codeIdAttr).toEqual({ key: "code_id", value: "1" });
|
||||
}
|
||||
|
||||
let contractAddress: string;
|
||||
|
||||
// instantiate
|
||||
{
|
||||
const memo = "Create an escrow instance";
|
||||
const theMsg: MsgInstantiateContract = {
|
||||
type: "wasm/instantiate",
|
||||
value: {
|
||||
sender: faucetAddress,
|
||||
code_id: "1",
|
||||
init_msg: {
|
||||
verifier: "cosmos1ltkhnmdcqemmd2tkhnx7qx66tq7e0wykw2j85k",
|
||||
beneficiary: "cosmos1ltkhnmdcqemmd2tkhnx7qx66tq7e0wykw2j85k",
|
||||
},
|
||||
init_funds: [
|
||||
{
|
||||
amount: "1234",
|
||||
denom: "ucosm",
|
||||
},
|
||||
{
|
||||
amount: "321",
|
||||
denom: "ustake",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const fee: StdFee = {
|
||||
amount: [
|
||||
{
|
||||
amount: "5000000",
|
||||
denom: "ucosm",
|
||||
},
|
||||
],
|
||||
gas: "89000000",
|
||||
};
|
||||
|
||||
const account = (await client.authAccounts(faucetAddress)).result.value;
|
||||
const signBytes = makeSignBytes([theMsg], fee, defaultNetworkId, memo, account) as SignableBytes;
|
||||
const rawSignature = await wallet.createTransactionSignature(signer, signBytes, PrehashType.Sha256);
|
||||
const signature = encodeSecp256k1Signature(signer.pubkey.data, rawSignature);
|
||||
|
||||
const signedTx: StdTx = {
|
||||
msg: [theMsg],
|
||||
fee: fee,
|
||||
memo: memo,
|
||||
signatures: [signature],
|
||||
};
|
||||
|
||||
const postableBytes = marshalTx(signedTx);
|
||||
const result = await client.postTx(postableBytes);
|
||||
expect(result.code).toBeFalsy();
|
||||
// console.log("Raw log:", result.raw_log);
|
||||
const [firstLog] = parseSuccess(result.raw_log);
|
||||
const contractAddressAttr = firstLog.events[0].attributes.find(
|
||||
attr => attr.key === "contract_address",
|
||||
);
|
||||
if (!contractAddressAttr) throw new Error("Could not find contract_address attribute");
|
||||
contractAddress = contractAddressAttr.value || "";
|
||||
|
||||
const balance = (await client.authAccounts(contractAddress)).result.value.coins;
|
||||
expect(balance).toEqual([
|
||||
{
|
||||
amount: "5000000",
|
||||
amount: "1234",
|
||||
denom: "ucosm",
|
||||
},
|
||||
],
|
||||
gas: "89000000",
|
||||
};
|
||||
|
||||
const client = new RestClient(httpUrl);
|
||||
const account = (await client.authAccounts(faucetAddress)).result.value;
|
||||
const signBytes = makeSignBytes([theMsg], fee, defaultNetworkId, memo, account) as SignableBytes;
|
||||
const rawSignature = await wallet.createTransactionSignature(signer, signBytes, PrehashType.Sha256);
|
||||
const signature = encodeSecp256k1Signature(signer.pubkey.data, rawSignature);
|
||||
|
||||
const signedTx: StdTx = {
|
||||
msg: [theMsg],
|
||||
fee: fee,
|
||||
memo: memo,
|
||||
signatures: [signature],
|
||||
};
|
||||
|
||||
const postableBytes = marshalTx(signedTx);
|
||||
const result = await client.postTx(postableBytes);
|
||||
// console.log("Raw log:", result.raw_log);
|
||||
expect(result.code).toBeFalsy();
|
||||
const [firstLog] = parseSuccess(result.raw_log);
|
||||
const codeIdAttr = firstLog.events[0].attributes.find(attr => attr.key === "code_id");
|
||||
expect(codeIdAttr).toEqual({ key: "code_id", value: "1" });
|
||||
{
|
||||
amount: "321",
|
||||
denom: "ustake",
|
||||
},
|
||||
]);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -56,7 +56,22 @@ export interface MsgStoreCode extends MsgTemplate {
|
||||
readonly value: ValueStoreCode;
|
||||
}
|
||||
|
||||
export type Msg = MsgSend | MsgStoreCode | MsgTemplate;
|
||||
export interface ValueInstantiateContract {
|
||||
/** Bech32 account address */
|
||||
readonly sender: string;
|
||||
/** ID of the Wasm code that was uploaded before */
|
||||
readonly code_id: string;
|
||||
/** Init message as JavaScript object */
|
||||
readonly init_msg: object;
|
||||
readonly init_funds: ReadonlyArray<Coin>;
|
||||
}
|
||||
|
||||
export interface MsgInstantiateContract extends MsgTemplate {
|
||||
readonly type: "wasm/instantiate";
|
||||
readonly value: ValueInstantiateContract;
|
||||
}
|
||||
|
||||
export type Msg = MsgSend | MsgStoreCode | MsgInstantiateContract | MsgTemplate;
|
||||
|
||||
export function isMsgSend(msg: Msg): msg is MsgSend {
|
||||
return (msg as MsgSend).type === "cosmos-sdk/MsgSend";
|
||||
@ -66,6 +81,10 @@ 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 interface StdFee {
|
||||
readonly amount: ReadonlyArray<Coin>;
|
||||
readonly gas: string;
|
||||
|
4
packages/sdk/types/logs.d.ts
vendored
4
packages/sdk/types/logs.d.ts
vendored
@ -1,9 +1,9 @@
|
||||
export interface Attribute {
|
||||
readonly key: string;
|
||||
readonly value: string;
|
||||
readonly value: string | undefined;
|
||||
}
|
||||
export interface Event {
|
||||
readonly type: "message";
|
||||
readonly type: "message" | "transfer";
|
||||
readonly attributes: readonly Attribute[];
|
||||
}
|
||||
export interface Log {
|
||||
|
16
packages/sdk/types/types.d.ts
vendored
16
packages/sdk/types/types.d.ts
vendored
@ -41,9 +41,23 @@ export interface MsgStoreCode extends MsgTemplate {
|
||||
readonly type: "wasm/store-code";
|
||||
readonly value: ValueStoreCode;
|
||||
}
|
||||
export declare type Msg = MsgSend | MsgStoreCode | MsgTemplate;
|
||||
export interface ValueInstantiateContract {
|
||||
/** Bech32 account address */
|
||||
readonly sender: string;
|
||||
/** ID of the Wasm code that was uploaded before */
|
||||
readonly code_id: string;
|
||||
/** Init message as JavaScript object */
|
||||
readonly init_msg: object;
|
||||
readonly init_funds: ReadonlyArray<Coin>;
|
||||
}
|
||||
export interface MsgInstantiateContract extends MsgTemplate {
|
||||
readonly type: "wasm/instantiate";
|
||||
readonly value: ValueInstantiateContract;
|
||||
}
|
||||
export declare type Msg = MsgSend | MsgStoreCode | MsgInstantiateContract | MsgTemplate;
|
||||
export declare function isMsgSend(msg: Msg): msg is MsgSend;
|
||||
export declare function isMsgStoreCode(msg: Msg): msg is MsgStoreCode;
|
||||
export declare function isMsgInstantiateContract(msg: Msg): msg is MsgInstantiateContract;
|
||||
export interface StdFee {
|
||||
readonly amount: ReadonlyArray<Coin>;
|
||||
readonly gas: string;
|
||||
|
Loading…
x
Reference in New Issue
Block a user