diff --git a/packages-ts/gauntlet-terra-contracts/src/commands/abstract/executionWrapper.ts b/packages-ts/gauntlet-terra-contracts/src/commands/abstract/executionWrapper.ts
index edbd6ac22..00766832b 100644
--- a/packages-ts/gauntlet-terra-contracts/src/commands/abstract/executionWrapper.ts
+++ b/packages-ts/gauntlet-terra-contracts/src/commands/abstract/executionWrapper.ts
@@ -1,7 +1,7 @@
import AbstractCommand, { makeAbstractCommand } from '.'
import { Result } from '@chainlink/gauntlet-core'
import { TerraCommand, TransactionResponse } from '@chainlink/gauntlet-terra'
-import { MsgExecuteContract } from '@terra-money/terra.js'
+import { AccAddress, MsgExecuteContract } from '@terra-money/terra.js'
export interface AbstractInstruction {
instruction: {
@@ -38,9 +38,9 @@ export const instructionToCommand = (instruction: AbstractInstruction)
return abstractCommand
}
- makeRawTransaction = async (): Promise => {
+ makeRawTransaction = async (signer: AccAddress): Promise => {
const command = await this.buildCommand()
- return command.makeRawTransaction()
+ return command.makeRawTransaction(signer)
}
execute = async (): Promise> => {
diff --git a/packages-ts/gauntlet-terra-contracts/src/commands/abstract/index.ts b/packages-ts/gauntlet-terra-contracts/src/commands/abstract/index.ts
index 810fb0168..f04616ab3 100644
--- a/packages-ts/gauntlet-terra-contracts/src/commands/abstract/index.ts
+++ b/packages-ts/gauntlet-terra-contracts/src/commands/abstract/index.ts
@@ -1,5 +1,5 @@
import { Result } from '@chainlink/gauntlet-core'
-import { MsgExecuteContract } from '@terra-money/terra.js'
+import { AccAddress, MsgExecuteContract } from '@terra-money/terra.js'
import { logger, prompt } from '@chainlink/gauntlet-core/dist/utils'
import { TransactionResponse, TerraCommand } from '@chainlink/gauntlet-terra'
import { Contract, CONTRACT_LIST, getContract, TerraABI, TERRA_OPERATIONS } from '../../lib/contracts'
@@ -126,9 +126,9 @@ export default class AbstractCommand extends TerraCommand {
this.contracts = [this.opts.contract.id]
}
- makeRawTransaction = async (): Promise => {
+ makeRawTransaction = async (signer: AccAddress): Promise => {
const address = this.args[0]
- return new MsgExecuteContract(this.wallet.key.accAddress, address, this.params)
+ return new MsgExecuteContract(signer, address, this.params)
}
abstractDeploy: AbstractExecute = async (params: any) => {
diff --git a/packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts b/packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts
index ff9690b15..b8849254d 100644
--- a/packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts
+++ b/packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts
@@ -1,11 +1,47 @@
import { Result } from '@chainlink/gauntlet-core'
-import { logger } from '@chainlink/gauntlet-core/dist/utils'
+import { logger, prompt } from '@chainlink/gauntlet-core/dist/utils'
import { TerraCommand, TransactionResponse } from '@chainlink/gauntlet-terra'
-import { MsgExecuteContract } from '@terra-money/terra.js'
+import { AccAddress, MsgExecuteContract } from '@terra-money/terra.js'
+
+type ProposalAction = (
+ signer: AccAddress,
+ proposalId: number,
+ message: MsgExecuteContract,
+) => Promise
+
+enum Vote {
+ YES = 'yes',
+ NO = 'no',
+ ABS = 'abstain',
+ VETO = 'veto',
+}
+
+type WasmMsg = {
+ execute: {
+ contract_addr: string
+ funds: {
+ denom: string
+ amount: string
+ }[]
+ msg: string
+ }
+}
+
+enum Action {
+ CREATE = 'create',
+ APPROVE = 'approve',
+ EXECUTE = 'execute',
+}
+
+type State = {
+ threshold: number
+ proposalStatus: Action
+}
export const wrapCommand = (command) => {
return class Multisig extends TerraCommand {
command: TerraCommand
+ multisig: AccAddress
static id = `${command.id}:multisig`
@@ -13,18 +49,120 @@ export const wrapCommand = (command) => {
super(flags, args)
this.command = new command(flags, args)
+
+ if (!AccAddress.validate(process.env.MULTISIG_ADDRESS)) throw new Error(`Invalid Multisig wallet address`)
+ this.multisig = process.env.MULTISIG_ADDRESS as AccAddress
+ }
+
+ makeRawTransaction = async (signer: AccAddress, status?: Action) => {
+ const message = await this.command.makeRawTransaction(this.multisig)
+
+ const operations = {
+ [Action.CREATE]: this.executePropose,
+ [Action.APPROVE]: this.executeApproval,
+ [Action.EXECUTE]: this.executeExecution,
+ }
+
+ return operations[status](signer, Number(this.flags.proposal), message)
+ }
+
+ toWasmMsg = (message: MsgExecuteContract): WasmMsg => {
+ return {
+ execute: {
+ contract_addr: message.contract,
+ funds: message.coins.toArray().map((c) => c.toData()),
+ msg: Buffer.from(message.toJSON()).toString('base64'),
+ },
+ }
+ }
+
+ executePropose: ProposalAction = async (signer, proposalId, message) => {
+ logger.info('Generating data for creating new proposal')
+ const proposeInput = {
+ propose: {
+ description: command.id,
+ msgs: [
+ {
+ wasm: this.toWasmMsg(message),
+ },
+ ],
+ title: command.id,
+ // latest: {
+ // never: {},
+ // },
+ },
+ }
+ return new MsgExecuteContract(signer, this.multisig, proposeInput)
+ }
+
+ executeApproval: ProposalAction = async (signer, proposalId) => {
+ logger.info(`Generating data for approving proposal ${proposalId}`)
+ const approvalInput = {
+ vote: {
+ vote: Vote.YES,
+ proposal_id: proposalId,
+ },
+ }
+ return new MsgExecuteContract(signer, this.multisig, approvalInput)
}
- makeRawTransaction = async () => {
- // TODO: Replace with Mulstig tx message
- return {} as MsgExecuteContract
+ executeExecution: ProposalAction = async (signer, proposalId) => {
+ logger.info(`Generating data for executing proposal ${proposalId}`)
+ const executeInput = {
+ execute: {
+ proposal_id: proposalId,
+ },
+ }
+ return new MsgExecuteContract(signer, this.multisig, executeInput)
+ }
+
+ fetchState = async (proposalId?: number): Promise => {
+ const threshold = await this.query(this.multisig, {
+ threshold: {},
+ })
+ if (!proposalId)
+ return {
+ threshold,
+ proposalStatus: Action.CREATE,
+ }
+ const proposalState = await this.query(this.multisig, {
+ proposal: {
+ proposal_id: proposalId,
+ },
+ })
+ console.log(proposalState)
+
+ return {
+ threshold,
+ proposalStatus: Action.APPROVE,
+ }
}
execute = async () => {
+ let proposalId = !!this.flags.proposal && Number(this.flags.proposal)
+ const state = await this.fetchState(proposalId)
+ const rawTx = await this.makeRawTransaction(this.wallet.key.accAddress, state.proposalStatus)
+
+ console.info(`
+ Proposal State:
+ - Threshold: ${state.threshold}
+ - Status: ${state.proposalStatus}
+ `)
+
+ const actionMessage = {
+ [Action.CREATE]: 'CREATING',
+ [Action.APPROVE]: 'APPROVING',
+ [Action.EXECUTE]: 'EXECUTING',
+ }
+ await prompt(`Continue ${actionMessage[state.proposalStatus]} proposal?`)
+ const tx = await this.signAndSend([rawTx])
+
+ if (state.proposalStatus === Action.CREATE) {
+ // get proposal ID from logs
+ }
+
// If ID Proposal is provided, check the proposal status, and either approve or execute.
// If ID Proposal is not provided, create a new proposal
- const message = await this.command.makeRawTransaction()
- logger.log('Command data:', message)
return {} as Result
}
diff --git a/packages-ts/gauntlet-terra/src/commands/internal/terra.ts b/packages-ts/gauntlet-terra/src/commands/internal/terra.ts
index a00f9162d..cbc5eb673 100644
--- a/packages-ts/gauntlet-terra/src/commands/internal/terra.ts
+++ b/packages-ts/gauntlet-terra/src/commands/internal/terra.ts
@@ -1,6 +1,6 @@
import { Result, WriteCommand } from '@chainlink/gauntlet-core'
import { logger } from '@chainlink/gauntlet-core/dist/utils'
-import { EventsByType, MsgStoreCode, TxLog } from '@terra-money/terra.js'
+import { EventsByType, MsgStoreCode, AccAddress, TxLog } from '@terra-money/terra.js'
import { SignMode } from '@terra-money/terra.proto/cosmos/tx/signing/v1beta1/signing'
import { withProvider, withWallet, withCodeIds, withNetwork } from '../middlewares'
@@ -23,7 +23,7 @@ export default abstract class TerraCommand extends WriteCommand Promise>
- abstract makeRawTransaction: () => Promise
+ abstract makeRawTransaction: (signer: AccAddress) => Promise
constructor(flags, args) {
super(flags, args)
@@ -64,6 +64,25 @@ export default abstract class TerraCommand extends WriteCommand => {
+ try {
+ const tx = await this.wallet.createAndSignTx({
+ msgs: messages,
+ ...(this.wallet.key instanceof LedgerKey && {
+ signMode: SignMode.SIGN_MODE_LEGACY_AMINO_JSON,
+ }),
+ })
+
+ const res = await this.provider.tx.broadcast(tx)
+
+ logger.debug(res)
+ return this.wrapResponse(res)
+ } catch (e) {
+ const details = e.response.data
+ throw new Error(details.message)
+ }
+ }
+
async call(address, input) {
const msg = new MsgExecuteContract(this.wallet.key.accAddress, address, input)