From 0a4f7150d6395adb1b78fa77c3724285414db663 Mon Sep 17 00:00:00 2001 From: microwavedcola1 <89031858+microwavedcola1@users.noreply.github.com> Date: Wed, 9 Nov 2022 09:59:42 +0100 Subject: [PATCH] add support for msrm vault for serum3 discounts (#285) * add support for msrm vault for serum3 discounts Signed-off-by: microwavedcola1 * Fix client Signed-off-by: microwavedcola1 * client support Signed-off-by: microwavedcola1 * More client code and todos Signed-off-by: microwavedcola1 * prettoer Signed-off-by: microwavedcola1 * remove mint, not needed Signed-off-by: microwavedcola1 Signed-off-by: microwavedcola1 --- client/src/client.rs | 6 + .../mango-v4/src/instructions/group_create.rs | 13 ++ .../instructions/group_create_msrm_vault.rs | 40 ++++ programs/mango-v4/src/instructions/mod.rs | 2 + .../src/instructions/serum3_place_order.rs | 5 + programs/mango-v4/src/lib.rs | 8 + programs/mango-v4/src/serum3_cpi.rs | 4 + programs/mango-v4/src/state/group.rs | 4 +- .../tests/program_test/mango_client.rs | 9 + .../tests/program_test/mango_setup.rs | 1 + ts/client/src/accounts/group.ts | 3 + ts/client/src/client.ts | 19 ++ ts/client/src/constants/index.ts | 6 + ts/client/src/deployment-scripts/mainnet.ts | 3 +- ts/client/src/mango_v4.ts | 190 +++++++++++++++++- ts/client/src/scripts/devnet-admin.ts | 10 +- ts/client/src/scripts/mb-example1-admin.ts | 10 +- .../src/scripts/mb-liqtest-create-group.ts | 10 +- 18 files changed, 333 insertions(+), 10 deletions(-) create mode 100644 programs/mango-v4/src/instructions/group_create_msrm_vault.rs diff --git a/client/src/client.rs b/client/src/client.rs index 65ca11283..6c0c83722 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -590,6 +590,11 @@ impl MangoClient { Serum3Side::Ask => s3.base.mint_info, }; + let group = account_fetcher_fetch_anchor_account::( + &*self.account_fetcher, + &self.context.group, + )?; + self.program() .request() .instruction(Instruction { @@ -598,6 +603,7 @@ impl MangoClient { let mut ams = anchor_lang::ToAccountMetas::to_account_metas( &mango_v4::accounts::Serum3PlaceOrder { group: self.group(), + msrm_vault: group.msrm_vault, account: self.mango_account_address, open_orders, payer_bank: payer_mint_info.first_bank(), diff --git a/programs/mango-v4/src/instructions/group_create.rs b/programs/mango-v4/src/instructions/group_create.rs index 40f37a3ce..960f08101 100644 --- a/programs/mango-v4/src/instructions/group_create.rs +++ b/programs/mango-v4/src/instructions/group_create.rs @@ -30,6 +30,18 @@ pub struct GroupCreate<'info> { )] pub insurance_vault: Account<'info, TokenAccount>, + pub msrm_mint: Account<'info, Mint>, + + #[account( + init, + seeds = [b"MsrmVault".as_ref(), group.key().as_ref()], + bump, + token::authority = group, + token::mint = msrm_mint, + payer = payer + )] + pub msrm_vault: Account<'info, TokenAccount>, + #[account(mut)] pub payer: Signer<'info>, @@ -54,5 +66,6 @@ pub fn group_create( group.bump = *ctx.bumps.get("group").ok_or(MangoError::SomeError)?; group.testing = testing; group.version = version; + group.msrm_vault = ctx.accounts.msrm_vault.key(); Ok(()) } diff --git a/programs/mango-v4/src/instructions/group_create_msrm_vault.rs b/programs/mango-v4/src/instructions/group_create_msrm_vault.rs new file mode 100644 index 000000000..fd59ea6b1 --- /dev/null +++ b/programs/mango-v4/src/instructions/group_create_msrm_vault.rs @@ -0,0 +1,40 @@ +use anchor_lang::prelude::*; +use anchor_spl::token::{Mint, Token, TokenAccount}; + +use crate::state::*; + +#[derive(Accounts)] +pub struct GroupCreateMsrmVault<'info> { + #[account( + mut, + has_one = admin, + )] + pub group: AccountLoader<'info, Group>, + pub admin: Signer<'info>, + + pub msrm_mint: Account<'info, Mint>, + + #[account( + init, + seeds = [b"MsrmVault".as_ref(), group.key().as_ref()], + bump, + token::authority = group, + token::mint = msrm_mint, + payer = payer + )] + pub msrm_vault: Account<'info, TokenAccount>, + + #[account(mut)] + pub payer: Signer<'info>, + + pub token_program: Program<'info, Token>, + pub system_program: Program<'info, System>, + pub rent: Sysvar<'info, Rent>, +} + +// Ix only exists to add vaults to groups created before msrm vault integration was done +pub fn group_create_msrm_vault(ctx: Context) -> Result<()> { + let mut group = ctx.accounts.group.load_mut()?; + group.msrm_vault = ctx.accounts.msrm_vault.key(); + Ok(()) +} diff --git a/programs/mango-v4/src/instructions/mod.rs b/programs/mango-v4/src/instructions/mod.rs index a62bd0d73..bb0797b23 100644 --- a/programs/mango-v4/src/instructions/mod.rs +++ b/programs/mango-v4/src/instructions/mod.rs @@ -9,6 +9,7 @@ pub use compute_account_data::*; pub use flash_loan::*; pub use group_close::*; pub use group_create::*; +pub use group_create_msrm_vault::*; pub use group_edit::*; pub use health_region::*; pub use perp_cancel_all_orders::*; @@ -61,6 +62,7 @@ mod compute_account_data; mod flash_loan; mod group_close; mod group_create; +mod group_create_msrm_vault; mod group_edit; mod health_region; mod perp_cancel_all_orders; diff --git a/programs/mango-v4/src/instructions/serum3_place_order.rs b/programs/mango-v4/src/instructions/serum3_place_order.rs index 953c4e2ee..fcf0d106b 100644 --- a/programs/mango-v4/src/instructions/serum3_place_order.rs +++ b/programs/mango-v4/src/instructions/serum3_place_order.rs @@ -133,7 +133,11 @@ pub enum Serum3Side { #[derive(Accounts)] pub struct Serum3PlaceOrder<'info> { + #[account( + has_one = msrm_vault + )] pub group: AccountLoader<'info, Group>, + pub msrm_vault: Account<'info, TokenAccount>, #[account( mut, @@ -491,6 +495,7 @@ fn cpi_place_order(ctx: &Serum3PlaceOrder, order: NewOrderInstructionV3) -> Resu open_orders: ctx.open_orders.to_account_info(), order_payer_token_account: ctx.payer_vault.to_account_info(), user_authority: ctx.group.to_account_info(), + msrm_vault: ctx.msrm_vault.to_account_info(), } .call(&group, order) } diff --git a/programs/mango-v4/src/lib.rs b/programs/mango-v4/src/lib.rs index a4344d02d..edea3a79d 100644 --- a/programs/mango-v4/src/lib.rs +++ b/programs/mango-v4/src/lib.rs @@ -40,6 +40,10 @@ pub mod mango_v4 { instructions::group_create(ctx, group_num, testing, version) } + pub fn group_create_msrm_vault(ctx: Context) -> Result<()> { + instructions::group_create_msrm_vault(ctx) + } + pub fn group_edit( ctx: Context, admin_opt: Option, @@ -227,6 +231,10 @@ pub mod mango_v4 { instructions::token_withdraw(ctx, amount, allow_borrow) } + // TODO + // pub fn token_deposit_msrm + // pub fn token_withdraw_msrm + pub fn flash_loan_begin<'key, 'accounts, 'remaining, 'info>( ctx: Context<'key, 'accounts, 'remaining, 'info, FlashLoanBegin<'info>>, loan_amounts: Vec, diff --git a/programs/mango-v4/src/serum3_cpi.rs b/programs/mango-v4/src/serum3_cpi.rs index b8ef5baf6..8820942e9 100644 --- a/programs/mango-v4/src/serum3_cpi.rs +++ b/programs/mango-v4/src/serum3_cpi.rs @@ -298,6 +298,8 @@ pub struct PlaceOrder<'info> { /// must cover the open_orders and the order_payer_token_account /// CHECK: cpi pub user_authority: AccountInfo<'info>, + /// CHECK: cpi + pub msrm_vault: AccountInfo<'info>, } impl<'a> PlaceOrder<'a> { @@ -323,6 +325,7 @@ impl<'a> PlaceOrder<'a> { AccountMeta::new(*self.quote_vault.key, false), AccountMeta::new_readonly(*self.token_program.key, false), AccountMeta::new_readonly(*self.user_authority.key, false), + AccountMeta::new_readonly(*self.msrm_vault.key, false), ], }; let account_infos = [ @@ -339,6 +342,7 @@ impl<'a> PlaceOrder<'a> { self.quote_vault, self.token_program, self.user_authority, + self.msrm_vault, ]; let seeds = group_seeds!(group); diff --git a/programs/mango-v4/src/state/group.rs b/programs/mango-v4/src/state/group.rs index aee51f8d8..e69ac9b4c 100644 --- a/programs/mango-v4/src/state/group.rs +++ b/programs/mango-v4/src/state/group.rs @@ -35,7 +35,9 @@ pub struct Group { pub address_lookup_tables: [Pubkey; 20], - pub reserved: [u8; 1920], + pub msrm_vault: Pubkey, + + pub reserved: [u8; 1888], } const_assert_eq!( size_of::(), diff --git a/programs/mango-v4/tests/program_test/mango_client.rs b/programs/mango-v4/tests/program_test/mango_client.rs index 37e81fd15..065b266e8 100644 --- a/programs/mango-v4/tests/program_test/mango_client.rs +++ b/programs/mango-v4/tests/program_test/mango_client.rs @@ -1119,6 +1119,7 @@ pub struct GroupCreateInstruction { pub creator: TestKeypair, pub payer: TestKeypair, pub insurance_mint: Pubkey, + pub msrm_mint: Pubkey, } #[async_trait::async_trait(?Send)] impl ClientInstruction for GroupCreateInstruction { @@ -1151,11 +1152,16 @@ impl ClientInstruction for GroupCreateInstruction { ) .0; + let msrm_vault = + Pubkey::find_program_address(&[b"MsrmVault".as_ref(), group.as_ref()], &program_id).0; + let accounts = Self::Accounts { group, creator: self.creator.pubkey(), insurance_mint: self.insurance_mint, insurance_vault, + msrm_mint: self.msrm_mint, + msrm_vault, payer: self.payer.pubkey(), token_program: Token::id(), system_program: System::id(), @@ -1712,8 +1718,11 @@ impl ClientInstruction for Serum3PlaceOrderInstruction { Serum3Side::Ask => (base_info.first_bank(), base_info.first_vault()), }; + let group: Group = account_loader.load(&account.fixed.group).await.unwrap(); + let accounts = Self::Accounts { group: account.fixed.group, + msrm_vault: group.msrm_vault, account: self.account, open_orders, payer_bank, diff --git a/programs/mango-v4/tests/program_test/mango_setup.rs b/programs/mango-v4/tests/program_test/mango_setup.rs index 3cc8f49a9..88b34e97d 100644 --- a/programs/mango-v4/tests/program_test/mango_setup.rs +++ b/programs/mango-v4/tests/program_test/mango_setup.rs @@ -45,6 +45,7 @@ impl<'a> GroupWithTokensConfig { creator: admin, payer, insurance_mint: mints[0].pubkey, + msrm_mint: mints[0].pubkey, }, ) .await diff --git a/ts/client/src/accounts/group.ts b/ts/client/src/accounts/group.ts index 9ce60355a..45f4c734e 100644 --- a/ts/client/src/accounts/group.ts +++ b/ts/client/src/accounts/group.ts @@ -40,6 +40,7 @@ export class Group { testing: number; version: number; addressLookupTables: PublicKey[]; + msrmVault: PublicKey; }, ): Group { return new Group( @@ -54,6 +55,7 @@ export class Group { obj.version, obj.addressLookupTables, [], // addressLookupTablesList + obj.msrmVault, new Map(), // banksMapByName new Map(), // banksMapByMint new Map(), // banksMapByTokenIndex @@ -81,6 +83,7 @@ export class Group { public version: number, public addressLookupTables: PublicKey[], public addressLookupTablesList: AddressLookupTableAccount[], + public msrmVault: PublicKey, public banksMapByName: Map, public banksMapByMint: Map, public banksMapByTokenIndex: Map, diff --git a/ts/client/src/client.ts b/ts/client/src/client.ts index 8f62d4fc1..c4c28fbe8 100644 --- a/ts/client/src/client.ts +++ b/ts/client/src/client.ts @@ -102,6 +102,7 @@ export class MangoClient { testing: boolean, version: number, insuranceMintPk: PublicKey, + msrmMintPk: PublicKey, ): Promise { const adminPk = (this.program.provider as AnchorProvider).wallet.publicKey; return await this.program.methods @@ -110,6 +111,23 @@ export class MangoClient { creator: adminPk, payer: adminPk, insuranceMint: insuranceMintPk, + msrmMint: msrmMintPk, + }) + .rpc(); + } + + public async groupCreateMsrmVault( + group: Group, + msrmMintPk: PublicKey, + ): Promise { + const adminPk = (this.program.provider as AnchorProvider).wallet.publicKey; + return await this.program.methods + .groupCreateMsrmVault() + .accounts({ + group: group.publicKey, + admin: adminPk, + msrmMint: msrmMintPk, + payer: adminPk, }) .rpc(); } @@ -1188,6 +1206,7 @@ export class MangoClient { ) .accounts({ group: group.publicKey, + msrmVault: group.msrmVault, account: mangoAccount.publicKey, owner: (this.program.provider as AnchorProvider).wallet.publicKey, openOrders: mangoAccount.getSerum3Account(serum3Market.marketIndex) diff --git a/ts/client/src/constants/index.ts b/ts/client/src/constants/index.ts index d517f4fc3..7faea69fc 100644 --- a/ts/client/src/constants/index.ts +++ b/ts/client/src/constants/index.ts @@ -11,3 +11,9 @@ export const MANGO_V4_ID = { devnet: new PublicKey('m43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD'), 'mainnet-beta': new PublicKey('m43thNJ58XCjL798ZSq6JGAG1BnWskhdq5or6kcnfsD'), }; + +export const MSRM_MINTS = { + testnet: new PublicKey('3Ho7PN3bYv9bp1JDErBD2FxsRepPkL88vju3oDX9c3Ez'), + devnet: new PublicKey('8DJBo4bF4mHNxobjdax3BL9RMh5o71Jf8UiKsf5C5eVH'), + 'mainnet-beta': new PublicKey('MSRMcoVyrFxnSgo5uXwone5SKcGhT1KEJMFEkMEWf9L'), +}; diff --git a/ts/client/src/deployment-scripts/mainnet.ts b/ts/client/src/deployment-scripts/mainnet.ts index 2271c97c8..4557d6878 100644 --- a/ts/client/src/deployment-scripts/mainnet.ts +++ b/ts/client/src/deployment-scripts/mainnet.ts @@ -2,7 +2,7 @@ import { AnchorProvider, Wallet } from '@project-serum/anchor'; import { Connection, Keypair, PublicKey } from '@solana/web3.js'; import fs from 'fs'; import { MangoClient } from '../client'; -import { MANGO_V4_ID } from '../constants'; +import { MANGO_V4_ID, MSRM_MINTS } from '../constants'; const GROUP_NUM = Number(process.env.GROUP_NUM || 0); @@ -62,6 +62,7 @@ async function createGroup() { true /* with intention */, 0 /* since spot and perp features are not finished */, insuranceMint, + MSRM_MINTS['mainnet-beta'], ); const group = await client.getGroupForCreator(admin.publicKey, GROUP_NUM); console.log(`...registered group ${group.publicKey}`); diff --git a/ts/client/src/mango_v4.ts b/ts/client/src/mango_v4.ts index 2d394247a..af2529c78 100644 --- a/ts/client/src/mango_v4.ts +++ b/ts/client/src/mango_v4.ts @@ -58,6 +58,30 @@ export type MangoV4 = { ] } }, + { + "name": "msrmMint", + "isMut": false, + "isSigner": false + }, + { + "name": "msrmVault", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "MsrmVault" + }, + { + "kind": "account", + "type": "publicKey", + "path": "group" + } + ] + } + }, { "name": "payer", "isMut": true, @@ -94,6 +118,66 @@ export type MangoV4 = { } ] }, + { + "name": "groupCreateMsrmVault", + "accounts": [ + { + "name": "group", + "isMut": true, + "isSigner": false + }, + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "msrmMint", + "isMut": false, + "isSigner": false + }, + { + "name": "msrmVault", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "MsrmVault" + }, + { + "kind": "account", + "type": "publicKey", + "path": "group" + } + ] + } + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, { "name": "groupEdit", "accounts": [ @@ -1603,6 +1687,11 @@ export type MangoV4 = { "isMut": false, "isSigner": false }, + { + "name": "msrmVault", + "isMut": false, + "isSigner": false + }, { "name": "account", "isMut": true, @@ -3593,12 +3682,16 @@ export type MangoV4 = { ] } }, + { + "name": "msrmVault", + "type": "publicKey" + }, { "name": "reserved", "type": { "array": [ "u8", - 1920 + 1888 ] } } @@ -6891,6 +6984,30 @@ export const IDL: MangoV4 = { ] } }, + { + "name": "msrmMint", + "isMut": false, + "isSigner": false + }, + { + "name": "msrmVault", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "MsrmVault" + }, + { + "kind": "account", + "type": "publicKey", + "path": "group" + } + ] + } + }, { "name": "payer", "isMut": true, @@ -6927,6 +7044,66 @@ export const IDL: MangoV4 = { } ] }, + { + "name": "groupCreateMsrmVault", + "accounts": [ + { + "name": "group", + "isMut": true, + "isSigner": false + }, + { + "name": "admin", + "isMut": false, + "isSigner": true + }, + { + "name": "msrmMint", + "isMut": false, + "isSigner": false + }, + { + "name": "msrmVault", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "MsrmVault" + }, + { + "kind": "account", + "type": "publicKey", + "path": "group" + } + ] + } + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, { "name": "groupEdit", "accounts": [ @@ -8436,6 +8613,11 @@ export const IDL: MangoV4 = { "isMut": false, "isSigner": false }, + { + "name": "msrmVault", + "isMut": false, + "isSigner": false + }, { "name": "account", "isMut": true, @@ -10426,12 +10608,16 @@ export const IDL: MangoV4 = { ] } }, + { + "name": "msrmVault", + "type": "publicKey" + }, { "name": "reserved", "type": { "array": [ "u8", - 1920 + 1888 ] } } diff --git a/ts/client/src/scripts/devnet-admin.ts b/ts/client/src/scripts/devnet-admin.ts index 59afde942..bd7a42ef8 100644 --- a/ts/client/src/scripts/devnet-admin.ts +++ b/ts/client/src/scripts/devnet-admin.ts @@ -7,7 +7,7 @@ import { } from '@solana/web3.js'; import fs from 'fs'; import { MangoClient } from '../client'; -import { MANGO_V4_ID } from '../constants'; +import { MANGO_V4_ID, MSRM_MINTS } from '../constants'; import { buildVersionedTx } from '../utils'; // @@ -74,7 +74,13 @@ async function main() { console.log(`Creating Group...`); const insuranceMint = new PublicKey(DEVNET_MINTS.get('USDC')!); try { - await client.groupCreate(GROUP_NUM, true, 0, insuranceMint); + await client.groupCreate( + GROUP_NUM, + true, + 0, + insuranceMint, + MSRM_MINTS['devnet'], + ); } catch (error) { console.log(error); } diff --git a/ts/client/src/scripts/mb-example1-admin.ts b/ts/client/src/scripts/mb-example1-admin.ts index a932531f0..2178e68f9 100644 --- a/ts/client/src/scripts/mb-example1-admin.ts +++ b/ts/client/src/scripts/mb-example1-admin.ts @@ -14,7 +14,7 @@ import { Serum3Side, } from '../accounts/serum3'; import { MangoClient } from '../client'; -import { MANGO_V4_ID } from '../constants'; +import { MANGO_V4_ID, MSRM_MINTS } from '../constants'; import { buildVersionedTx } from '../utils'; const MAINNET_MINTS = new Map([ @@ -110,7 +110,13 @@ async function createGroup() { console.log(`Creating Group...`); const insuranceMint = new PublicKey(MAINNET_MINTS.get('USDC')!); - await client.groupCreate(2, true, 0, insuranceMint); + await client.groupCreate( + 2, + true, + 0, + insuranceMint, + MSRM_MINTS['mainnet-beta'], + ); const group = await client.getGroupForCreator(admin.publicKey, 2); console.log(`...registered group ${group.publicKey}`); } diff --git a/ts/client/src/scripts/mb-liqtest-create-group.ts b/ts/client/src/scripts/mb-liqtest-create-group.ts index e59ddeb4b..658519476 100644 --- a/ts/client/src/scripts/mb-liqtest-create-group.ts +++ b/ts/client/src/scripts/mb-liqtest-create-group.ts @@ -2,7 +2,7 @@ import { AnchorProvider, Wallet } from '@project-serum/anchor'; import { Connection, Keypair, PublicKey } from '@solana/web3.js'; import fs from 'fs'; import { MangoClient } from '../client'; -import { MANGO_V4_ID } from '../constants'; +import { MANGO_V4_ID, MSRM_MINTS } from '../constants'; // // Script which depoys a new mango group, and registers 3 tokens @@ -60,7 +60,13 @@ async function main() { console.log(`Creating Group...`); try { const insuranceMint = new PublicKey(MAINNET_MINTS.get('USDC')!); - await client.groupCreate(GROUP_NUM, true, 0, insuranceMint); + await client.groupCreate( + GROUP_NUM, + true, + 0, + insuranceMint, + MSRM_MINTS['mainnet-beta'], + ); } catch (error) { console.log(error); }