From 0544836a609dddf350270ccd7514db4b2faf8cc2 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 10 Feb 2020 15:51:31 +0100 Subject: [PATCH 1/6] Start with some helpers --- packages/cli/examples/helpers.ts | 88 ++++++++++++++++++++++++++++++++ scripts/guide.md | 32 ++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 packages/cli/examples/helpers.ts create mode 100644 scripts/guide.md diff --git a/packages/cli/examples/helpers.ts b/packages/cli/examples/helpers.ts new file mode 100644 index 00000000..ee84fdff --- /dev/null +++ b/packages/cli/examples/helpers.ts @@ -0,0 +1,88 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { + logs, +} from "@cosmwasm/sdk"; + +const defaultHttpUrl = "http://localhost:1317"; +const defaultNetworkId = "testing"; +const defaultFee: types.StdFee = { + amount: [ + { + amount: "5000", + denom: "ucosm", + }, + ], + gas: "890000", +}; + +const faucetMnemonic = + "economy stock theory fatal elder harbor betray wasp final emotion task crumble siren bottom lizard educate guess current outdoor pair theory focus wife stone"; +const faucetAddress = "cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6"; + +const pen = await Secp256k1Pen.fromMnemonic(faucetMnemonic); +const client = new RestClient(defaultHttpUrl); + +const networkId = "testing"; + + +// helper functions +async function instantiateContract(initClient: RestClient, initPen: Secp256k1Pen, codeId: number, msg: object, transferAmount?: types.Coin[]): Promise { + const memo = "Create an ERC20 instance"; + const sender = encodeAddress({ "type": "tendermint/Secp256k1PubKey", "value": toBase64(initPen.pubkey)}, "cosmos"); + const instantiateContractMsg = { + type: "wasm/instantiate", + value: { + sender: sender, + code_id: codeId.toString(), + init_msg: msg, + init_funds: transferAmount || [], + }, + }; + const account = (await initClient.authAccounts(faucetAddress)).result.value; + const signBytes = makeSignBytes([instantiateContractMsg], defaultFee, networkId, memo, account); + const signature = encodeSecp256k1Signature(initPen.pubkey, await initPen.createSignature(signBytes)); + const signedTx = { + msg: [instantiateContractMsg], + fee: defaultFee, + memo: memo, + signatures: [signature], + }; + const result = await initClient.postTx(marshalTx(signedTx)); + if (result.code) { + throw new Error(`Failed tx: (${result.code}): ${result.raw_log}`) + } + const instantiationLogs = logs.parseLogs(result.logs); + const contractAddress = logs.findAttribute(instantiationLogs, "message", "contract_address").value; + return contractAddress; +} + +// helper functions +async function executeContract(execClient: RestClient, execPen: Secp256k1Pen, contractAddr: string, msg: object, transferAmount?: types.Coin[]): Promise { + const memo = "Create an ERC20 instance"; + const sender = encodeAddress({ "type": "tendermint/Secp256k1PubKey", "value": toBase64(execPen.pubkey)}, "cosmos"); + const instantiateContractMsg = { + type: "wasm/execute", + value: { + sender: sender, + contract: contractAddr, + msg: msg, + sent_funds: transferAmount || [], + }, + }; + const account = (await execClient.authAccounts(faucetAddress)).result.value; + const signBytes = makeSignBytes([instantiateContractMsg], defaultFee, networkId, memo, account); + const signature = encodeSecp256k1Signature(execPen.pubkey, await execPen.createSignature(signBytes)); + const signedTx = { + msg: [instantiateContractMsg], + fee: defaultFee, + memo: memo, + signatures: [signature], + }; + const result = await execClient.postTx(marshalTx(signedTx)); + if (result.code) { + throw new Error(`Failed tx: (${result.code}): ${result.raw_log}`) + } + const execLogs = logs.parseLogs(result.logs); + return execLogs; +} + diff --git a/scripts/guide.md b/scripts/guide.md new file mode 100644 index 00000000..ce75709a --- /dev/null +++ b/scripts/guide.md @@ -0,0 +1,32 @@ +## Setup + +```sh +node ./scripts/cosm/deploy_erc20.js +``` + +## Make account + +```sh +cd packages/cli +./bin/cosmwasm-cli --init examples/helpers.ts +``` + +Now, check it and init/execute contracts: + +```js +const account = (await client.authAccounts(faucetAddress)).result.value; +account + +client.listCodeInfo() +client.listContractAddresses() + +// query this contract +const addr = (await client.listContractAddresses())[0] +const info = await client.getContractInfo(addr) +info.init_msg + +// try some actions +client.queryContractSmart(addr, { balance: { address: faucetAddress } }) + +// make a new contract +``` From b047658af3ccc96b8388a309a53b2b3783c958c6 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 10 Feb 2020 15:58:30 +0100 Subject: [PATCH 2/6] Got guide working --- packages/cli/examples/helpers.ts | 8 ++++---- scripts/guide.md | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/cli/examples/helpers.ts b/packages/cli/examples/helpers.ts index ee84fdff..18ade78e 100644 --- a/packages/cli/examples/helpers.ts +++ b/packages/cli/examples/helpers.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/camelcase */ import { - logs, + logs } from "@cosmwasm/sdk"; const defaultHttpUrl = "http://localhost:1317"; @@ -26,9 +26,9 @@ const networkId = "testing"; // helper functions -async function instantiateContract(initClient: RestClient, initPen: Secp256k1Pen, codeId: number, msg: object, transferAmount?: types.Coin[]): Promise { +const instantiateContract = async (initClient: RestClient, initPen: Secp256k1Pen, codeId: number, msg: object, transferAmount?: types.Coin[]): Promise => { const memo = "Create an ERC20 instance"; - const sender = encodeAddress({ "type": "tendermint/Secp256k1PubKey", "value": toBase64(initPen.pubkey)}, "cosmos"); + const sender = encodeAddress({ "type": types.pubkeyType.secp256k1, "value": toBase64(initPen.pubkey)}, "cosmos"); const instantiateContractMsg = { type: "wasm/instantiate", value: { @@ -57,7 +57,7 @@ async function instantiateContract(initClient: RestClient, initPen: Secp256k1Pen } // helper functions -async function executeContract(execClient: RestClient, execPen: Secp256k1Pen, contractAddr: string, msg: object, transferAmount?: types.Coin[]): Promise { +const executeContract = async (execClient: RestClient, execPen: Secp256k1Pen, contractAddr: string, msg: object, transferAmount?: types.Coin[]): Promise => { const memo = "Create an ERC20 instance"; const sender = encodeAddress({ "type": "tendermint/Secp256k1PubKey", "value": toBase64(execPen.pubkey)}, "cosmos"); const instantiateContractMsg = { diff --git a/scripts/guide.md b/scripts/guide.md index ce75709a..3efc8e78 100644 --- a/scripts/guide.md +++ b/scripts/guide.md @@ -29,4 +29,11 @@ info.init_msg client.queryContractSmart(addr, { balance: { address: faucetAddress } }) // make a new contract +const initMsg = { name: "Foo Coin", symbol: "FOO", decimals: 2, initial_balances: [{address: faucetAddress, amount: "123456789"}]} +const foo = await instantiateContract(client, pen, 1, initMsg); + +client.queryContractSmart(foo, { balance: { address: faucetAddress } }) + +// TODO: unused account +// TODO: execute and send tokens ``` From 1bea452fbc7f01beeb60bb08a71bfcd2f66b5dba Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 10 Feb 2020 16:28:57 +0100 Subject: [PATCH 3/6] more --- packages/cli/examples/helpers.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/cli/examples/helpers.ts b/packages/cli/examples/helpers.ts index 18ade78e..9d36fee2 100644 --- a/packages/cli/examples/helpers.ts +++ b/packages/cli/examples/helpers.ts @@ -86,3 +86,9 @@ const executeContract = async (execClient: RestClient, execPen: Secp256k1Pen, co return execLogs; } +const randomAddress = async (): Promise => { + const mnemonic = Bip39.encode(Random.getBytes(16)).toString(); + const randomPen = await Secp256k1Pen.fromMnemonic(mnemonic); + const pubkey = encodeSecp256k1Pubkey(randomPen.pubkey); + return encodeAddress(pubkey, "cosmos"); +} From ec9a1eab9224c95d962dd06ddc73df74e685487f Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 10 Feb 2020 16:49:25 +0100 Subject: [PATCH 4/6] Run through the full script --- packages/cli/examples/helpers.ts | 2 +- scripts/guide.md | 39 ++++++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/packages/cli/examples/helpers.ts b/packages/cli/examples/helpers.ts index 9d36fee2..b3761e5a 100644 --- a/packages/cli/examples/helpers.ts +++ b/packages/cli/examples/helpers.ts @@ -59,7 +59,7 @@ const instantiateContract = async (initClient: RestClient, initPen: Secp256k1Pen // helper functions const executeContract = async (execClient: RestClient, execPen: Secp256k1Pen, contractAddr: string, msg: object, transferAmount?: types.Coin[]): Promise => { const memo = "Create an ERC20 instance"; - const sender = encodeAddress({ "type": "tendermint/Secp256k1PubKey", "value": toBase64(execPen.pubkey)}, "cosmos"); + const sender = encodeAddress({ "type": types.pubkeyType.secp256k1, "value": toBase64(execPen.pubkey)}, "cosmos"); const instantiateContractMsg = { type: "wasm/execute", value: { diff --git a/scripts/guide.md b/scripts/guide.md index 3efc8e78..19c1fab4 100644 --- a/scripts/guide.md +++ b/scripts/guide.md @@ -1,22 +1,37 @@ ## Setup +Start chain: + +```sh +./scripts/cosm/start.sh +``` + +```sh +cd packages/cli +./bin/cosmwasm-cli --init examples/helpers.ts +``` + +```js +const account = (await client.authAccounts(faucetAddress)).result.value; +account + +client.listCodeInfo() +client.listContractAddresses() +``` + +## Deploy Contract + ```sh node ./scripts/cosm/deploy_erc20.js ``` ## Make account -```sh -cd packages/cli -./bin/cosmwasm-cli --init examples/helpers.ts -``` Now, check it and init/execute contracts: ```js -const account = (await client.authAccounts(faucetAddress)).result.value; -account - +// see it is now deployed client.listCodeInfo() client.listContractAddresses() @@ -34,6 +49,16 @@ const foo = await instantiateContract(client, pen, 1, initMsg); client.queryContractSmart(foo, { balance: { address: faucetAddress } }) +const rcpt = await randomAddress(); +rcpt +client.queryContractSmart(foo, { balance: { address: rcpt } }) + +const execMsg = { transfer: {recipient: rcpt, amount: "808"}} +const exec = await executeContract(client, pen, foo, execMsg); +exec +client.queryContractSmart(foo, { balance: { address: rcpt } }) + + // TODO: unused account // TODO: execute and send tokens ``` From c3bd4985244279fef8eaf28a14f51b0509cf1f30 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 11 Feb 2020 10:48:36 +0100 Subject: [PATCH 5/6] Merge guide into readme --- packages/cli/README.md | 44 +++++++++++++++++++++++++++++ scripts/guide.md | 64 ------------------------------------------ 2 files changed, 44 insertions(+), 64 deletions(-) delete mode 100644 scripts/guide.md diff --git a/packages/cli/README.md b/packages/cli/README.md index 5b8389c5..c541c306 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -75,6 +75,50 @@ const signedTx: types.StdTx = { const postResult = await client.postTx(marshalTx(signedTx)); ``` +## Extended helpers + +The above code shows you the use of the API and various objects and is a great way to learn +how to embed cosmwasm-js into your project. However, if you just want a cli to perform some +quick queries on a chain, you can use an extended set of helpers: + +1. Start a local wasmd blockchain, for example running the setup from `../../scripts/cosm/start.sh` +2. Start with `./bin/cosmwasm-cli --init examples/helpers.ts` (note the new init file) +3. Deploy some erc20 contracts: `../../scripts/cosm/init.sh` +4. Play around as in the following example code + +```ts +const account = (await client.authAccounts(faucetAddress)).result.value; +account + +// show all code and contracts +client.listCodeInfo() +client.listContractAddresses() + +// query the first contract +const addr = (await client.listContractAddresses())[0] +const info = await client.getContractInfo(addr) +info.init_msg + +// see your balance here +client.queryContractSmart(addr, { balance: { address: faucetAddress } }) + +// make a new contract +const initMsg = { name: "Foo Coin", symbol: "FOO", decimals: 2, initial_balances: [{address: faucetAddress, amount: "123456789"}]} +const foo = await instantiateContract(client, pen, 1, initMsg); + +client.queryContractSmart(foo, { balance: { address: faucetAddress } }) + +const rcpt = await randomAddress(); +rcpt +client.queryContractSmart(foo, { balance: { address: rcpt } }) + +const execMsg = { transfer: {recipient: rcpt, amount: "808"}} +const exec = await executeContract(client, pen, foo, execMsg); +exec +client.queryContractSmart(foo, { balance: { address: rcpt } }) + +``` + ## Other example codes ### Create random mnemonic and Cosmos address diff --git a/scripts/guide.md b/scripts/guide.md deleted file mode 100644 index 19c1fab4..00000000 --- a/scripts/guide.md +++ /dev/null @@ -1,64 +0,0 @@ -## Setup - -Start chain: - -```sh -./scripts/cosm/start.sh -``` - -```sh -cd packages/cli -./bin/cosmwasm-cli --init examples/helpers.ts -``` - -```js -const account = (await client.authAccounts(faucetAddress)).result.value; -account - -client.listCodeInfo() -client.listContractAddresses() -``` - -## Deploy Contract - -```sh -node ./scripts/cosm/deploy_erc20.js -``` - -## Make account - - -Now, check it and init/execute contracts: - -```js -// see it is now deployed -client.listCodeInfo() -client.listContractAddresses() - -// query this contract -const addr = (await client.listContractAddresses())[0] -const info = await client.getContractInfo(addr) -info.init_msg - -// try some actions -client.queryContractSmart(addr, { balance: { address: faucetAddress } }) - -// make a new contract -const initMsg = { name: "Foo Coin", symbol: "FOO", decimals: 2, initial_balances: [{address: faucetAddress, amount: "123456789"}]} -const foo = await instantiateContract(client, pen, 1, initMsg); - -client.queryContractSmart(foo, { balance: { address: faucetAddress } }) - -const rcpt = await randomAddress(); -rcpt -client.queryContractSmart(foo, { balance: { address: rcpt } }) - -const execMsg = { transfer: {recipient: rcpt, amount: "808"}} -const exec = await executeContract(client, pen, foo, execMsg); -exec -client.queryContractSmart(foo, { balance: { address: rcpt } }) - - -// TODO: unused account -// TODO: execute and send tokens -``` From d9bcd821c16f873debf0675943cabfbbb22f29fc Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 11 Feb 2020 10:55:48 +0100 Subject: [PATCH 6/6] Added helper to parse Uint8Array as Utf8-encoded json for smart queries --- packages/cli/README.md | 10 +++++----- packages/cli/examples/helpers.ts | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index c541c306..e4f8ed3c 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -100,23 +100,23 @@ const info = await client.getContractInfo(addr) info.init_msg // see your balance here -client.queryContractSmart(addr, { balance: { address: faucetAddress } }) +smartQuery(client, addr, { balance: { address: faucetAddress } }) // make a new contract const initMsg = { name: "Foo Coin", symbol: "FOO", decimals: 2, initial_balances: [{address: faucetAddress, amount: "123456789"}]} const foo = await instantiateContract(client, pen, 1, initMsg); -client.queryContractSmart(foo, { balance: { address: faucetAddress } }) +smartQuery(client, foo, { balance: { address: faucetAddress } }) const rcpt = await randomAddress(); rcpt -client.queryContractSmart(foo, { balance: { address: rcpt } }) +smartQuery(client, foo, { balance: { address: rcpt } }) const execMsg = { transfer: {recipient: rcpt, amount: "808"}} const exec = await executeContract(client, pen, foo, execMsg); exec -client.queryContractSmart(foo, { balance: { address: rcpt } }) - +exec[0].events[0] +smartQuery(client, foo, { balance: { address: rcpt } }) ``` ## Other example codes diff --git a/packages/cli/examples/helpers.ts b/packages/cli/examples/helpers.ts index b3761e5a..5f175a05 100644 --- a/packages/cli/examples/helpers.ts +++ b/packages/cli/examples/helpers.ts @@ -86,6 +86,13 @@ const executeContract = async (execClient: RestClient, execPen: Secp256k1Pen, co return execLogs; } +// smartQuery assumes the content is proper JSON data and parses before returning it +const smartQuery = async (client: RestClient, addr: string, query: object): Promise => { + const bin = await client.queryContractSmart(addr, query); + return JSON.parse(fromUtf8(bin)); +} + + const randomAddress = async (): Promise => { const mnemonic = Bip39.encode(Random.getBytes(16)).toString(); const randomPen = await Secp256k1Pen.fromMnemonic(mnemonic);