From 7fc54bcd0ef4ceb11f207b521fe92a274bb20928 Mon Sep 17 00:00:00 2001
From: RodrigoAD <15104916+RodrigoAD@users.noreply.github.com>
Date: Thu, 10 Feb 2022 17:27:14 +0100
Subject: [PATCH 1/7] commands export data

raw tx on inspection command
---
 .../src/commands/abstract/executionWrapper.ts     | 15 +++++++++++++--
 .../src/commands/abstract/index.ts                |  6 ++++++
 .../src/commands/abstract/inspectionWrapper.ts    |  4 ++++
 .../src/commands/contracts/link/deploy.ts         |  4 ++++
 .../src/commands/contracts/link/transfer.ts       |  4 ++++
 .../src/commands/tooling/upload.ts                |  4 ++++
 .../gauntlet-terra/src/commands/internal/terra.ts |  1 +
 7 files changed, 36 insertions(+), 2 deletions(-)

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 ca24ed2c..edbd6ac2 100644
--- a/packages-ts/gauntlet-terra-contracts/src/commands/abstract/executionWrapper.ts
+++ b/packages-ts/gauntlet-terra-contracts/src/commands/abstract/executionWrapper.ts
@@ -1,6 +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'
 
 export interface AbstractInstruction<Input, ContractInput> {
   instruction: {
@@ -26,7 +27,7 @@ export const instructionToCommand = (instruction: AbstractInstruction<any, any>)
       super(flags, args)
     }
 
-    execute = async (): Promise<Result<TransactionResponse>> => {
+    buildCommand = async (): Promise<TerraCommand> => {
       const commandInput = await instruction.makeInput(this.flags, this.args)
       if (!instruction.validateInput(commandInput)) {
         throw new Error(`Invalid input params:  ${JSON.stringify(commandInput)}`)
@@ -34,7 +35,17 @@ export const instructionToCommand = (instruction: AbstractInstruction<any, any>)
       const input = await instruction.makeContractInput(commandInput)
       const abstractCommand = await makeAbstractCommand(id, this.flags, this.args, input)
       await abstractCommand.invokeMiddlewares(abstractCommand, abstractCommand.middlewares)
-      let response = await abstractCommand.execute()
+      return abstractCommand
+    }
+
+    makeRawTransaction = async (): Promise<MsgExecuteContract> => {
+      const command = await this.buildCommand()
+      return command.makeRawTransaction()
+    }
+
+    execute = async (): Promise<Result<TransactionResponse>> => {
+      const command = await this.buildCommand()
+      let response = await command.execute()
       if (instruction.afterExecute) {
         const data = instruction.afterExecute(response)
         response = { ...response, data: { ...data } }
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 67e5370a..c4eb5340 100644
--- a/packages-ts/gauntlet-terra-contracts/src/commands/abstract/index.ts
+++ b/packages-ts/gauntlet-terra-contracts/src/commands/abstract/index.ts
@@ -1,4 +1,5 @@
 import { Result } from '@chainlink/gauntlet-core'
+import { 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'
@@ -130,6 +131,11 @@ export default class AbstractCommand extends TerraCommand {
     this.contracts = [this.opts.contract.id]
   }
 
+  makeRawTransaction = async (): Promise<MsgExecuteContract> => {
+    const address = this.args[0]
+    return new MsgExecuteContract(this.wallet.key.accAddress, address, this.params)
+  }
+
   abstractDeploy: AbstractExecute = async (params: any) => {
     logger.loading(`Deploying contract ${this.opts.contract.id}`)
     const codeId = this.codeIds[this.opts.contract.id]
diff --git a/packages-ts/gauntlet-terra-contracts/src/commands/abstract/inspectionWrapper.ts b/packages-ts/gauntlet-terra-contracts/src/commands/abstract/inspectionWrapper.ts
index af08a195..c58578da 100644
--- a/packages-ts/gauntlet-terra-contracts/src/commands/abstract/inspectionWrapper.ts
+++ b/packages-ts/gauntlet-terra-contracts/src/commands/abstract/inspectionWrapper.ts
@@ -47,6 +47,10 @@ export const instructionToInspectCommand = <CommandInput, Expected>(
       super(flags, args)
     }
 
+    makeRawTransaction = () => {
+      throw new Error('Inspection command does not involve any transaction')
+    }
+
     execute = async (): Promise<Result<TransactionResponse>> => {
       const input = await inspectInstruction.makeInput(this.flags, this.args)
       const commands = await Promise.all(
diff --git a/packages-ts/gauntlet-terra-contracts/src/commands/contracts/link/deploy.ts b/packages-ts/gauntlet-terra-contracts/src/commands/contracts/link/deploy.ts
index c644a4e9..33fd1b45 100644
--- a/packages-ts/gauntlet-terra-contracts/src/commands/contracts/link/deploy.ts
+++ b/packages-ts/gauntlet-terra-contracts/src/commands/contracts/link/deploy.ts
@@ -19,6 +19,10 @@ export default class DeployLink extends TerraCommand {
     super(flags, args)
   }
 
+  makeRawTransaction = async () => {
+    throw new Error('Deploy LINK command: makeRawTransaction method not implemented')
+  }
+
   execute = async () => {
     await prompt(`Begin deploying LINK Token?`)
     const deploy = await this.deploy(CW20_BASE_CODE_IDs[this.flags.network], {
diff --git a/packages-ts/gauntlet-terra-contracts/src/commands/contracts/link/transfer.ts b/packages-ts/gauntlet-terra-contracts/src/commands/contracts/link/transfer.ts
index 367a1ea2..fd2732f6 100644
--- a/packages-ts/gauntlet-terra-contracts/src/commands/contracts/link/transfer.ts
+++ b/packages-ts/gauntlet-terra-contracts/src/commands/contracts/link/transfer.ts
@@ -17,6 +17,10 @@ export default class TransferLink extends TerraCommand {
     super(flags, args)
   }
 
+  makeRawTransaction = async () => {
+    throw new Error('Transfer LINK command: makeRawTransaction method not implemented')
+  }
+
   execute = async () => {
     const decimals = this.flags.decimals || 18
     const link = this.flags.link || process.env.LINK
diff --git a/packages-ts/gauntlet-terra-contracts/src/commands/tooling/upload.ts b/packages-ts/gauntlet-terra-contracts/src/commands/tooling/upload.ts
index 3862513a..0840c116 100644
--- a/packages-ts/gauntlet-terra-contracts/src/commands/tooling/upload.ts
+++ b/packages-ts/gauntlet-terra-contracts/src/commands/tooling/upload.ts
@@ -23,6 +23,10 @@ export default class UploadContractCode extends TerraCommand {
     super(flags, args)
   }
 
+  makeRawTransaction = async () => {
+    throw new Error('Upload command: makeRawTransaction method not implemented')
+  }
+
   getCodeId(response): number | undefined {
     return Number(this.parseResponseValue(response, 'store_code', 'code_id'))
   }
diff --git a/packages-ts/gauntlet-terra/src/commands/internal/terra.ts b/packages-ts/gauntlet-terra/src/commands/internal/terra.ts
index 72a140bf..a00f9162 100644
--- a/packages-ts/gauntlet-terra/src/commands/internal/terra.ts
+++ b/packages-ts/gauntlet-terra/src/commands/internal/terra.ts
@@ -23,6 +23,7 @@ export default abstract class TerraCommand extends WriteCommand<TransactionRespo
   contracts: string[]
   public codeIds: CodeIds
   abstract execute: () => Promise<Result<TransactionResponse>>
+  abstract makeRawTransaction: () => Promise<MsgExecuteContract>
 
   constructor(flags, args) {
     super(flags, args)

From 74ec3cc22c3c0a5a4fb78b336e28e8112bb5d0fd Mon Sep 17 00:00:00 2001
From: RodrigoAD <15104916+RodrigoAD@users.noreply.github.com>
Date: Mon, 14 Feb 2022 17:10:17 +0100
Subject: [PATCH 2/7] basic package and schema

---
 .../gauntlet-terra-contracts/src/index.ts     |  3 +-
 .../gauntlet-terra-cw20-multisig/README.md    |  1 +
 .../gauntlet-terra-cw20-multisig/package.json | 32 +++++++++++++++++++
 .../src/commands/multisig.ts                  | 32 +++++++++++++++++++
 .../gauntlet-terra-cw20-multisig/src/index.ts |  3 ++
 .../tsconfig.json                             |  9 ++++++
 tsconfig.json                                 |  3 ++
 7 files changed, 82 insertions(+), 1 deletion(-)
 create mode 100644 packages-ts/gauntlet-terra-cw20-multisig/README.md
 create mode 100644 packages-ts/gauntlet-terra-cw20-multisig/package.json
 create mode 100644 packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts
 create mode 100644 packages-ts/gauntlet-terra-cw20-multisig/src/index.ts
 create mode 100644 packages-ts/gauntlet-terra-cw20-multisig/tsconfig.json

diff --git a/packages-ts/gauntlet-terra-contracts/src/index.ts b/packages-ts/gauntlet-terra-contracts/src/index.ts
index dcf0b1ba..adbb36dd 100644
--- a/packages-ts/gauntlet-terra-contracts/src/index.ts
+++ b/packages-ts/gauntlet-terra-contracts/src/index.ts
@@ -1,4 +1,5 @@
 import { executeCLI } from '@chainlink/gauntlet-core'
+import { multisigWrapCommand } from '@chainlink/gauntlet-terra-cw20-multisig'
 import { existsSync } from 'fs'
 import path from 'path'
 import { io } from '@chainlink/gauntlet-core/dist/utils'
@@ -7,7 +8,7 @@ import { makeAbstractCommand } from './commands/abstract'
 import { defaultFlags } from './lib/args'
 
 const commands = {
-  custom: [...Terra],
+  custom: [...Terra, ...Terra.map(multisigWrapCommand)],
   loadDefaultFlags: () => defaultFlags,
   abstract: {
     findPolymorphic: () => undefined,
diff --git a/packages-ts/gauntlet-terra-cw20-multisig/README.md b/packages-ts/gauntlet-terra-cw20-multisig/README.md
new file mode 100644
index 00000000..e5c408c6
--- /dev/null
+++ b/packages-ts/gauntlet-terra-cw20-multisig/README.md
@@ -0,0 +1 @@
+# Gauntlet Terra CW20 Multisig
\ No newline at end of file
diff --git a/packages-ts/gauntlet-terra-cw20-multisig/package.json b/packages-ts/gauntlet-terra-cw20-multisig/package.json
new file mode 100644
index 00000000..d50505f5
--- /dev/null
+++ b/packages-ts/gauntlet-terra-cw20-multisig/package.json
@@ -0,0 +1,32 @@
+{
+  "name": "@chainlink/gauntlet-terra-cw20-multisig",
+  "version": "0.0.1",
+  "description": "Gauntlet Terra Cw20 Multisig",
+  "keywords": [
+    "typescript",
+    "cli"
+  ],
+  "main": "./dist/index.js",
+  "types": "dist/index.d.ts",
+  "files": [
+    "dist/**/*",
+    "!dist/**/*.test.js"
+  ],
+  "scripts": {
+    "preinstall": "node ../../scripts/require-yarn.js",
+    "gauntlet": "ts-node ./src/index.ts",
+    "lint": "tsc",
+    "test": "SKIP_PROMPTS=true jest --runInBand",
+    "test:coverage": "yarn test --collectCoverage",
+    "test:ci": "yarn test --ci",
+    "lint:format": "yarn prettier --check ./src",
+    "format": "yarn prettier --write ./src",
+    "clean": "rm -rf ./dist/ ./bin/",
+    "build": "yarn clean && tsc",
+    "bundle": "yarn build && pkg ."
+  },
+  "dependencies": {
+    "@chainlink/gauntlet-core": "0.0.7",
+    "@chainlink/gauntlet-terra": "*"
+  }
+}
diff --git a/packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts b/packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts
new file mode 100644
index 00000000..ff9690b1
--- /dev/null
+++ b/packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts
@@ -0,0 +1,32 @@
+import { Result } from '@chainlink/gauntlet-core'
+import { logger } from '@chainlink/gauntlet-core/dist/utils'
+import { TerraCommand, TransactionResponse } from '@chainlink/gauntlet-terra'
+import { MsgExecuteContract } from '@terra-money/terra.js'
+
+export const wrapCommand = (command) => {
+  return class Multisig extends TerraCommand {
+    command: TerraCommand
+
+    static id = `${command.id}:multisig`
+
+    constructor(flags, args) {
+      super(flags, args)
+
+      this.command = new command(flags, args)
+    }
+
+    makeRawTransaction = async () => {
+      // TODO: Replace with Mulstig tx message
+      return {} as MsgExecuteContract
+    }
+
+    execute = async () => {
+      // 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<TransactionResponse>
+    }
+  }
+}
diff --git a/packages-ts/gauntlet-terra-cw20-multisig/src/index.ts b/packages-ts/gauntlet-terra-cw20-multisig/src/index.ts
new file mode 100644
index 00000000..08a5280c
--- /dev/null
+++ b/packages-ts/gauntlet-terra-cw20-multisig/src/index.ts
@@ -0,0 +1,3 @@
+import { wrapCommand as multisigWrapCommand } from './commands/multisig'
+
+export { multisigWrapCommand }
diff --git a/packages-ts/gauntlet-terra-cw20-multisig/tsconfig.json b/packages-ts/gauntlet-terra-cw20-multisig/tsconfig.json
new file mode 100644
index 00000000..2c84c1fc
--- /dev/null
+++ b/packages-ts/gauntlet-terra-cw20-multisig/tsconfig.json
@@ -0,0 +1,9 @@
+{
+  "extends": "../../tsconfig.base.json",
+  "compilerOptions": {
+    "outDir": "dist",
+    "rootDir": "src"
+  },
+  "include": ["src/**/*"],
+  "exclude": ["dist", "**/*.spec.ts", "**/*.test.ts"]
+}
diff --git a/tsconfig.json b/tsconfig.json
index fb612e2a..a6bd767f 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,6 +6,9 @@
     {
       "path": "./packages-ts/gauntlet-terra"
     },
+    {
+      "path": "./packages-ts/gauntlet-terra-cw20-multisig"
+    },
     {
       "path": "./packages-ts/gauntlet-terra-contracts"
     }

From 242757138c696b0236b0e8700d0c10ab8275dd14 Mon Sep 17 00:00:00 2001
From: RodrigoAD <15104916+RodrigoAD@users.noreply.github.com>
Date: Wed, 16 Feb 2022 13:25:36 +0100
Subject: [PATCH 3/7] multisig command

---
 .../src/commands/abstract/executionWrapper.ts |   6 +-
 .../src/commands/abstract/index.ts            |   6 +-
 .../src/commands/multisig.ts                  | 152 +++++++++++++++++-
 .../src/commands/internal/terra.ts            |  23 ++-
 4 files changed, 172 insertions(+), 15 deletions(-)

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 edbd6ac2..00766832 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<Input, ContractInput> {
   instruction: {
@@ -38,9 +38,9 @@ export const instructionToCommand = (instruction: AbstractInstruction<any, any>)
       return abstractCommand
     }
 
-    makeRawTransaction = async (): Promise<MsgExecuteContract> => {
+    makeRawTransaction = async (signer: AccAddress): Promise<MsgExecuteContract> => {
       const command = await this.buildCommand()
-      return command.makeRawTransaction()
+      return command.makeRawTransaction(signer)
     }
 
     execute = async (): Promise<Result<TransactionResponse>> => {
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 c4eb5340..74b60a21 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'
@@ -131,9 +131,9 @@ export default class AbstractCommand extends TerraCommand {
     this.contracts = [this.opts.contract.id]
   }
 
-  makeRawTransaction = async (): Promise<MsgExecuteContract> => {
+  makeRawTransaction = async (signer: AccAddress): Promise<MsgExecuteContract> => {
     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 ff9690b1..b8849254 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<MsgExecuteContract>
+
+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<State> => {
+      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<TransactionResponse>
     }
diff --git a/packages-ts/gauntlet-terra/src/commands/internal/terra.ts b/packages-ts/gauntlet-terra/src/commands/internal/terra.ts
index a00f9162..cbc5eb67 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<TransactionRespo
   contracts: string[]
   public codeIds: CodeIds
   abstract execute: () => Promise<Result<TransactionResponse>>
-  abstract makeRawTransaction: () => Promise<MsgExecuteContract>
+  abstract makeRawTransaction: (signer: AccAddress) => Promise<MsgExecuteContract>
 
   constructor(flags, args) {
     super(flags, args)
@@ -64,6 +64,25 @@ export default abstract class TerraCommand extends WriteCommand<TransactionRespo
     return await this.provider.wasm.contractQuery(address, input, params)
   }
 
+  signAndSend = async (messages: MsgExecuteContract[]): Promise<TransactionResponse> => {
+    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)
 

From bd956c829ef0cff5c69c1f3fbf859a5f875ac429 Mon Sep 17 00:00:00 2001
From: RodrigoAD <15104916+RodrigoAD@users.noreply.github.com>
Date: Wed, 16 Feb 2022 14:31:05 +0100
Subject: [PATCH 4/7] multisig command improvements

list some todos
---
 .../src/commands/multisig.ts                  | 137 +++++++++++++-----
 .../src/lib/utils.ts                          |  13 ++
 2 files changed, 114 insertions(+), 36 deletions(-)
 create mode 100644 packages-ts/gauntlet-terra-cw20-multisig/src/lib/utils.ts

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 b8849254..eaf97514 100644
--- a/packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts
+++ b/packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts
@@ -2,6 +2,7 @@ import { Result } from '@chainlink/gauntlet-core'
 import { logger, prompt } from '@chainlink/gauntlet-core/dist/utils'
 import { TerraCommand, TransactionResponse } from '@chainlink/gauntlet-terra'
 import { AccAddress, MsgExecuteContract } from '@terra-money/terra.js'
+import { isDeepEqual } from '../lib/utils'
 
 type ProposalAction = (
   signer: AccAddress,
@@ -17,13 +18,15 @@ enum Vote {
 }
 
 type WasmMsg = {
-  execute: {
-    contract_addr: string
-    funds: {
-      denom: string
-      amount: string
-    }[]
-    msg: string
+  wasm: {
+    execute: {
+      contract_addr: string
+      funds: {
+        denom: string
+        amount: string
+      }[]
+      msg: string
+    }
   }
 }
 
@@ -31,11 +34,15 @@ enum Action {
   CREATE = 'create',
   APPROVE = 'approve',
   EXECUTE = 'execute',
+  NONE = 'none',
 }
 
 type State = {
   threshold: number
-  proposalStatus: Action
+  nextAction: Action
+  // https://github.com/CosmWasm/cw-plus/blob/82138f9484e538913f7faf78bc292fb14407aae8/packages/cw3/src/query.rs#L75
+  currentStatus?: 'pending' | 'open' | 'rejected' | 'passed' | 'executed'
+  data?: WasmMsg[]
 }
 
 export const wrapCommand = (command) => {
@@ -54,42 +61,53 @@ export const wrapCommand = (command) => {
       this.multisig = process.env.MULTISIG_ADDRESS as AccAddress
     }
 
-    makeRawTransaction = async (signer: AccAddress, status?: Action) => {
+    makeRawTransaction = async (signer: AccAddress, state?: State) => {
       const message = await this.command.makeRawTransaction(this.multisig)
 
       const operations = {
         [Action.CREATE]: this.executePropose,
         [Action.APPROVE]: this.executeApproval,
         [Action.EXECUTE]: this.executeExecution,
+        [Action.NONE]: () => {
+          throw new Error('No action needed')
+        },
       }
 
-      return operations[status](signer, Number(this.flags.proposal), message)
+      if (state.nextAction !== Action.CREATE) {
+        this.require(
+          await this.isSameProposal(state.data, [this.toWasmMsg(message)]),
+          'The transaction generated is different from the proposal provided',
+        )
+      }
+
+      return operations[state.nextAction](signer, Number(this.flags.proposal), message)
+    }
+
+    isSameProposal = (proposalMsgs: WasmMsg[], generatedMsgs: WasmMsg[]) => {
+      return isDeepEqual(proposalMsgs, generatedMsgs)
     }
 
     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'),
+        wasm: {
+          execute: {
+            contract_addr: message.contract,
+            funds: message.coins.toArray().map((c) => c.toData()),
+            msg: Buffer.from(JSON.stringify(message.execute_msg)).toString('base64'),
+          },
         },
       }
     }
 
-    executePropose: ProposalAction = async (signer, proposalId, message) => {
+    executePropose: ProposalAction = async (signer, _, message) => {
       logger.info('Generating data for creating new proposal')
       const proposeInput = {
         propose: {
           description: command.id,
-          msgs: [
-            {
-              wasm: this.toWasmMsg(message),
-            },
-          ],
+          msgs: [this.toWasmMsg(message)],
           title: command.id,
-          // latest: {
-          //   never: {},
-          // },
+          // TODO: Set expiration time
+          // latest: { never: {} }
         },
       }
       return new MsgExecuteContract(signer, this.multisig, proposeInput)
@@ -117,36 +135,72 @@ export const wrapCommand = (command) => {
     }
 
     fetchState = async (proposalId?: number): Promise<State> => {
-      const threshold = await this.query(this.multisig, {
+      const thresholdState = await this.query(this.multisig, {
         threshold: {},
       })
-      if (!proposalId)
+      const threshold = thresholdState.absolute_count.total_weight
+      if (!proposalId) {
         return {
           threshold,
-          proposalStatus: Action.CREATE,
+          nextAction: Action.CREATE,
         }
+      }
+
       const proposalState = await this.query(this.multisig, {
         proposal: {
           proposal_id: proposalId,
         },
       })
-      console.log(proposalState)
 
+      // TODO: Fetch owners and add them to state
+
+      const status = proposalState.status
+      const toNextAction = {
+        passed: Action.EXECUTE,
+        open: Action.APPROVE,
+        pending: Action.APPROVE,
+        rejected: Action.NONE,
+        executed: Action.NONE,
+      }
       return {
         threshold,
-        proposalStatus: Action.APPROVE,
+        nextAction: toNextAction[status],
+        currentStatus: status,
+        data: proposalState.msgs,
+      }
+    }
+
+    printPostInstructions = async (proposalId: number) => {
+      const state = await this.fetchState(proposalId)
+      // TODO: Calculate approvals left
+      const approvalsLeft = state.threshold - 1
+      const messages = {
+        [Action.APPROVE]: `The proposal needs ${approvalsLeft} more approvals. Run the same command with the flag --proposal=${proposalId}`,
+        [Action.EXECUTE]: `The proposal reached the threshold and can be executed. Run the same command with the flag --proposal=${proposalId}`,
+        [Action.NONE]: `The proposal has been executed. No more actions needed`,
       }
+      logger.line()
+      logger.info(`Next Actions:
+          ${messages[state.nextAction]}
+      `)
+      logger.line()
     }
 
     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)
+
+      if (state.nextAction === Action.NONE) {
+        await this.printPostInstructions(proposalId)
+        return
+      }
+      const rawTx = await this.makeRawTransaction(this.wallet.key.accAddress, state)
 
       console.info(`
         Proposal State:
           - Threshold: ${state.threshold}
-          - Status: ${state.proposalStatus}
+          - Next Action: ${state.nextAction.toUpperCase()}
+          - Owners: TODO
       `)
 
       const actionMessage = {
@@ -154,17 +208,28 @@ export const wrapCommand = (command) => {
         [Action.APPROVE]: 'APPROVING',
         [Action.EXECUTE]: 'EXECUTING',
       }
-      await prompt(`Continue ${actionMessage[state.proposalStatus]} proposal?`)
+      await prompt(`Continue ${actionMessage[state.nextAction]} proposal?`)
       const tx = await this.signAndSend([rawTx])
 
-      if (state.proposalStatus === Action.CREATE) {
-        // get proposal ID from logs
+      if (state.nextAction === Action.CREATE) {
+        const proposalFromEvent = tx.events[0].wasm.proposal_id[0]
+        logger.success(`New proposal created with ID: ${proposalFromEvent}`)
+        proposalId = Number(proposalFromEvent)
       }
 
-      // If ID Proposal is provided, check the proposal status, and either approve or execute.
-      // If ID Proposal is not provided, create a new proposal
+      await this.printPostInstructions(proposalId)
 
-      return {} as Result<TransactionResponse>
+      return {
+        responses: [
+          {
+            tx,
+            contract: this.multisig,
+          },
+        ],
+        data: {
+          proposalId,
+        },
+      } as Result<TransactionResponse>
     }
   }
 }
diff --git a/packages-ts/gauntlet-terra-cw20-multisig/src/lib/utils.ts b/packages-ts/gauntlet-terra-cw20-multisig/src/lib/utils.ts
new file mode 100644
index 00000000..07401085
--- /dev/null
+++ b/packages-ts/gauntlet-terra-cw20-multisig/src/lib/utils.ts
@@ -0,0 +1,13 @@
+import assert from 'assert'
+
+export const isDeepEqual = (a: any, b: any) => {
+  try {
+    assert.deepStrictEqual(a, b)
+  } catch (error) {
+    if (error.name === 'AssertionError') {
+      return false
+    }
+    throw error
+  }
+  return true
+}

From be350c4e15dedd5daac26abc94dc702c69da21f2 Mon Sep 17 00:00:00 2001
From: RodrigoAD <15104916+RodrigoAD@users.noreply.github.com>
Date: Wed, 16 Feb 2022 17:18:00 +0100
Subject: [PATCH 5/7] more detailed state

---
 .../src/commands/multisig.ts                  | 43 +++++++++++++------
 1 file changed, 29 insertions(+), 14 deletions(-)

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 eaf97514..a72f8195 100644
--- a/packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts
+++ b/packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts
@@ -40,6 +40,8 @@ enum Action {
 type State = {
   threshold: number
   nextAction: Action
+  owners: AccAddress[]
+  approvers: string[]
   // https://github.com/CosmWasm/cw-plus/blob/82138f9484e538913f7faf78bc292fb14407aae8/packages/cw3/src/query.rs#L75
   currentStatus?: 'pending' | 'open' | 'rejected' | 'passed' | 'executed'
   data?: WasmMsg[]
@@ -49,6 +51,7 @@ export const wrapCommand = (command) => {
   return class Multisig extends TerraCommand {
     command: TerraCommand
     multisig: AccAddress
+    multisigGroup: AccAddress
 
     static id = `${command.id}:multisig`
 
@@ -58,7 +61,9 @@ export const wrapCommand = (command) => {
       this.command = new command(flags, args)
 
       if (!AccAddress.validate(process.env.MULTISIG_ADDRESS)) throw new Error(`Invalid Multisig wallet address`)
+      if (!AccAddress.validate(process.env.MULTISIG_GROUP)) throw new Error(`Invalid Multisig group address`)
       this.multisig = process.env.MULTISIG_ADDRESS as AccAddress
+      this.multisigGroup = process.env.MULTISIG_GROUP as AccAddress
     }
 
     makeRawTransaction = async (signer: AccAddress, state?: State) => {
@@ -135,6 +140,10 @@ export const wrapCommand = (command) => {
     }
 
     fetchState = async (proposalId?: number): Promise<State> => {
+      const groupState = await this.query(this.multisigGroup, {
+        list_members: {},
+      })
+      const owners = groupState.members.map((m) => m.addr)
       const thresholdState = await this.query(this.multisig, {
         threshold: {},
       })
@@ -143,17 +152,20 @@ export const wrapCommand = (command) => {
         return {
           threshold,
           nextAction: Action.CREATE,
+          owners,
+          approvers: [],
         }
       }
-
       const proposalState = await this.query(this.multisig, {
         proposal: {
           proposal_id: proposalId,
         },
       })
-
-      // TODO: Fetch owners and add them to state
-
+      const votes = await this.query(this.multisig, {
+        list_votes: {
+          proposal_id: proposalId,
+        },
+      })
       const status = proposalState.status
       const toNextAction = {
         passed: Action.EXECUTE,
@@ -165,24 +177,23 @@ export const wrapCommand = (command) => {
       return {
         threshold,
         nextAction: toNextAction[status],
+        owners,
         currentStatus: status,
         data: proposalState.msgs,
+        approvers: votes.votes.filter((v) => v.vote === Vote.YES).map((v) => v.voter),
       }
     }
 
     printPostInstructions = async (proposalId: number) => {
       const state = await this.fetchState(proposalId)
-      // TODO: Calculate approvals left
-      const approvalsLeft = state.threshold - 1
+      const approvalsLeft = state.threshold - state.approvers.length
       const messages = {
         [Action.APPROVE]: `The proposal needs ${approvalsLeft} more approvals. Run the same command with the flag --proposal=${proposalId}`,
         [Action.EXECUTE]: `The proposal reached the threshold and can be executed. Run the same command with the flag --proposal=${proposalId}`,
         [Action.NONE]: `The proposal has been executed. No more actions needed`,
       }
       logger.line()
-      logger.info(`Next Actions:
-          ${messages[state.nextAction]}
-      `)
+      logger.info(`${messages[state.nextAction]}`)
       logger.line()
     }
 
@@ -196,11 +207,15 @@ export const wrapCommand = (command) => {
       }
       const rawTx = await this.makeRawTransaction(this.wallet.key.accAddress, state)
 
-      console.info(`
-        Proposal State:
-          - Threshold: ${state.threshold}
-          - Next Action: ${state.nextAction.toUpperCase()}
-          - Owners: TODO
+      logger.info(`Proposal State:
+        - Total Owners: ${state.owners.length}
+        - Owners List: ${state.owners}
+
+        - Threshold: ${state.threshold}
+        - Total Approvers: ${state.approvers.length}
+        - Approvers List: ${state.approvers}
+
+        - Next Action: ${state.nextAction.toUpperCase()}
       `)
 
       const actionMessage = {

From 076a163155a26776a9325dd70933cf7f53564949 Mon Sep 17 00:00:00 2001
From: RodrigoAD <15104916+RodrigoAD@users.noreply.github.com>
Date: Thu, 17 Feb 2022 13:24:54 +0100
Subject: [PATCH 6/7] execute option on multisig

---
 .../src/commands/multisig.ts                  | 41 +++++++++++++++----
 .../src/commands/internal/terra.ts            |  2 +
 2 files changed, 35 insertions(+), 8 deletions(-)

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 a72f8195..f0b16c0c 100644
--- a/packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts
+++ b/packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts
@@ -223,26 +223,51 @@ export const wrapCommand = (command) => {
         [Action.APPROVE]: 'APPROVING',
         [Action.EXECUTE]: 'EXECUTING',
       }
-      await prompt(`Continue ${actionMessage[state.nextAction]} proposal?`)
-      const tx = await this.signAndSend([rawTx])
 
-      if (state.nextAction === Action.CREATE) {
-        const proposalFromEvent = tx.events[0].wasm.proposal_id[0]
-        logger.success(`New proposal created with ID: ${proposalFromEvent}`)
-        proposalId = Number(proposalFromEvent)
+      if (this.flags.execute) {
+        await prompt(`Continue ${actionMessage[state.nextAction]} proposal?`)
+        const tx = await this.signAndSend([rawTx])
+
+        if (state.nextAction === Action.CREATE) {
+          const proposalFromEvent = tx.events[0].wasm.proposal_id[0]
+          logger.success(`New proposal created with ID: ${proposalFromEvent}`)
+          proposalId = Number(proposalFromEvent)
+        }
+
+        await this.printPostInstructions(proposalId)
+
+        return {
+          responses: [
+            {
+              tx,
+              contract: this.multisig,
+            },
+          ],
+          data: {
+            proposalId,
+          },
+        } as Result<TransactionResponse>
       }
 
-      await this.printPostInstructions(proposalId)
+      // TODO: Test raw message
+      const msgData = Buffer.from(JSON.stringify(rawTx.execute_msg)).toString('base64')
+      logger.line()
+      logger.success(`Message generated succesfully for ${actionMessage[state.nextAction]} proposal`)
+      logger.log()
+      logger.log(msgData)
+      logger.log()
+      logger.line()
 
       return {
         responses: [
           {
-            tx,
+            tx: {},
             contract: this.multisig,
           },
         ],
         data: {
           proposalId,
+          message: msgData,
         },
       } as Result<TransactionResponse>
     }
diff --git a/packages-ts/gauntlet-terra/src/commands/internal/terra.ts b/packages-ts/gauntlet-terra/src/commands/internal/terra.ts
index cbc5eb67..d62a3178 100644
--- a/packages-ts/gauntlet-terra/src/commands/internal/terra.ts
+++ b/packages-ts/gauntlet-terra/src/commands/internal/terra.ts
@@ -66,6 +66,7 @@ export default abstract class TerraCommand extends WriteCommand<TransactionRespo
 
   signAndSend = async (messages: MsgExecuteContract[]): Promise<TransactionResponse> => {
     try {
+      logger.loading('Signing transaction...')
       const tx = await this.wallet.createAndSignTx({
         msgs: messages,
         ...(this.wallet.key instanceof LedgerKey && {
@@ -73,6 +74,7 @@ export default abstract class TerraCommand extends WriteCommand<TransactionRespo
         }),
       })
 
+      logger.loading('Sending transaction...')
       const res = await this.provider.tx.broadcast(tx)
 
       logger.debug(res)

From 45a7bd08176895e935185659d2c4f6aa5041a3b6 Mon Sep 17 00:00:00 2001
From: RodrigoAD <15104916+RodrigoAD@users.noreply.github.com>
Date: Fri, 18 Feb 2022 11:40:43 +0100
Subject: [PATCH 7/7] refactor

---
 .../networks/.env.bombay-testnet              |  5 +++--
 .../gauntlet-terra-contracts/src/index.ts     |  2 +-
 .../src/lib/constants.ts                      |  2 +-
 packages-ts/gauntlet-terra-cw-plus/README.md  |  1 +
 .../package.json                              |  4 ++--
 .../src/commands/multisig.ts                  | 20 +++++++++----------
 .../src/index.ts                              |  0
 .../src/lib/utils.ts                          |  0
 .../tsconfig.json                             |  0
 .../gauntlet-terra-cw20-multisig/README.md    |  1 -
 .../src/commands/internal/terra.ts            |  8 ++------
 tsconfig.json                                 |  2 +-
 12 files changed, 21 insertions(+), 24 deletions(-)
 create mode 100644 packages-ts/gauntlet-terra-cw-plus/README.md
 rename packages-ts/{gauntlet-terra-cw20-multisig => gauntlet-terra-cw-plus}/package.json (88%)
 rename packages-ts/{gauntlet-terra-cw20-multisig => gauntlet-terra-cw-plus}/src/commands/multisig.ts (91%)
 rename packages-ts/{gauntlet-terra-cw20-multisig => gauntlet-terra-cw-plus}/src/index.ts (100%)
 rename packages-ts/{gauntlet-terra-cw20-multisig => gauntlet-terra-cw-plus}/src/lib/utils.ts (100%)
 rename packages-ts/{gauntlet-terra-cw20-multisig => gauntlet-terra-cw-plus}/tsconfig.json (100%)
 delete mode 100644 packages-ts/gauntlet-terra-cw20-multisig/README.md

diff --git a/packages-ts/gauntlet-terra-contracts/networks/.env.bombay-testnet b/packages-ts/gauntlet-terra-contracts/networks/.env.bombay-testnet
index 15fc2f61..f659c2de 100644
--- a/packages-ts/gauntlet-terra-contracts/networks/.env.bombay-testnet
+++ b/packages-ts/gauntlet-terra-contracts/networks/.env.bombay-testnet
@@ -4,5 +4,6 @@ DEFAULT_GAS_PRICE=0.5
 LINK=terra1fcksmfjncl6m7apvpalvhwv5jxd9djv5lwyu82
 BILLING_ACCESS_CONTROLLER=terra1trcufj64y53hxk7g8cra33xw3jkyvlr9lu99eu
 REQUESTER_ACCESS_CONTROLLER=terra1s38kfu4qp0ttwxkka9zupaysefl5qruhv5rc0z
-MULTISIG_GROUP=terra168lv95kfm49y9zu0409jmplj756ukxdrew7uta
-MULTISIG_WALLET=terra1u89pduw4enewduy9qydj925738cyn9juszgj54
+
+CW4_GROUP=terra1edk45cc6rckjszfmr87qfx50pfn2mnhg5mn3vd
+CW3_FLEX_MULTISIG=terra1cql0r4csmce0ntf68dmkvu3negs7m662uyg90g
diff --git a/packages-ts/gauntlet-terra-contracts/src/index.ts b/packages-ts/gauntlet-terra-contracts/src/index.ts
index adbb36dd..ef5a87fa 100644
--- a/packages-ts/gauntlet-terra-contracts/src/index.ts
+++ b/packages-ts/gauntlet-terra-contracts/src/index.ts
@@ -1,5 +1,5 @@
 import { executeCLI } from '@chainlink/gauntlet-core'
-import { multisigWrapCommand } from '@chainlink/gauntlet-terra-cw20-multisig'
+import { multisigWrapCommand } from '@chainlink/gauntlet-terra-cw-plus'
 import { existsSync } from 'fs'
 import path from 'path'
 import { io } from '@chainlink/gauntlet-core/dist/utils'
diff --git a/packages-ts/gauntlet-terra-contracts/src/lib/constants.ts b/packages-ts/gauntlet-terra-contracts/src/lib/constants.ts
index 20cb82fe..228a97ec 100644
--- a/packages-ts/gauntlet-terra-contracts/src/lib/constants.ts
+++ b/packages-ts/gauntlet-terra-contracts/src/lib/constants.ts
@@ -11,7 +11,7 @@ export const enum CATEGORIES {
   DEVIATION_FLAGGING_VALIDATOR = 'Devaiation Flagging Validator',
 }
 
-export const DEFAULT_RELEASE_VERSION = 'v0.0.4'
+export const DEFAULT_RELEASE_VERSION = 'local'
 export const DEFAULT_CWPLUS_VERSION = 'v0.9.1'
 
 export const ORACLES_MAX_LENGTH = 31
diff --git a/packages-ts/gauntlet-terra-cw-plus/README.md b/packages-ts/gauntlet-terra-cw-plus/README.md
new file mode 100644
index 00000000..3c95c93f
--- /dev/null
+++ b/packages-ts/gauntlet-terra-cw-plus/README.md
@@ -0,0 +1 @@
+# Gauntlet Terra CW Plus
\ No newline at end of file
diff --git a/packages-ts/gauntlet-terra-cw20-multisig/package.json b/packages-ts/gauntlet-terra-cw-plus/package.json
similarity index 88%
rename from packages-ts/gauntlet-terra-cw20-multisig/package.json
rename to packages-ts/gauntlet-terra-cw-plus/package.json
index d50505f5..c7f36436 100644
--- a/packages-ts/gauntlet-terra-cw20-multisig/package.json
+++ b/packages-ts/gauntlet-terra-cw-plus/package.json
@@ -1,7 +1,7 @@
 {
-  "name": "@chainlink/gauntlet-terra-cw20-multisig",
+  "name": "@chainlink/gauntlet-terra-cw-plus",
   "version": "0.0.1",
-  "description": "Gauntlet Terra Cw20 Multisig",
+  "description": "Gauntlet Terra CW Plus contracts",
   "keywords": [
     "typescript",
     "cli"
diff --git a/packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts b/packages-ts/gauntlet-terra-cw-plus/src/commands/multisig.ts
similarity index 91%
rename from packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts
rename to packages-ts/gauntlet-terra-cw-plus/src/commands/multisig.ts
index f0b16c0c..c15d5ce0 100644
--- a/packages-ts/gauntlet-terra-cw20-multisig/src/commands/multisig.ts
+++ b/packages-ts/gauntlet-terra-cw-plus/src/commands/multisig.ts
@@ -60,19 +60,19 @@ export const wrapCommand = (command) => {
 
       this.command = new command(flags, args)
 
-      if (!AccAddress.validate(process.env.MULTISIG_ADDRESS)) throw new Error(`Invalid Multisig wallet address`)
-      if (!AccAddress.validate(process.env.MULTISIG_GROUP)) throw new Error(`Invalid Multisig group address`)
-      this.multisig = process.env.MULTISIG_ADDRESS as AccAddress
-      this.multisigGroup = process.env.MULTISIG_GROUP as AccAddress
+      if (!AccAddress.validate(process.env.CW3_FLEX_MULTISIG)) throw new Error(`Invalid Multisig wallet address`)
+      if (!AccAddress.validate(process.env.CW4_GROUP)) throw new Error(`Invalid Multisig group address`)
+      this.multisig = process.env.CW3_FLEX_MULTISIG as AccAddress
+      this.multisigGroup = process.env.CW4_GROUP as AccAddress
     }
 
     makeRawTransaction = async (signer: AccAddress, state?: State) => {
       const message = await this.command.makeRawTransaction(this.multisig)
 
       const operations = {
-        [Action.CREATE]: this.executePropose,
-        [Action.APPROVE]: this.executeApproval,
-        [Action.EXECUTE]: this.executeExecution,
+        [Action.CREATE]: this.makeProposeTransaction,
+        [Action.APPROVE]: this.makeAcceptTransaction,
+        [Action.EXECUTE]: this.makeExecuteTransaction,
         [Action.NONE]: () => {
           throw new Error('No action needed')
         },
@@ -104,7 +104,7 @@ export const wrapCommand = (command) => {
       }
     }
 
-    executePropose: ProposalAction = async (signer, _, message) => {
+    makeProposeTransaction: ProposalAction = async (signer, _, message) => {
       logger.info('Generating data for creating new proposal')
       const proposeInput = {
         propose: {
@@ -118,7 +118,7 @@ export const wrapCommand = (command) => {
       return new MsgExecuteContract(signer, this.multisig, proposeInput)
     }
 
-    executeApproval: ProposalAction = async (signer, proposalId) => {
+    makeAcceptTransaction: ProposalAction = async (signer, proposalId) => {
       logger.info(`Generating data for approving proposal ${proposalId}`)
       const approvalInput = {
         vote: {
@@ -129,7 +129,7 @@ export const wrapCommand = (command) => {
       return new MsgExecuteContract(signer, this.multisig, approvalInput)
     }
 
-    executeExecution: ProposalAction = async (signer, proposalId) => {
+    makeExecuteTransaction: ProposalAction = async (signer, proposalId) => {
       logger.info(`Generating data for executing proposal ${proposalId}`)
       const executeInput = {
         execute: {
diff --git a/packages-ts/gauntlet-terra-cw20-multisig/src/index.ts b/packages-ts/gauntlet-terra-cw-plus/src/index.ts
similarity index 100%
rename from packages-ts/gauntlet-terra-cw20-multisig/src/index.ts
rename to packages-ts/gauntlet-terra-cw-plus/src/index.ts
diff --git a/packages-ts/gauntlet-terra-cw20-multisig/src/lib/utils.ts b/packages-ts/gauntlet-terra-cw-plus/src/lib/utils.ts
similarity index 100%
rename from packages-ts/gauntlet-terra-cw20-multisig/src/lib/utils.ts
rename to packages-ts/gauntlet-terra-cw-plus/src/lib/utils.ts
diff --git a/packages-ts/gauntlet-terra-cw20-multisig/tsconfig.json b/packages-ts/gauntlet-terra-cw-plus/tsconfig.json
similarity index 100%
rename from packages-ts/gauntlet-terra-cw20-multisig/tsconfig.json
rename to packages-ts/gauntlet-terra-cw-plus/tsconfig.json
diff --git a/packages-ts/gauntlet-terra-cw20-multisig/README.md b/packages-ts/gauntlet-terra-cw20-multisig/README.md
deleted file mode 100644
index e5c408c6..00000000
--- a/packages-ts/gauntlet-terra-cw20-multisig/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# Gauntlet Terra CW20 Multisig
\ No newline at end of file
diff --git a/packages-ts/gauntlet-terra/src/commands/internal/terra.ts b/packages-ts/gauntlet-terra/src/commands/internal/terra.ts
index d62a3178..ee6298ee 100644
--- a/packages-ts/gauntlet-terra/src/commands/internal/terra.ts
+++ b/packages-ts/gauntlet-terra/src/commands/internal/terra.ts
@@ -76,12 +76,10 @@ export default abstract class TerraCommand extends WriteCommand<TransactionRespo
 
       logger.loading('Sending transaction...')
       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)
+      const message = e?.response?.data?.message || e.message
+      throw new Error(message)
     }
   }
 
@@ -96,8 +94,6 @@ export default abstract class TerraCommand extends WriteCommand<TransactionRespo
     })
 
     const res = await this.provider.tx.broadcast(tx)
-
-    logger.debug(res)
     return this.wrapResponse(res)
   }
 
diff --git a/tsconfig.json b/tsconfig.json
index a6bd767f..e99b113c 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -7,7 +7,7 @@
       "path": "./packages-ts/gauntlet-terra"
     },
     {
-      "path": "./packages-ts/gauntlet-terra-cw20-multisig"
+      "path": "./packages-ts/gauntlet-terra-cw-plus"
     },
     {
       "path": "./packages-ts/gauntlet-terra-contracts"