From 06da4768f32218b88a5b6748ae4f8650d2e5381c Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Thu, 14 Dec 2023 11:33:41 +0100 Subject: [PATCH] Gov instruction creation helpers - Add one to make a proposal for all serum markets - Switch created proposals to be drafts - Add PROPOSAL_LINK env arg - Add serum3EditMarketIx() --- .../governanceInstructions/createProposal.ts | 35 ++-- ts/client/scripts/update-risk-params.ts | 4 +- .../scripts/update-serum-market-params.ts | 153 ++++++++++++++++++ ts/client/src/client.ts | 43 +++-- 4 files changed, 208 insertions(+), 27 deletions(-) create mode 100644 ts/client/scripts/update-serum-market-params.ts diff --git a/ts/client/scripts/governanceInstructions/createProposal.ts b/ts/client/scripts/governanceInstructions/createProposal.ts index 76d535229..396fe90c1 100644 --- a/ts/client/scripts/governanceInstructions/createProposal.ts +++ b/ts/client/scripts/governanceInstructions/createProposal.ts @@ -40,6 +40,7 @@ export const createProposal = async ( proposalIndex: number, proposalInstructions: TransactionInstruction[], client: VsrClient, + signOff: boolean, ) => { const instructions: TransactionInstruction[] = []; const walletPk = wallet.publicKey!; @@ -95,11 +96,6 @@ export const createProposal = async ( payer, ); - const signatoryRecordAddress = await getSignatoryRecordAddress( - MANGO_GOVERNANCE_PROGRAM, - proposalAddress, - signatory, - ); const insertInstructions: TransactionInstruction[] = []; for (const i in proposalInstructions) { const instruction = getInstructionDataFromBase64( @@ -120,17 +116,24 @@ export const createProposal = async ( payer, ); } - withSignOffProposal( - insertInstructions, // SingOff proposal needs to be executed after inserting instructions hence we add it to insertInstructions - MANGO_GOVERNANCE_PROGRAM, - programVersion, - MANGO_REALM_PK, - governance, - proposalAddress, - signatory, - signatoryRecordAddress, - undefined, - ); + if (signOff) { + const signatoryRecordAddress = await getSignatoryRecordAddress( + MANGO_GOVERNANCE_PROGRAM, + proposalAddress, + signatory, + ); + withSignOffProposal( + insertInstructions, // SingOff proposal needs to be executed after inserting instructions hence we add it to insertInstructions + MANGO_GOVERNANCE_PROGRAM, + programVersion, + MANGO_REALM_PK, + governance, + proposalAddress, + signatory, + signatoryRecordAddress, + undefined, + ); + } const txChunks = chunk([...instructions, ...insertInstructions], 2); diff --git a/ts/client/scripts/update-risk-params.ts b/ts/client/scripts/update-risk-params.ts index 6e7570fb0..edcc3f93c 100644 --- a/ts/client/scripts/update-risk-params.ts +++ b/ts/client/scripts/update-risk-params.ts @@ -42,6 +42,7 @@ import { const { MB_CLUSTER_URL, PROPOSAL_TITLE, + PROPOSAL_LINK, VSR_DELEGATE_KEYPAIR, VSR_DELEGATE_FROM_PK, DRY_RUN, @@ -285,10 +286,11 @@ async function updateTokenParams(): Promise { MANGO_DAO_WALLET_GOVERNANCE, tokenOwnerRecord, PROPOSAL_TITLE ? PROPOSAL_TITLE : 'Update risk parameters for tokens', - '', + PROPOSAL_LINK ?? '', Object.values(proposals).length, instructions, vsrClient!, + false, ); console.log(proposalAddress.toBase58()); } diff --git a/ts/client/scripts/update-serum-market-params.ts b/ts/client/scripts/update-serum-market-params.ts new file mode 100644 index 000000000..419c2bc5e --- /dev/null +++ b/ts/client/scripts/update-serum-market-params.ts @@ -0,0 +1,153 @@ +import { + MidPriceImpact, + getMidPriceImpacts, +} from '@blockworks-foundation/mango-v4-settings/lib/helpers/listingTools'; +import { AnchorProvider, Wallet } from '@coral-xyz/anchor'; +import { BN } from '@project-serum/anchor'; +import { + getAllProposals, + getTokenOwnerRecord, + getTokenOwnerRecordAddress, +} from '@solana/spl-governance'; +import { + AccountMeta, + Connection, + Keypair, + PublicKey, + Transaction, + TransactionInstruction, +} from '@solana/web3.js'; +import fs from 'fs'; +import { Bank } from '../src/accounts/bank'; +import { Group } from '../src/accounts/group'; +import { MangoAccount } from '../src/accounts/mangoAccount'; +import { Builder } from '../src/builder'; +import { MangoClient } from '../src/client'; +import { NullTokenEditParams } from '../src/clientIxParamBuilder'; +import { MANGO_V4_MAIN_GROUP as MANGO_V4_PRIMARY_GROUP } from '../src/constants'; +import { getEquityForMangoAccounts } from '../src/risk'; +import { buildFetch } from '../src/utils'; +import { + MANGO_DAO_WALLET_GOVERNANCE, + MANGO_GOVERNANCE_PROGRAM, + MANGO_MINT, + MANGO_REALM_PK, +} from './governanceInstructions/constants'; +import { createProposal } from './governanceInstructions/createProposal'; +import { + DEFAULT_VSR_ID, + VsrClient, +} from './governanceInstructions/voteStakeRegistryClient'; + +const { + MB_CLUSTER_URL, + PROPOSAL_TITLE, + PROPOSAL_LINK, + VSR_DELEGATE_KEYPAIR, + VSR_DELEGATE_FROM_PK, + DRY_RUN, +} = process.env; + +async function buildClient(): Promise { + return await MangoClient.connectDefault(MB_CLUSTER_URL!); +} + +async function setupWallet(): Promise { + const clientKeypair = Keypair.fromSecretKey( + Buffer.from(JSON.parse(fs.readFileSync(VSR_DELEGATE_KEYPAIR!, 'utf-8'))), + ); + const clientWallet = new Wallet(clientKeypair); + + return clientWallet; +} + +async function setupVsr( + connection: Connection, + clientWallet: Wallet, +): Promise { + const options = AnchorProvider.defaultOptions(); + const provider = new AnchorProvider(connection, clientWallet, options); + const vsrClient = await VsrClient.connect(provider, DEFAULT_VSR_ID); + return vsrClient; +} + +async function updateSerumMarketParams(): Promise { + const [client, wallet] = await Promise.all([buildClient(), setupWallet()]); + const vsrClient = await setupVsr(client.connection, wallet); + + const group = await client.getGroup(MANGO_V4_PRIMARY_GROUP); + + const instructions: TransactionInstruction[] = []; + + Array.from(group.serum3MarketsMapByMarketIndex.values()).forEach( + async (sm) => { + const ix = await client.serum3EditMarketIx( + group, + sm.marketIndex, + group.admin, + null, + null, + null, + 0.5, + ); + + const tx = new Transaction({ feePayer: wallet.publicKey }).add(ix); + const simulated = await client.connection.simulateTransaction(tx); + + if (simulated.value.err) { + console.log('error', simulated.value.logs); + throw simulated.value.logs; + } + + instructions.push(ix); + }, + ); + + const tokenOwnerRecordPk = await getTokenOwnerRecordAddress( + MANGO_GOVERNANCE_PROGRAM, + MANGO_REALM_PK, + MANGO_MINT, + new PublicKey(VSR_DELEGATE_FROM_PK!), + ); + + const [tokenOwnerRecord, proposals] = await Promise.all([ + getTokenOwnerRecord(client.connection, tokenOwnerRecordPk), + getAllProposals( + client.connection, + MANGO_GOVERNANCE_PROGRAM, + MANGO_REALM_PK, + ), + ]); + + const walletSigner = wallet as never; + + if (!DRY_RUN) { + const proposalAddress = await createProposal( + client.connection, + walletSigner, + MANGO_DAO_WALLET_GOVERNANCE, + tokenOwnerRecord, + PROPOSAL_TITLE ? PROPOSAL_TITLE : 'Update risk parameters for tokens', + PROPOSAL_LINK ?? '', + Object.values(proposals).length, + instructions, + vsrClient!, + false, + ); + console.log(proposalAddress.toBase58()); + } +} + +async function main(): Promise { + try { + await updateSerumMarketParams(); + } catch (error) { + console.log(error); + } +} + +try { + main(); +} catch (error) { + console.log(error); +} diff --git a/ts/client/src/client.ts b/ts/client/src/client.ts index 88933c062..1018c6fa0 100644 --- a/ts/client/src/client.ts +++ b/ts/client/src/client.ts @@ -1,6 +1,7 @@ import { AnchorProvider, BN, + Instruction, Program, Provider, Wallet, @@ -1634,6 +1635,28 @@ export class MangoClient { return await this.sendAndConfirmTransactionForGroup(group, [ix]); } + public async serum3EditMarketIx( + group: Group, + serum3MarketIndex: MarketIndex, + admin: PublicKey, + reduceOnly: boolean | null, + forceClose: boolean | null, + name: string | null, + oraclePriceBand: number | null, + ): Promise { + const serum3Market = + group.serum3MarketsMapByMarketIndex.get(serum3MarketIndex); + const ix = await this.program.methods + .serum3EditMarket(reduceOnly, forceClose, name, oraclePriceBand) + .accounts({ + group: group.publicKey, + admin: admin, + market: serum3Market?.publicKey, + }) + .instruction(); + return ix; + } + public async serum3EditMarket( group: Group, serum3MarketIndex: MarketIndex, @@ -1642,16 +1665,16 @@ export class MangoClient { name: string | null, oraclePriceBand: number | null, ): Promise { - const serum3Market = - group.serum3MarketsMapByMarketIndex.get(serum3MarketIndex); - const ix = await this.program.methods - .serum3EditMarket(reduceOnly, forceClose, name, oraclePriceBand) - .accounts({ - group: group.publicKey, - admin: (this.program.provider as AnchorProvider).wallet.publicKey, - market: serum3Market?.publicKey, - }) - .instruction(); + const admin = (this.program.provider as AnchorProvider).wallet.publicKey; + const ix = await this.serum3EditMarketIx( + group, + serum3MarketIndex, + admin, + reduceOnly, + forceClose, + name, + oraclePriceBand, + ); return await this.sendAndConfirmTransactionForGroup(group, [ix]); }