diff --git a/packages/cli/README.md b/packages/cli/README.md index 5b8389c5..e4f8ed3c 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 +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); + +smartQuery(client, foo, { balance: { address: faucetAddress } }) + +const rcpt = await randomAddress(); +rcpt +smartQuery(client, foo, { balance: { address: rcpt } }) + +const execMsg = { transfer: {recipient: rcpt, amount: "808"}} +const exec = await executeContract(client, pen, foo, execMsg); +exec +exec[0].events[0] +smartQuery(client, foo, { balance: { address: rcpt } }) +``` + ## Other example codes ### Create random mnemonic and Cosmos address diff --git a/packages/cli/examples/helpers.ts b/packages/cli/examples/helpers.ts new file mode 100644 index 00000000..5f175a05 --- /dev/null +++ b/packages/cli/examples/helpers.ts @@ -0,0 +1,101 @@ +/* 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 +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": types.pubkeyType.secp256k1, "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 +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": types.pubkeyType.secp256k1, "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; +} + +// 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); + const pubkey = encodeSecp256k1Pubkey(randomPen.pubkey); + return encodeAddress(pubkey, "cosmos"); +}