diff --git a/CHANGELOG.md b/CHANGELOG.md index 191718e..ff3d968 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,9 +23,12 @@ All notable changes to the Cosmy Wasmy extension will be documented in this file ### Added - For testnet and mainnet chain, all contracts by the selected account can be imported in a single click +- Add option to simulate a contract execution in the tx view in sidebar ### Changed +- Cosmwasm history view now shows the input at the end of the table as it can be unknowingly long + ### Deprecated ### Removed diff --git a/src/helpers/cosmwasm/api.ts b/src/helpers/cosmwasm/api.ts index 2b5016d..8775bee 100644 --- a/src/helpers/cosmwasm/api.ts +++ b/src/helpers/cosmwasm/api.ts @@ -1,4 +1,5 @@ -import { CodeDetails, CosmWasmClient } from "@cosmjs/cosmwasm-stargate"; +import { CodeDetails, CosmWasmClient, MsgExecuteContractEncodeObject } from "@cosmjs/cosmwasm-stargate"; +import { MsgExecuteContract, } from "cosmjs-types/cosmwasm/wasm/v1/tx"; import { SigningCosmWasmClient } from "@cosmjs/cosmwasm-stargate"; import { WrapWallet } from '../sign/wrapwallet'; import { GasPrice } from '@cosmjs/stargate'; @@ -8,6 +9,7 @@ import { Workspace } from "../workspace"; import { ResponseHandler } from "../responseHandler"; import { Account } from "../../models/account"; import { Utils } from "../../views/utils"; +import { TextEncoder } from "util"; export class CosmwasmAPI { @@ -25,8 +27,8 @@ export class CosmwasmAPI { for (let code of codes) { const contracts = await client.getContracts(code.id); for (let contract of contracts) { - let contractInfo = await client.getContract(contract); - importedContracts.push(new Contract(contractInfo.label, contractInfo.address, contractInfo.codeId, contractInfo.creator, global.workspaceChain.configName)); + let contractInfo = await client.getContract(contract); + importedContracts.push(new Contract(contractInfo.label, contractInfo.address, contractInfo.codeId, contractInfo.creator, global.workspaceChain.configName)); } } return importedContracts; @@ -52,7 +54,7 @@ export class CosmwasmAPI { if (!isNaN(parseFloat(decimals))) { return Utils.TransDecimals(balance.amount, decimals); } - + return balance.amount; } @@ -72,7 +74,7 @@ export class CosmwasmAPI { export class Cosmwasm { - public static async GetQueryClient(): Promise { + public static async GetQueryClient(): Promise { const rpcEndpoint = global.workspaceChain.rpcEndpoint; return CosmWasmClient.connect(rpcEndpoint); } @@ -86,7 +88,7 @@ export class Cosmwasm { if (!gasDenom) { gasDenom = global.workspaceChain.chainDenom; } - + let gasPrice = global.workspaceChain.defaultGasPrice + gasDenom; let client = await SigningCosmWasmClient.connectWithSigner( global.workspaceChain.rpcEndpoint, @@ -134,6 +136,38 @@ export class Cosmwasm { } } + public static async Simulate(account: Account, contract: string, req: any, memo: string, fundsStr: string) { + try { + let client = await Cosmwasm.GetSigningClient(); + let funds = Utils.ParseCoins(fundsStr); + const inputJson = JSON.stringify(req); + const encodeInput = toUtf8(inputJson); + const encodeMsg = MsgExecuteContract.fromPartial({ + sender: account.address, + contract: contract, + msg: encodeInput, + funds: [...(funds || [])], + }); + const msg: MsgExecuteContractEncodeObject = { + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: encodeMsg, + }; + let gasConsumed = await client.simulate(account.address, [msg], memo) + ResponseHandler.OutputSuccess(JSON.stringify(req, null, 4), "Gas Consumed: " + gasConsumed, "Simulate"); + return { + isSuccess: true, + response: gasConsumed + }; + } + catch (err: any) { + ResponseHandler.OutputError(JSON.stringify(req, null, 4), err, "Simulate"); + return { + isSuccess: false, + response: err + }; + } + } + // public static async ExecuteMultiple(account: Account, txs: ExecuteInstruction[]) { // try { // let client = await Cosmwasm.GetSigningClient(); @@ -206,3 +240,6 @@ export class Cosmwasm { } } } +function toUtf8(str: string): Uint8Array { + return new TextEncoder().encode(str); +} diff --git a/src/helpers/cosmwasm/executer.ts b/src/helpers/cosmwasm/executer.ts index 8e1bfe2..78378d4 100644 --- a/src/helpers/cosmwasm/executer.ts +++ b/src/helpers/cosmwasm/executer.ts @@ -100,6 +100,47 @@ export class Executer { } }); } + + public Simulate(value: any, location: vscode.ProgressLocation | { viewId: string }) { + const account = Workspace.GetSelectedAccount(); + if (!account) { + vscode.window.showErrorMessage(vscode.l10n.t("No account selected. Select an account from the Accounts view.")); + return; + } + const contract = Workspace.GetSelectedContract(); + if (!contract) { + vscode.window.showErrorMessage(vscode.l10n.t("No contract selected. Select a contract in the Contracts view.")); + return; + } + try { + JSON.parse(value.input); + } catch { + vscode.window.showErrorMessage(vscode.l10n.t("The input is not valid JSON")); + return; + } + + const req = JSON.parse(value.input); + + if (this.useHistoryHandler) { + HistoryHandler.RecordAction(this.context, contract, Action.Simulate, value); + } + + vscode.window.withProgress({ + location: location, + title: vscode.l10n.t("Simulating msg on the contract - {label}", { label: contract.label }), + cancellable: false + }, async (progress, token) => { + token.onCancellationRequested(() => { }); + progress.report({ message: '' }); + const gasUsed = await Cosmwasm.Simulate(account, contract.contractAddress, req, "Sent from cosmy-wasmy", value.funds); + if (gasUsed.isSuccess) { + return Promise.resolve(); + } + else { + return Promise.reject(); + } + }); + } } diff --git a/src/helpers/extensionData/historyHandler.ts b/src/helpers/extensionData/historyHandler.ts index fb759d5..3518295 100644 --- a/src/helpers/extensionData/historyHandler.ts +++ b/src/helpers/extensionData/historyHandler.ts @@ -49,5 +49,6 @@ export enum Action { Tx, Migrate, Initialize, - Invalid + Invalid, + Simulate } diff --git a/src/views/cosmwasmHistoryView.ts b/src/views/cosmwasmHistoryView.ts index 4f858ba..4c82633 100644 --- a/src/views/cosmwasmHistoryView.ts +++ b/src/views/cosmwasmHistoryView.ts @@ -38,6 +38,10 @@ export class CosmwasmHistoryView { this.executer.Execute(action.inputData, vscode.ProgressLocation.Notification) break; } + case Action.Simulate: { + this.executer.Simulate(action.inputData, vscode.ProgressLocation.Notification); + break; + } default: { } } } @@ -136,9 +140,9 @@ export class CosmwasmHistoryView { Run Type Label - Input Data - Input Funds Contract Address + Input Funds + Input Data `; content += this.getTableData(); content += "
"; @@ -175,9 +179,9 @@ export class CosmwasmHistoryView { else { tableContent += "" + vscode.l10n.t("Contract not found in imported contracts.") + ""; } - tableContent += "" + inputData + ""; - tableContent += "" + inputFunds + ""; tableContent += "" + item.contractAddr + ""; + tableContent += "" + inputFunds + ""; + tableContent += "" + inputData + ""; tableContent += ""; }); return tableContent; diff --git a/src/views/txProvider.ts b/src/views/txProvider.ts index 0be3fb9..9328c52 100644 --- a/src/views/txProvider.ts +++ b/src/views/txProvider.ts @@ -24,6 +24,11 @@ export class TxProvider implements vscode.WebviewViewProvider { new Executer(this.context, true).Execute(data.value, { viewId: Constants.VIEWS_EXECUTE }); break; } + case 'simulate-text': + { + new Executer(this.context, true).Simulate(data.value, { viewId: Constants.VIEWS_EXECUTE }); + break; + } } }); } @@ -56,6 +61,7 @@ export class TxProvider implements vscode.WebviewViewProvider { +