From 9afb28caf44409c606b4bc87801507069ef23faf Mon Sep 17 00:00:00 2001 From: Rodrigo Ariza <15104916+RodrigoAD@users.noreply.github.com> Date: Tue, 25 Jan 2022 17:01:57 +0100 Subject: [PATCH] Gauntlet Multisig - Reduce create proposal TX size (#134) * split account creation into separate tx * removed temp env vars --- .../src/commands/multisig.ts | 69 ++-- .../artifacts/schemas/serum_multisig.json | 322 ++++++++++++++++++ 2 files changed, 361 insertions(+), 30 deletions(-) create mode 100644 gauntlet/packages/gauntlet-solana-contracts/artifacts/schemas/serum_multisig.json diff --git a/gauntlet/packages/gauntlet-serum-multisig/src/commands/multisig.ts b/gauntlet/packages/gauntlet-serum-multisig/src/commands/multisig.ts index d88947356..fc5bbef50 100644 --- a/gauntlet/packages/gauntlet-serum-multisig/src/commands/multisig.ts +++ b/gauntlet/packages/gauntlet-serum-multisig/src/commands/multisig.ts @@ -1,7 +1,7 @@ import { Result } from '@chainlink/gauntlet-core' import { TransactionResponse, SolanaCommand, RawTransaction } from '@chainlink/gauntlet-solana' -import { logger, BN } from '@chainlink/gauntlet-core/dist/utils' -import { PublicKey, SYSVAR_RENT_PUBKEY, Keypair } from '@solana/web3.js' +import { logger } from '@chainlink/gauntlet-core/dist/utils' +import { PublicKey, SYSVAR_RENT_PUBKEY, Keypair, Transaction } from '@solana/web3.js' import { CONTRACT_LIST, getContract } from '@chainlink/gauntlet-solana-contracts' import { ProgramError, parseIdlErrors, Idl, Program } from '@project-serum/anchor' @@ -11,7 +11,7 @@ type ProposalContext = { proposalState: any } -type ProposalAction = (proposal: Keypair | PublicKey, context: ProposalContext) => Promise +type ProposalAction = (proposal: PublicKey, context: ProposalContext) => Promise export const wrapCommand = (command) => { return class Multisig extends SolanaCommand { @@ -60,13 +60,13 @@ export const wrapCommand = (command) => { const rawTx = (await this.command.makeRawTransaction(multisigSigner))[0] const isCreation = !this.flags.proposal if (isCreation) { - const proposal = Keypair.generate() + const proposal = await this.createProposalAcount() const result = await this.wrapAction(this.createProposal)(proposal, { rawTx, multisigSigner, proposalState: {}, }) - this.inspectProposalState(proposal.publicKey, threshold, owners) + this.inspectProposalState(proposal, threshold, owners) return result } @@ -83,7 +83,14 @@ export const wrapCommand = (command) => { const isAlreadyExecuted = proposalState.didExecute if (isAlreadyExecuted) { logger.info(`Proposal is already executed`) - return {} as Result + return { + responses: [ + { + tx: this.wrapResponse('', proposal.toString()), + contract: proposal.toString(), + }, + ], + } } if (!this.isReadyForExecution(proposalState, threshold)) { @@ -95,31 +102,34 @@ export const wrapCommand = (command) => { return result } - wrapAction = (action: ProposalAction) => async (proposal: Keypair | PublicKey, context: ProposalContext) => { - try { - const tx = await action(proposal, context) - return { - responses: [ - { - tx: this.wrapResponse(tx, proposal.toString()), - contract: proposal.toString(), - }, - ], - } as Result - } catch (e) { - // known errors, defined in multisig contract. see serum_multisig.json - if (e.code >= 300 && e.code < 400) { - logger.error(e.msg) - } else { - logger.error(e) - } - throw e + wrapAction = (action: ProposalAction) => async ( + proposal: PublicKey, + context: ProposalContext, + ): Promise> => { + const tx = await action(proposal, context) + return { + responses: [ + { + tx: this.wrapResponse(tx, proposal.toString()), + contract: proposal.toString(), + }, + ], } } - createProposal: ProposalAction = async (proposal: Keypair, context): Promise => { + createProposalAcount = async (): Promise => { + logger.log('Creating proposal account') + const proposal = Keypair.generate() + const txSize = 1300 // Space enough + const proposalAccount = await this.program.account.transaction.createInstruction(proposal, txSize) + const accountTx = new Transaction().add(proposalAccount) + await this.provider.send(accountTx, [proposal, this.wallet.payer]) + logger.success(`Proposal account created at: ${proposal.publicKey.toString()}`) + return proposal.publicKey + } + + createProposal: ProposalAction = async (proposal: PublicKey, context): Promise => { logger.loading(`Creating proposal`) - const txSize = 1000 const tx = await this.program.rpc.createTransaction( context.rawTx.programId, context.rawTx.accounts, @@ -127,12 +137,11 @@ export const wrapCommand = (command) => { { accounts: { multisig: this.multisigAddress, - transaction: proposal.publicKey, + transaction: proposal, proposer: this.wallet.payer.publicKey, rent: SYSVAR_RENT_PUBKEY, }, - instructions: [await this.program.account.transaction.createInstruction(proposal, txSize)], - signers: [proposal, this.wallet.payer], + signers: [this.wallet.payer], }, ) return tx diff --git a/gauntlet/packages/gauntlet-solana-contracts/artifacts/schemas/serum_multisig.json b/gauntlet/packages/gauntlet-solana-contracts/artifacts/schemas/serum_multisig.json new file mode 100644 index 000000000..a0a1203a1 --- /dev/null +++ b/gauntlet/packages/gauntlet-solana-contracts/artifacts/schemas/serum_multisig.json @@ -0,0 +1,322 @@ +{ + "version": "0.0.0", + "name": "serum_multisig", + "instructions": [ + { + "name": "createMultisig", + "accounts": [ + { + "name": "multisig", + "isMut": true, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "owners", + "type": { + "vec": "publicKey" + } + }, + { + "name": "threshold", + "type": "u64" + }, + { + "name": "nonce", + "type": "u8" + } + ] + }, + { + "name": "createTransaction", + "accounts": [ + { + "name": "multisig", + "isMut": false, + "isSigner": false + }, + { + "name": "transaction", + "isMut": true, + "isSigner": false + }, + { + "name": "proposer", + "isMut": false, + "isSigner": true + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "pid", + "type": "publicKey" + }, + { + "name": "accs", + "type": { + "vec": { + "defined": "TransactionAccount" + } + } + }, + { + "name": "data", + "type": "bytes" + } + ] + }, + { + "name": "approve", + "accounts": [ + { + "name": "multisig", + "isMut": false, + "isSigner": false + }, + { + "name": "transaction", + "isMut": true, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "setOwnersAndChangeThreshold", + "accounts": [ + { + "name": "multisig", + "isMut": true, + "isSigner": false + }, + { + "name": "multisigSigner", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "owners", + "type": { + "vec": "publicKey" + } + }, + { + "name": "threshold", + "type": "u64" + } + ] + }, + { + "name": "setOwners", + "accounts": [ + { + "name": "multisig", + "isMut": true, + "isSigner": false + }, + { + "name": "multisigSigner", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "owners", + "type": { + "vec": "publicKey" + } + } + ] + }, + { + "name": "changeThreshold", + "accounts": [ + { + "name": "multisig", + "isMut": true, + "isSigner": false + }, + { + "name": "multisigSigner", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "threshold", + "type": "u64" + } + ] + }, + { + "name": "executeTransaction", + "accounts": [ + { + "name": "multisig", + "isMut": false, + "isSigner": false + }, + { + "name": "multisigSigner", + "isMut": false, + "isSigner": false + }, + { + "name": "transaction", + "isMut": true, + "isSigner": false + } + ], + "args": [] + } + ], + "accounts": [ + { + "name": "Multisig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "owners", + "type": { + "vec": "publicKey" + } + }, + { + "name": "threshold", + "type": "u64" + }, + { + "name": "nonce", + "type": "u8" + }, + { + "name": "ownerSetSeqno", + "type": "u32" + } + ] + } + }, + { + "name": "Transaction", + "type": { + "kind": "struct", + "fields": [ + { + "name": "multisig", + "type": "publicKey" + }, + { + "name": "programId", + "type": "publicKey" + }, + { + "name": "accounts", + "type": { + "vec": { + "defined": "TransactionAccount" + } + } + }, + { + "name": "data", + "type": "bytes" + }, + { + "name": "signers", + "type": { + "vec": "bool" + } + }, + { + "name": "didExecute", + "type": "bool" + }, + { + "name": "ownerSetSeqno", + "type": "u32" + } + ] + } + } + ], + "types": [ + { + "name": "TransactionAccount", + "type": { + "kind": "struct", + "fields": [ + { + "name": "pubkey", + "type": "publicKey" + }, + { + "name": "isSigner", + "type": "bool" + }, + { + "name": "isWritable", + "type": "bool" + } + ] + } + } + ], + "errors": [ + { + "code": 300, + "name": "InvalidOwner", + "msg": "The given owner is not part of this multisig." + }, + { + "code": 301, + "name": "NotEnoughSigners", + "msg": "Not enough owners signed this transaction." + }, + { + "code": 302, + "name": "TransactionAlreadySigned", + "msg": "Cannot delete a transaction that has been signed by an owner." + }, + { + "code": 303, + "name": "Overflow", + "msg": "Overflow when adding." + }, + { + "code": 304, + "name": "UnableToDelete", + "msg": "Cannot delete a transaction the owner did not create." + }, + { + "code": 305, + "name": "AlreadyExecuted", + "msg": "The given transaction has already been executed." + }, + { + "code": 306, + "name": "InvalidThreshold", + "msg": "Threshold must be less than or equal to the number of owners." + } + ] +} \ No newline at end of file