From 7836e68f6841d1733c329dcec3dd868937c9fd77 Mon Sep 17 00:00:00 2001 From: Christian Krueger Date: Thu, 24 Oct 2024 15:05:13 -0600 Subject: [PATCH 01/11] wrong direction maybe --- cli/src/vault_handler.rs | 2 +- .../instructions/initializeVault.ts | 72 ++++++--- .../instructions/initialize_vault.rs | 150 ++++++++++++++---- idl/jito_vault.json | 12 +- .../tests/fixtures/vault_client.rs | 29 +++- vault_program/src/initialize_vault.rs | 49 +++++- vault_sdk/src/instruction.rs | 12 +- vault_sdk/src/sdk.rs | 8 +- 8 files changed, 264 insertions(+), 70 deletions(-) diff --git a/cli/src/vault_handler.rs b/cli/src/vault_handler.rs index 02dcdb3e..f479282c 100644 --- a/cli/src/vault_handler.rs +++ b/cli/src/vault_handler.rs @@ -162,7 +162,7 @@ impl VaultCliHandler { .config(Config::find_program_address(&self.vault_program_id).0) .vault(vault) .vrt_mint(vrt_mint.pubkey()) - .token_mint(token_mint) + .st_mint(token_mint) .admin(keypair.pubkey()) .base(base.pubkey()) .deposit_fee_bps(deposit_fee_bps) diff --git a/clients/js/vault_client/instructions/initializeVault.ts b/clients/js/vault_client/instructions/initializeVault.ts index 9e389cf9..5d7e4bc0 100644 --- a/clients/js/vault_client/instructions/initializeVault.ts +++ b/clients/js/vault_client/instructions/initializeVault.ts @@ -44,7 +44,9 @@ export type InitializeVaultInstruction< TAccountConfig extends string | IAccountMeta = string, TAccountVault extends string | IAccountMeta = string, TAccountVrtMint extends string | IAccountMeta = string, - TAccountTokenMint extends string | IAccountMeta = string, + TAccountStMint extends string | IAccountMeta = string, + TAccountAdminStTokenAccount extends string | IAccountMeta = string, + TAccountVaultStTokenAccount extends string | IAccountMeta = string, TAccountAdmin extends string | IAccountMeta = string, TAccountBase extends string | IAccountMeta = string, TAccountSystemProgram extends @@ -68,9 +70,15 @@ export type InitializeVaultInstruction< ? WritableSignerAccount & IAccountSignerMeta : TAccountVrtMint, - TAccountTokenMint extends string - ? ReadonlyAccount - : TAccountTokenMint, + TAccountStMint extends string + ? ReadonlyAccount + : TAccountStMint, + TAccountAdminStTokenAccount extends string + ? WritableAccount + : TAccountAdminStTokenAccount, + TAccountVaultStTokenAccount extends string + ? WritableAccount + : TAccountVaultStTokenAccount, TAccountAdmin extends string ? WritableSignerAccount & IAccountSignerMeta @@ -140,7 +148,9 @@ export type InitializeVaultInput< TAccountConfig extends string = string, TAccountVault extends string = string, TAccountVrtMint extends string = string, - TAccountTokenMint extends string = string, + TAccountStMint extends string = string, + TAccountAdminStTokenAccount extends string = string, + TAccountVaultStTokenAccount extends string = string, TAccountAdmin extends string = string, TAccountBase extends string = string, TAccountSystemProgram extends string = string, @@ -149,7 +159,9 @@ export type InitializeVaultInput< config: Address; vault: Address; vrtMint: TransactionSigner; - tokenMint: Address; + stMint: Address; + adminStTokenAccount: Address; + vaultStTokenAccount: Address; admin: TransactionSigner; base: TransactionSigner; systemProgram?: Address; @@ -164,7 +176,9 @@ export function getInitializeVaultInstruction< TAccountConfig extends string, TAccountVault extends string, TAccountVrtMint extends string, - TAccountTokenMint extends string, + TAccountStMint extends string, + TAccountAdminStTokenAccount extends string, + TAccountVaultStTokenAccount extends string, TAccountAdmin extends string, TAccountBase extends string, TAccountSystemProgram extends string, @@ -174,7 +188,9 @@ export function getInitializeVaultInstruction< TAccountConfig, TAccountVault, TAccountVrtMint, - TAccountTokenMint, + TAccountStMint, + TAccountAdminStTokenAccount, + TAccountVaultStTokenAccount, TAccountAdmin, TAccountBase, TAccountSystemProgram, @@ -185,7 +201,9 @@ export function getInitializeVaultInstruction< TAccountConfig, TAccountVault, TAccountVrtMint, - TAccountTokenMint, + TAccountStMint, + TAccountAdminStTokenAccount, + TAccountVaultStTokenAccount, TAccountAdmin, TAccountBase, TAccountSystemProgram, @@ -199,7 +217,15 @@ export function getInitializeVaultInstruction< config: { value: input.config ?? null, isWritable: true }, vault: { value: input.vault ?? null, isWritable: true }, vrtMint: { value: input.vrtMint ?? null, isWritable: true }, - tokenMint: { value: input.tokenMint ?? null, isWritable: false }, + stMint: { value: input.stMint ?? null, isWritable: false }, + adminStTokenAccount: { + value: input.adminStTokenAccount ?? null, + isWritable: true, + }, + vaultStTokenAccount: { + value: input.vaultStTokenAccount ?? null, + isWritable: true, + }, admin: { value: input.admin ?? null, isWritable: true }, base: { value: input.base ?? null, isWritable: false }, systemProgram: { value: input.systemProgram ?? null, isWritable: false }, @@ -229,7 +255,9 @@ export function getInitializeVaultInstruction< getAccountMeta(accounts.config), getAccountMeta(accounts.vault), getAccountMeta(accounts.vrtMint), - getAccountMeta(accounts.tokenMint), + getAccountMeta(accounts.stMint), + getAccountMeta(accounts.adminStTokenAccount), + getAccountMeta(accounts.vaultStTokenAccount), getAccountMeta(accounts.admin), getAccountMeta(accounts.base), getAccountMeta(accounts.systemProgram), @@ -244,7 +272,9 @@ export function getInitializeVaultInstruction< TAccountConfig, TAccountVault, TAccountVrtMint, - TAccountTokenMint, + TAccountStMint, + TAccountAdminStTokenAccount, + TAccountVaultStTokenAccount, TAccountAdmin, TAccountBase, TAccountSystemProgram, @@ -263,11 +293,13 @@ export type ParsedInitializeVaultInstruction< config: TAccountMetas[0]; vault: TAccountMetas[1]; vrtMint: TAccountMetas[2]; - tokenMint: TAccountMetas[3]; - admin: TAccountMetas[4]; - base: TAccountMetas[5]; - systemProgram: TAccountMetas[6]; - tokenProgram: TAccountMetas[7]; + stMint: TAccountMetas[3]; + adminStTokenAccount: TAccountMetas[4]; + vaultStTokenAccount: TAccountMetas[5]; + admin: TAccountMetas[6]; + base: TAccountMetas[7]; + systemProgram: TAccountMetas[8]; + tokenProgram: TAccountMetas[9]; }; data: InitializeVaultInstructionData; }; @@ -280,7 +312,7 @@ export function parseInitializeVaultInstruction< IInstructionWithAccounts & IInstructionWithData ): ParsedInitializeVaultInstruction { - if (instruction.accounts.length < 8) { + if (instruction.accounts.length < 10) { // TODO: Coded error. throw new Error('Not enough accounts'); } @@ -296,7 +328,9 @@ export function parseInitializeVaultInstruction< config: getNextAccount(), vault: getNextAccount(), vrtMint: getNextAccount(), - tokenMint: getNextAccount(), + stMint: getNextAccount(), + adminStTokenAccount: getNextAccount(), + vaultStTokenAccount: getNextAccount(), admin: getNextAccount(), base: getNextAccount(), systemProgram: getNextAccount(), diff --git a/clients/rust/vault_client/src/generated/instructions/initialize_vault.rs b/clients/rust/vault_client/src/generated/instructions/initialize_vault.rs index 70231f2a..54ee99e4 100644 --- a/clients/rust/vault_client/src/generated/instructions/initialize_vault.rs +++ b/clients/rust/vault_client/src/generated/instructions/initialize_vault.rs @@ -14,7 +14,11 @@ pub struct InitializeVault { pub vrt_mint: solana_program::pubkey::Pubkey, - pub token_mint: solana_program::pubkey::Pubkey, + pub st_mint: solana_program::pubkey::Pubkey, + + pub admin_st_token_account: solana_program::pubkey::Pubkey, + + pub vault_st_token_account: solana_program::pubkey::Pubkey, pub admin: solana_program::pubkey::Pubkey, @@ -38,7 +42,7 @@ impl InitializeVault { args: InitializeVaultInstructionArgs, remaining_accounts: &[solana_program::instruction::AccountMeta], ) -> solana_program::instruction::Instruction { - let mut accounts = Vec::with_capacity(8 + remaining_accounts.len()); + let mut accounts = Vec::with_capacity(10 + remaining_accounts.len()); accounts.push(solana_program::instruction::AccountMeta::new( self.config, false, @@ -51,7 +55,15 @@ impl InitializeVault { true, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.token_mint, + self.st_mint, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.admin_st_token_account, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.vault_st_token_account, false, )); accounts.push(solana_program::instruction::AccountMeta::new( @@ -114,17 +126,21 @@ pub struct InitializeVaultInstructionArgs { /// 0. `[writable]` config /// 1. `[writable]` vault /// 2. `[writable, signer]` vrt_mint -/// 3. `[]` token_mint -/// 4. `[writable, signer]` admin -/// 5. `[signer]` base -/// 6. `[optional]` system_program (default to `11111111111111111111111111111111`) -/// 7. `[optional]` token_program (default to `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`) +/// 3. `[]` st_mint +/// 4. `[writable]` admin_st_token_account +/// 5. `[writable]` vault_st_token_account +/// 6. `[writable, signer]` admin +/// 7. `[signer]` base +/// 8. `[optional]` system_program (default to `11111111111111111111111111111111`) +/// 9. `[optional]` token_program (default to `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`) #[derive(Clone, Debug, Default)] pub struct InitializeVaultBuilder { config: Option, vault: Option, vrt_mint: Option, - token_mint: Option, + st_mint: Option, + admin_st_token_account: Option, + vault_st_token_account: Option, admin: Option, base: Option, system_program: Option, @@ -156,8 +172,24 @@ impl InitializeVaultBuilder { self } #[inline(always)] - pub fn token_mint(&mut self, token_mint: solana_program::pubkey::Pubkey) -> &mut Self { - self.token_mint = Some(token_mint); + pub fn st_mint(&mut self, st_mint: solana_program::pubkey::Pubkey) -> &mut Self { + self.st_mint = Some(st_mint); + self + } + #[inline(always)] + pub fn admin_st_token_account( + &mut self, + admin_st_token_account: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.admin_st_token_account = Some(admin_st_token_account); + self + } + #[inline(always)] + pub fn vault_st_token_account( + &mut self, + vault_st_token_account: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.vault_st_token_account = Some(vault_st_token_account); self } #[inline(always)] @@ -226,7 +258,13 @@ impl InitializeVaultBuilder { config: self.config.expect("config is not set"), vault: self.vault.expect("vault is not set"), vrt_mint: self.vrt_mint.expect("vrt_mint is not set"), - token_mint: self.token_mint.expect("token_mint is not set"), + st_mint: self.st_mint.expect("st_mint is not set"), + admin_st_token_account: self + .admin_st_token_account + .expect("admin_st_token_account is not set"), + vault_st_token_account: self + .vault_st_token_account + .expect("vault_st_token_account is not set"), admin: self.admin.expect("admin is not set"), base: self.base.expect("base is not set"), system_program: self @@ -264,7 +302,11 @@ pub struct InitializeVaultCpiAccounts<'a, 'b> { pub vrt_mint: &'b solana_program::account_info::AccountInfo<'a>, - pub token_mint: &'b solana_program::account_info::AccountInfo<'a>, + pub st_mint: &'b solana_program::account_info::AccountInfo<'a>, + + pub admin_st_token_account: &'b solana_program::account_info::AccountInfo<'a>, + + pub vault_st_token_account: &'b solana_program::account_info::AccountInfo<'a>, pub admin: &'b solana_program::account_info::AccountInfo<'a>, @@ -286,7 +328,11 @@ pub struct InitializeVaultCpi<'a, 'b> { pub vrt_mint: &'b solana_program::account_info::AccountInfo<'a>, - pub token_mint: &'b solana_program::account_info::AccountInfo<'a>, + pub st_mint: &'b solana_program::account_info::AccountInfo<'a>, + + pub admin_st_token_account: &'b solana_program::account_info::AccountInfo<'a>, + + pub vault_st_token_account: &'b solana_program::account_info::AccountInfo<'a>, pub admin: &'b solana_program::account_info::AccountInfo<'a>, @@ -310,7 +356,9 @@ impl<'a, 'b> InitializeVaultCpi<'a, 'b> { config: accounts.config, vault: accounts.vault, vrt_mint: accounts.vrt_mint, - token_mint: accounts.token_mint, + st_mint: accounts.st_mint, + admin_st_token_account: accounts.admin_st_token_account, + vault_st_token_account: accounts.vault_st_token_account, admin: accounts.admin, base: accounts.base, system_program: accounts.system_program, @@ -351,7 +399,7 @@ impl<'a, 'b> InitializeVaultCpi<'a, 'b> { bool, )], ) -> solana_program::entrypoint::ProgramResult { - let mut accounts = Vec::with_capacity(8 + remaining_accounts.len()); + let mut accounts = Vec::with_capacity(10 + remaining_accounts.len()); accounts.push(solana_program::instruction::AccountMeta::new( *self.config.key, false, @@ -365,7 +413,15 @@ impl<'a, 'b> InitializeVaultCpi<'a, 'b> { true, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.token_mint.key, + *self.st_mint.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.admin_st_token_account.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.vault_st_token_account.key, false, )); accounts.push(solana_program::instruction::AccountMeta::new( @@ -400,12 +456,14 @@ impl<'a, 'b> InitializeVaultCpi<'a, 'b> { accounts, data, }; - let mut account_infos = Vec::with_capacity(8 + 1 + remaining_accounts.len()); + let mut account_infos = Vec::with_capacity(10 + 1 + remaining_accounts.len()); account_infos.push(self.__program.clone()); account_infos.push(self.config.clone()); account_infos.push(self.vault.clone()); account_infos.push(self.vrt_mint.clone()); - account_infos.push(self.token_mint.clone()); + account_infos.push(self.st_mint.clone()); + account_infos.push(self.admin_st_token_account.clone()); + account_infos.push(self.vault_st_token_account.clone()); account_infos.push(self.admin.clone()); account_infos.push(self.base.clone()); account_infos.push(self.system_program.clone()); @@ -429,11 +487,13 @@ impl<'a, 'b> InitializeVaultCpi<'a, 'b> { /// 0. `[writable]` config /// 1. `[writable]` vault /// 2. `[writable, signer]` vrt_mint -/// 3. `[]` token_mint -/// 4. `[writable, signer]` admin -/// 5. `[signer]` base -/// 6. `[]` system_program -/// 7. `[]` token_program +/// 3. `[]` st_mint +/// 4. `[writable]` admin_st_token_account +/// 5. `[writable]` vault_st_token_account +/// 6. `[writable, signer]` admin +/// 7. `[signer]` base +/// 8. `[]` system_program +/// 9. `[]` token_program #[derive(Clone, Debug)] pub struct InitializeVaultCpiBuilder<'a, 'b> { instruction: Box>, @@ -446,7 +506,9 @@ impl<'a, 'b> InitializeVaultCpiBuilder<'a, 'b> { config: None, vault: None, vrt_mint: None, - token_mint: None, + st_mint: None, + admin_st_token_account: None, + vault_st_token_account: None, admin: None, base: None, system_program: None, @@ -481,11 +543,27 @@ impl<'a, 'b> InitializeVaultCpiBuilder<'a, 'b> { self } #[inline(always)] - pub fn token_mint( + pub fn st_mint( &mut self, - token_mint: &'b solana_program::account_info::AccountInfo<'a>, + st_mint: &'b solana_program::account_info::AccountInfo<'a>, ) -> &mut Self { - self.instruction.token_mint = Some(token_mint); + self.instruction.st_mint = Some(st_mint); + self + } + #[inline(always)] + pub fn admin_st_token_account( + &mut self, + admin_st_token_account: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.admin_st_token_account = Some(admin_st_token_account); + self + } + #[inline(always)] + pub fn vault_st_token_account( + &mut self, + vault_st_token_account: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.vault_st_token_account = Some(vault_st_token_account); self } #[inline(always)] @@ -606,7 +684,17 @@ impl<'a, 'b> InitializeVaultCpiBuilder<'a, 'b> { vrt_mint: self.instruction.vrt_mint.expect("vrt_mint is not set"), - token_mint: self.instruction.token_mint.expect("token_mint is not set"), + st_mint: self.instruction.st_mint.expect("st_mint is not set"), + + admin_st_token_account: self + .instruction + .admin_st_token_account + .expect("admin_st_token_account is not set"), + + vault_st_token_account: self + .instruction + .vault_st_token_account + .expect("vault_st_token_account is not set"), admin: self.instruction.admin.expect("admin is not set"), @@ -636,7 +724,9 @@ struct InitializeVaultCpiBuilderInstruction<'a, 'b> { config: Option<&'b solana_program::account_info::AccountInfo<'a>>, vault: Option<&'b solana_program::account_info::AccountInfo<'a>>, vrt_mint: Option<&'b solana_program::account_info::AccountInfo<'a>>, - token_mint: Option<&'b solana_program::account_info::AccountInfo<'a>>, + st_mint: Option<&'b solana_program::account_info::AccountInfo<'a>>, + admin_st_token_account: Option<&'b solana_program::account_info::AccountInfo<'a>>, + vault_st_token_account: Option<&'b solana_program::account_info::AccountInfo<'a>>, admin: Option<&'b solana_program::account_info::AccountInfo<'a>>, base: Option<&'b solana_program::account_info::AccountInfo<'a>>, system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, diff --git a/idl/jito_vault.json b/idl/jito_vault.json index 5dc61b17..86857ce8 100644 --- a/idl/jito_vault.json +++ b/idl/jito_vault.json @@ -56,10 +56,20 @@ "isSigner": true }, { - "name": "tokenMint", + "name": "stMint", "isMut": false, "isSigner": false }, + { + "name": "adminStTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "vaultStTokenAccount", + "isMut": true, + "isSigner": false + }, { "name": "admin", "isMut": true, diff --git a/integration_tests/tests/fixtures/vault_client.rs b/integration_tests/tests/fixtures/vault_client.rs index e62f957d..99d1ec4d 100644 --- a/integration_tests/tests/fixtures/vault_client.rs +++ b/integration_tests/tests/fixtures/vault_client.rs @@ -297,17 +297,30 @@ impl VaultProgramClient { let vrt_mint = Keypair::new(); let vault_admin = Keypair::new(); - let token_mint = Keypair::new(); + let st_mint = Keypair::new(); self.airdrop(&vault_admin.pubkey(), 100.0).await?; - self.create_token_mint(&token_mint, &spl_token::id()) + self.create_token_mint(&st_mint, &spl_token::id()).await?; + + // Needs to be created before initialize vault + self.create_ata(&st_mint.pubkey(), &vault_pubkey).await?; + self.create_ata(&st_mint.pubkey(), &self.payer.pubkey()) + .await?; + + self.mint_spl_to(&st_mint.pubkey(), &self.payer.pubkey(), 10_000) .await?; + let admin_st_token_account = + get_associated_token_address(&self.payer.pubkey(), &st_mint.pubkey()); + let vault_st_token_account = get_associated_token_address(&vault_pubkey, &st_mint.pubkey()); + self.initialize_vault( &Config::find_program_address(&jito_vault_program::id()).0, &vault_pubkey, &vrt_mint, - &token_mint, + &st_mint, + &admin_st_token_account, + &vault_st_token_account, &vault_admin, &vault_base, deposit_fee_bps, @@ -317,8 +330,6 @@ impl VaultProgramClient { ) .await?; - // for holding the backed asset in the vault - self.create_ata(&token_mint.pubkey(), &vault_pubkey).await?; // for holding fees self.create_ata(&vrt_mint.pubkey(), &vault_admin.pubkey()) .await?; @@ -733,7 +744,9 @@ impl VaultProgramClient { config: &Pubkey, vault: &Pubkey, vrt_mint: &Keypair, - token_mint: &Keypair, + st_mint: &Keypair, + admin_st_token_account: &Pubkey, + vault_st_token_account: &Pubkey, vault_admin: &Keypair, vault_base: &Keypair, deposit_fee_bps: u16, @@ -749,7 +762,9 @@ impl VaultProgramClient { config, vault, &vrt_mint.pubkey(), - &token_mint.pubkey(), + &st_mint.pubkey(), + admin_st_token_account, + vault_st_token_account, &vault_admin.pubkey(), &vault_base.pubkey(), deposit_fee_bps, diff --git a/vault_program/src/initialize_vault.rs b/vault_program/src/initialize_vault.rs index 7691f38a..6da41ac1 100644 --- a/vault_program/src/initialize_vault.rs +++ b/vault_program/src/initialize_vault.rs @@ -4,7 +4,8 @@ use jito_bytemuck::{AccountDeserialize, Discriminator}; use jito_jsm_core::{ create_account, loader::{ - load_signer, load_system_account, load_system_program, load_token_mint, load_token_program, + load_signer, load_system_account, load_system_program, load_token_account, load_token_mint, + load_token_program, }, }; use jito_vault_core::{config::Config, vault::Vault, MAX_FEE_BPS}; @@ -14,7 +15,7 @@ use solana_program::{ program_error::ProgramError, program_pack::Pack, pubkey::Pubkey, rent::Rent, system_instruction, sysvar::Sysvar, }; -use spl_token::state::Mint; +use spl_token::{instruction::transfer, state::Mint}; /// Processes the create instruction: [`crate::VaultInstruction::InitializeVault`] pub fn process_initialize_vault( @@ -25,7 +26,8 @@ pub fn process_initialize_vault( reward_fee_bps: u16, decimals: u8, ) -> ProgramResult { - let [config, vault, vrt_mint, mint, admin, base, system_program, token_program] = accounts + let [config, vault, vrt_mint, st_mint, admin_st_token_account, vault_st_token_account, admin, base, system_program, token_program] = + accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; @@ -33,14 +35,28 @@ pub fn process_initialize_vault( let mut config_data = config.data.borrow_mut(); let config = Config::try_from_slice_unchecked_mut(&mut config_data)?; + const MIN_DEPOSIT: u64 = 10_000; + load_system_account(vault, true)?; load_system_account(vrt_mint, true)?; load_signer(vrt_mint, true)?; - load_token_mint(mint)?; + load_token_mint(st_mint)?; load_signer(admin, true)?; load_signer(base, false)?; load_system_program(system_program)?; load_token_program(token_program)?; + load_token_account( + admin_st_token_account, + admin.key, + st_mint.key, + token_program, + )?; + load_token_account( + vault_st_token_account, + vault.key, + st_mint.key, + token_program, + )?; // The vault account shall be at the canonical PDA let (vault_pubkey, vault_bump, mut vault_seeds) = @@ -90,6 +106,25 @@ pub fn process_initialize_vault( )?; } + // Deposit min ST + { + invoke( + &transfer( + token_program.key, + admin_st_token_account.key, + vault_st_token_account.key, + admin.key, + &[], + MIN_DEPOSIT, + )?, + &[ + vault_st_token_account.clone(), + admin_st_token_account.clone(), + admin.clone(), + ], + )?; + } + let slot = Clock::get()?.slot; // Initialize vault @@ -113,7 +148,7 @@ pub fn process_initialize_vault( *vault = Vault::new( *vrt_mint.key, - *mint.key, + *st_mint.key, *admin.key, config.num_vaults(), *base.key, @@ -123,6 +158,10 @@ pub fn process_initialize_vault( vault_bump, slot, )?; + + // Mint initial VRT supply + vault.set_vrt_supply(MIN_DEPOSIT); + vault.set_tokens_deposited(MIN_DEPOSIT); } config.increment_num_vaults()?; diff --git a/vault_sdk/src/instruction.rs b/vault_sdk/src/instruction.rs index 4ada1d57..0c703196 100644 --- a/vault_sdk/src/instruction.rs +++ b/vault_sdk/src/instruction.rs @@ -18,11 +18,13 @@ pub enum VaultInstruction { #[account(0, writable, name = "config")] #[account(1, writable, name = "vault")] #[account(2, writable, signer, name = "vrt_mint")] - #[account(3, name = "token_mint")] - #[account(4, writable, signer, name = "admin")] - #[account(5, signer, name = "base")] - #[account(6, name = "system_program")] - #[account(7, name = "token_program")] + #[account(3, name = "st_mint")] + #[account(4, writable, name = "admin_st_token_account")] + #[account(5, writable, name = "vault_st_token_account")] + #[account(6, writable, signer, name = "admin")] + #[account(7, signer, name = "base")] + #[account(8, name = "system_program")] + #[account(9, name = "token_program")] InitializeVault { deposit_fee_bps: u16, withdrawal_fee_bps: u16, diff --git a/vault_sdk/src/sdk.rs b/vault_sdk/src/sdk.rs index 574ce055..9cbbdf36 100644 --- a/vault_sdk/src/sdk.rs +++ b/vault_sdk/src/sdk.rs @@ -40,7 +40,9 @@ pub fn initialize_vault( config: &Pubkey, vault: &Pubkey, vrt_mint: &Pubkey, - token_mint: &Pubkey, + st_mint: &Pubkey, + admin_st_token_account: &Pubkey, + vault_st_token_account: &Pubkey, admin: &Pubkey, base: &Pubkey, deposit_fee_bps: u16, @@ -52,7 +54,9 @@ pub fn initialize_vault( AccountMeta::new(*config, false), AccountMeta::new(*vault, false), AccountMeta::new(*vrt_mint, true), - AccountMeta::new_readonly(*token_mint, false), + AccountMeta::new_readonly(*st_mint, false), + AccountMeta::new(*admin_st_token_account, true), + AccountMeta::new(*vault_st_token_account, true), AccountMeta::new(*admin, true), AccountMeta::new_readonly(*base, true), AccountMeta::new_readonly(system_program::id(), false), From b3892fc5f6332f5cd8d2e2df06f50112c08c6075 Mon Sep 17 00:00:00 2001 From: Christian Krueger Date: Thu, 24 Oct 2024 18:28:36 -0600 Subject: [PATCH 02/11] partial solution --- integration_tests/tests/fixtures/vault_client.rs | 6 +++--- integration_tests/tests/vault/initialize_vault.rs | 6 ++++-- vault_sdk/src/sdk.rs | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/integration_tests/tests/fixtures/vault_client.rs b/integration_tests/tests/fixtures/vault_client.rs index 99d1ec4d..e3eaafc4 100644 --- a/integration_tests/tests/fixtures/vault_client.rs +++ b/integration_tests/tests/fixtures/vault_client.rs @@ -304,14 +304,14 @@ impl VaultProgramClient { // Needs to be created before initialize vault self.create_ata(&st_mint.pubkey(), &vault_pubkey).await?; - self.create_ata(&st_mint.pubkey(), &self.payer.pubkey()) + self.create_ata(&st_mint.pubkey(), &vault_admin.pubkey()) .await?; - self.mint_spl_to(&st_mint.pubkey(), &self.payer.pubkey(), 10_000) + self.mint_spl_to(&st_mint.pubkey(), &vault_admin.pubkey(), 10_000) .await?; let admin_st_token_account = - get_associated_token_address(&self.payer.pubkey(), &st_mint.pubkey()); + get_associated_token_address(&vault_admin.pubkey(), &st_mint.pubkey()); let vault_st_token_account = get_associated_token_address(&vault_pubkey, &st_mint.pubkey()); self.initialize_vault( diff --git a/integration_tests/tests/vault/initialize_vault.rs b/integration_tests/tests/vault/initialize_vault.rs index c433454b..812b0a99 100644 --- a/integration_tests/tests/vault/initialize_vault.rs +++ b/integration_tests/tests/vault/initialize_vault.rs @@ -39,8 +39,10 @@ mod tests { assert_eq!(vault.mint_burn_admin, Pubkey::default()); assert_eq!(vault.deposit_capacity(), u64::MAX); assert_eq!(vault.vault_index(), 0); - assert_eq!(vault.vrt_supply(), 0); - assert_eq!(vault.tokens_deposited(), 0); + + // Min initial deposit is 10_000 + assert_eq!(vault.vrt_supply(), 10000); + assert_eq!(vault.tokens_deposited(), 10000); assert_eq!(vault.deposit_fee_bps(), 99); assert_eq!(vault.withdrawal_fee_bps(), 100); assert_eq!(vault.ncn_count(), 0); diff --git a/vault_sdk/src/sdk.rs b/vault_sdk/src/sdk.rs index 9cbbdf36..534e2026 100644 --- a/vault_sdk/src/sdk.rs +++ b/vault_sdk/src/sdk.rs @@ -55,8 +55,8 @@ pub fn initialize_vault( AccountMeta::new(*vault, false), AccountMeta::new(*vrt_mint, true), AccountMeta::new_readonly(*st_mint, false), - AccountMeta::new(*admin_st_token_account, true), - AccountMeta::new(*vault_st_token_account, true), + AccountMeta::new(*admin_st_token_account, false), + AccountMeta::new(*vault_st_token_account, false), AccountMeta::new(*admin, true), AccountMeta::new_readonly(*base, true), AccountMeta::new_readonly(system_program::id(), false), From 3b50e8e4d199e403a6da33922e23e059b29fd732 Mon Sep 17 00:00:00 2001 From: Christian Krueger Date: Thu, 24 Oct 2024 20:23:01 -0600 Subject: [PATCH 03/11] tests passing --- .../tests/vault/add_delegation.rs | 17 +++++-- .../tests/vault/burn_withdrawal_ticket.rs | 49 ++++++++++++++----- .../vault/crank_vault_update_state_tracker.rs | 23 ++++++--- .../tests/vault/initialize_vault.rs | 6 +-- integration_tests/tests/vault/reward_fee.rs | 39 +++++++++++---- integration_tests/tests/vault/slash.rs | 8 ++- .../tests/vault/update_vault_balance.rs | 12 +++-- vault_core/src/vault.rs | 1 + vault_program/src/initialize_vault.rs | 8 ++- 9 files changed, 120 insertions(+), 43 deletions(-) diff --git a/integration_tests/tests/vault/add_delegation.rs b/integration_tests/tests/vault/add_delegation.rs index 8659dcae..83ada921 100644 --- a/integration_tests/tests/vault/add_delegation.rs +++ b/integration_tests/tests/vault/add_delegation.rs @@ -1,5 +1,6 @@ #[cfg(test)] mod tests { + use jito_vault_core::vault::Vault; use jito_vault_sdk::error::VaultError; use solana_sdk::signature::{Keypair, Signer}; @@ -80,8 +81,14 @@ mod tests { vault.delegation_state, vault_operator_delegation.delegation_state ); - assert_eq!(vault.tokens_deposited(), AMOUNT_IN); - assert_eq!(vault.vrt_supply(), AMOUNT_IN); + assert_eq!( + vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + AMOUNT_IN + ); + assert_eq!( + vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + AMOUNT_IN + ); } #[tokio::test] @@ -124,7 +131,11 @@ mod tests { .unwrap(); vault_program_client - .do_add_delegation(&vault_root, &operator_roots[0].operator_pubkey, 50_000) + .do_add_delegation( + &vault_root, + &operator_roots[0].operator_pubkey, + 50_000 + Vault::INITIALIZATION_TOKEN_AMOUNT, + ) .await .unwrap(); diff --git a/integration_tests/tests/vault/burn_withdrawal_ticket.rs b/integration_tests/tests/vault/burn_withdrawal_ticket.rs index 64bef02c..d87e4bf5 100644 --- a/integration_tests/tests/vault/burn_withdrawal_ticket.rs +++ b/integration_tests/tests/vault/burn_withdrawal_ticket.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests { use jito_vault_core::{ - config::Config, delegation_state::DelegationState, + config::Config, delegation_state::DelegationState, vault::Vault, vault_staker_withdrawal_ticket::VaultStakerWithdrawalTicket, }; use jito_vault_sdk::error::VaultError; @@ -299,8 +299,11 @@ mod tests { .get_vault(&vault_root.vault_pubkey) .await .unwrap(); - assert_eq!(vault.tokens_deposited(), 0); - assert_eq!(vault.vrt_supply(), 0); + assert_eq!( + vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + 0 + ); + assert_eq!(vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, 0); assert_eq!(vault.delegation_state, DelegationState::default()); assert_eq!(vault.vrt_enqueued_for_cooldown_amount(), 0); assert_eq!(vault.vrt_ready_to_claim_amount(), 0); @@ -466,8 +469,14 @@ mod tests { .get_vault(&vault_root.vault_pubkey) .await .unwrap(); - assert_eq!(vault.tokens_deposited(), expected_fee); - assert_eq!(vault.vrt_supply(), expected_fee); + assert_eq!( + vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + expected_fee + ); + assert_eq!( + vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + expected_fee + ); assert_eq!(vault.delegation_state, DelegationState::default()); assert_eq!(vault.vrt_enqueued_for_cooldown_amount(), 0); assert_eq!(vault.vrt_ready_to_claim_amount(), 0); @@ -617,8 +626,14 @@ mod tests { .get_vault(&vault_root.vault_pubkey) .await .unwrap(); - assert_eq!(vault.tokens_deposited(), expected_fee); - assert_eq!(vault.vrt_supply(), expected_fee); + assert_eq!( + vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + expected_fee + ); + assert_eq!( + vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + expected_fee + ); assert_eq!(vault.delegation_state, DelegationState::default()); assert_eq!(vault.vrt_enqueued_for_cooldown_amount(), 0); assert_eq!(vault.vrt_ready_to_claim_amount(), 0); @@ -736,8 +751,14 @@ mod tests { .get_vault(&vault_root.vault_pubkey) .await .unwrap(); - assert_eq!(vault.tokens_deposited(), MINT_AMOUNT - AMOUNT_TO_WITHDRAWAL); - assert_eq!(vault.vrt_supply(), MINT_AMOUNT - AMOUNT_TO_WITHDRAWAL); + assert_eq!( + vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + MINT_AMOUNT - AMOUNT_TO_WITHDRAWAL + ); + assert_eq!( + vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + MINT_AMOUNT - AMOUNT_TO_WITHDRAWAL + ); assert_eq!(vault.delegation_state, DelegationState::default()); assert_eq!(vault.vrt_enqueued_for_cooldown_amount(), 0); assert_eq!(vault.vrt_ready_to_claim_amount(), 0); @@ -950,8 +971,14 @@ mod tests { ); // Vault balance state - assert_eq!(vault.tokens_deposited(), expected_remaining_supply); - assert_eq!(vault.vrt_supply(), expected_remaining_supply); + assert_eq!( + vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + expected_remaining_supply + ); + assert_eq!( + vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + expected_remaining_supply + ); // Vault state assert_eq!(vault.delegation_state, DelegationState::default()); diff --git a/integration_tests/tests/vault/crank_vault_update_state_tracker.rs b/integration_tests/tests/vault/crank_vault_update_state_tracker.rs index 9cc87410..90578998 100644 --- a/integration_tests/tests/vault/crank_vault_update_state_tracker.rs +++ b/integration_tests/tests/vault/crank_vault_update_state_tracker.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests { use jito_vault_core::{ - config::Config, delegation_state::DelegationState, + config::Config, delegation_state::DelegationState, vault::Vault, vault_update_state_tracker::VaultUpdateStateTracker, }; use jito_vault_sdk::error::VaultError; @@ -758,8 +758,11 @@ mod tests { .get_vault(&vault_root.vault_pubkey) .await .unwrap(); - assert_eq!(vault.tokens_deposited(), 0); - assert_eq!(vault.vrt_supply(), 0); + assert_eq!( + vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + 0 + ); + assert_eq!(vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, 0); assert_eq!(vault.delegation_state, DelegationState::default()); assert_eq!(vault.vrt_enqueued_for_cooldown_amount(), 0); assert_eq!(vault.vrt_ready_to_claim_amount(), 0); @@ -876,8 +879,11 @@ mod tests { .get_vault(&vault_root.vault_pubkey) .await .unwrap(); - assert_eq!(vault.tokens_deposited(), 0); - assert_eq!(vault.vrt_supply(), 0); + assert_eq!( + vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + 0 + ); + assert_eq!(vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, 0); assert_eq!(vault.delegation_state, DelegationState::default()); assert_eq!(vault.vrt_enqueued_for_cooldown_amount(), 0); assert_eq!(vault.vrt_ready_to_claim_amount(), 0); @@ -994,8 +1000,11 @@ mod tests { .get_vault(&vault_root.vault_pubkey) .await .unwrap(); - assert_eq!(vault.tokens_deposited(), 0); - assert_eq!(vault.vrt_supply(), 0); + assert_eq!( + vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + 0 + ); + assert_eq!(vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, 0); assert_eq!(vault.delegation_state, DelegationState::default()); assert_eq!(vault.vrt_enqueued_for_cooldown_amount(), 0); assert_eq!(vault.vrt_ready_to_claim_amount(), 0); diff --git a/integration_tests/tests/vault/initialize_vault.rs b/integration_tests/tests/vault/initialize_vault.rs index 812b0a99..410907a0 100644 --- a/integration_tests/tests/vault/initialize_vault.rs +++ b/integration_tests/tests/vault/initialize_vault.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod tests { - use jito_vault_core::config::Config; + use jito_vault_core::{config::Config, vault::Vault}; use jito_vault_sdk::error::VaultError; use solana_program::pubkey::Pubkey; use solana_sdk::signature::Signer; @@ -41,8 +41,8 @@ mod tests { assert_eq!(vault.vault_index(), 0); // Min initial deposit is 10_000 - assert_eq!(vault.vrt_supply(), 10000); - assert_eq!(vault.tokens_deposited(), 10000); + assert_eq!(vault.vrt_supply(), Vault::INITIALIZATION_TOKEN_AMOUNT); + assert_eq!(vault.tokens_deposited(), Vault::INITIALIZATION_TOKEN_AMOUNT); assert_eq!(vault.deposit_fee_bps(), 99); assert_eq!(vault.withdrawal_fee_bps(), 100); assert_eq!(vault.ncn_count(), 0); diff --git a/integration_tests/tests/vault/reward_fee.rs b/integration_tests/tests/vault/reward_fee.rs index e3eddfc8..c2f2b6ea 100644 --- a/integration_tests/tests/vault/reward_fee.rs +++ b/integration_tests/tests/vault/reward_fee.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod tests { - use jito_vault_core::config::Config; + use jito_vault_core::{config::Config, vault::Vault}; use solana_sdk::signature::{Keypair, Signer}; use crate::fixtures::fixture::{ConfiguredVault, TestBuilder}; @@ -70,9 +70,15 @@ mod tests { .await .unwrap(); - assert_eq!(MINT_AMOUNT, vault.tokens_deposited()); + assert_eq!( + MINT_AMOUNT, + vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + ); assert_eq!(MINT_AMOUNT / 10, reward_fee_account.amount); - assert_eq!(MINT_AMOUNT / 10, vault.vrt_supply()); + assert_eq!( + MINT_AMOUNT / 10, + vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + ); } #[tokio::test] @@ -140,9 +146,15 @@ mod tests { .await .unwrap(); - assert_eq!(MINT_AMOUNT, vault.tokens_deposited()); + assert_eq!( + MINT_AMOUNT, + vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + ); assert_eq!(MINT_AMOUNT, reward_fee_account.amount); - assert_eq!(MINT_AMOUNT, vault.vrt_supply()); + assert_eq!( + MINT_AMOUNT, + vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + ); } #[tokio::test] @@ -210,9 +222,12 @@ mod tests { .await .unwrap(); - assert_eq!(MINT_AMOUNT, vault.tokens_deposited()); + assert_eq!( + MINT_AMOUNT, + vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT + ); assert_eq!(0, reward_fee_account.amount); - assert_eq!(0, vault.vrt_supply()); + assert_eq!(0, vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT); } #[tokio::test] @@ -300,8 +315,14 @@ mod tests { .await .unwrap(); - assert_eq!(MINT_AMOUNT * 2, vault.tokens_deposited()); + assert_eq!( + MINT_AMOUNT * 2, + vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + ); assert_eq!(MINT_AMOUNT / 10, reward_fee_account.amount); - assert_eq!(MINT_AMOUNT + MINT_AMOUNT / 10, vault.vrt_supply()); + assert_eq!( + MINT_AMOUNT + MINT_AMOUNT / 10, + vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + ); } } diff --git a/integration_tests/tests/vault/slash.rs b/integration_tests/tests/vault/slash.rs index b0a37fa5..596f362c 100644 --- a/integration_tests/tests/vault/slash.rs +++ b/integration_tests/tests/vault/slash.rs @@ -1,7 +1,8 @@ #[cfg(test)] mod tests { use jito_vault_core::{ - config::Config, vault_ncn_slasher_operator_ticket::VaultNcnSlasherOperatorTicket, + config::Config, vault::Vault, + vault_ncn_slasher_operator_ticket::VaultNcnSlasherOperatorTicket, vault_ncn_slasher_ticket::VaultNcnSlasherTicket, }; use jito_vault_sdk::error::VaultError; @@ -135,7 +136,10 @@ mod tests { .get_vault(&vault_root.vault_pubkey) .await .unwrap(); - assert_eq!(vault.tokens_deposited(), MINT_AMOUNT - MAX_SLASH_AMOUNT); + assert_eq!( + vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + MINT_AMOUNT - MAX_SLASH_AMOUNT + ); assert_eq!( vault.delegation_state.total_security().unwrap(), DELEGATION_AMOUNT - MAX_SLASH_AMOUNT diff --git a/integration_tests/tests/vault/update_vault_balance.rs b/integration_tests/tests/vault/update_vault_balance.rs index bbfbccbc..c314fba2 100644 --- a/integration_tests/tests/vault/update_vault_balance.rs +++ b/integration_tests/tests/vault/update_vault_balance.rs @@ -1,7 +1,7 @@ #[cfg(test)] mod tests { use jito_vault_core::{ - config::Config, vault_operator_delegation::VaultOperatorDelegation, + config::Config, vault::Vault, vault_operator_delegation::VaultOperatorDelegation, vault_update_state_tracker::VaultUpdateStateTracker, }; use jito_vault_sdk::error::VaultError; @@ -128,9 +128,15 @@ mod tests { .await .unwrap(); - assert_eq!(vault.tokens_deposited(), MINT_AMOUNT); + assert_eq!( + vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + MINT_AMOUNT + ); assert_eq!(reward_fee_account.amount, MINT_AMOUNT / 10); - assert_eq!(vault.vrt_supply(), MINT_AMOUNT / 10); + assert_eq!( + vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + MINT_AMOUNT / 10 + ); } #[tokio::test] diff --git a/vault_core/src/vault.rs b/vault_core/src/vault.rs index 0305a2ce..6e3780dd 100644 --- a/vault_core/src/vault.rs +++ b/vault_core/src/vault.rs @@ -144,6 +144,7 @@ pub struct Vault { impl Vault { pub const MIN_WITHDRAWAL_SLIPPAGE_BPS: u16 = 50; // 0.5% + pub const INITIALIZATION_TOKEN_AMOUNT: u64 = 10_000; #[allow(clippy::too_many_arguments)] pub fn new( diff --git a/vault_program/src/initialize_vault.rs b/vault_program/src/initialize_vault.rs index 6da41ac1..f0c5f6a1 100644 --- a/vault_program/src/initialize_vault.rs +++ b/vault_program/src/initialize_vault.rs @@ -35,8 +35,6 @@ pub fn process_initialize_vault( let mut config_data = config.data.borrow_mut(); let config = Config::try_from_slice_unchecked_mut(&mut config_data)?; - const MIN_DEPOSIT: u64 = 10_000; - load_system_account(vault, true)?; load_system_account(vrt_mint, true)?; load_signer(vrt_mint, true)?; @@ -115,7 +113,7 @@ pub fn process_initialize_vault( vault_st_token_account.key, admin.key, &[], - MIN_DEPOSIT, + Vault::INITIALIZATION_TOKEN_AMOUNT, )?, &[ vault_st_token_account.clone(), @@ -160,8 +158,8 @@ pub fn process_initialize_vault( )?; // Mint initial VRT supply - vault.set_vrt_supply(MIN_DEPOSIT); - vault.set_tokens_deposited(MIN_DEPOSIT); + vault.set_vrt_supply(Vault::INITIALIZATION_TOKEN_AMOUNT); + vault.set_tokens_deposited(Vault::INITIALIZATION_TOKEN_AMOUNT); } config.increment_num_vaults()?; From 956e88f6f04296bb6559fc2656b2746ed361ea7e Mon Sep 17 00:00:00 2001 From: Coach Chuck <169060940+coachchucksol@users.noreply.github.com> Date: Thu, 24 Oct 2024 15:15:27 -0600 Subject: [PATCH 04/11] VAULT: Remove Slash (#141) Take out slash. I've opted for removal over no-op since we'll learn things as the program goes live. Slash should be designed around that new information. --- .github/workflows/ci.yaml | 2 +- clients/js/vault_client/instructions/index.ts | 1 - clients/js/vault_client/instructions/slash.ts | 417 -------- clients/js/vault_client/programs/jitoVault.ts | 10 +- .../src/generated/instructions/mod.rs | 3 +- .../src/generated/instructions/slash.rs | 952 ------------------ idl/jito_vault.json | 95 -- integration_tests/tests/fixtures/fixture.rs | 6 +- .../tests/fixtures/vault_client.rs | 159 +-- .../tests/vault/burn_withdrawal_ticket.rs | 18 - .../vault/crank_vault_update_state_tracker.rs | 6 - integration_tests/tests/vault/mod.rs | 1 - integration_tests/tests/vault/slash.rs | 301 ------ .../tests/vault/update_vault_balance.rs | 2 - vault_program/src/lib.rs | 10 +- vault_program/src/slash.rs | 272 ----- vault_sdk/src/instruction.rs | 21 +- vault_sdk/src/sdk.rs | 45 - 18 files changed, 10 insertions(+), 2311 deletions(-) delete mode 100644 clients/js/vault_client/instructions/slash.ts delete mode 100644 clients/rust/vault_client/src/generated/instructions/slash.rs delete mode 100644 integration_tests/tests/vault/slash.rs delete mode 100644 vault_program/src/slash.rs diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6969c3e7..0fefcfee 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -108,7 +108,7 @@ jobs: filenames: program.env - uses: actions-rust-lang/setup-rust-toolchain@v1 - name: install solana toolsuite - run: sh -c "$(curl -sSfL https://release.anza.xyz/v1.18.22/install)" + run: sh -c "$(curl -sSfL https://release.solana.com/v1.18.22/install)" - name: add to path run: echo "/home/runner/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH - name: Building programs diff --git a/clients/js/vault_client/instructions/index.ts b/clients/js/vault_client/instructions/index.ts index 0398217c..361e9333 100644 --- a/clients/js/vault_client/instructions/index.ts +++ b/clients/js/vault_client/instructions/index.ts @@ -33,7 +33,6 @@ export * from './setIsPaused'; export * from './setProgramFee'; export * from './setProgramFeeWallet'; export * from './setSecondaryAdmin'; -export * from './slash'; export * from './updateTokenMetadata'; export * from './updateVaultBalance'; export * from './warmupVaultNcnSlasherTicket'; diff --git a/clients/js/vault_client/instructions/slash.ts b/clients/js/vault_client/instructions/slash.ts deleted file mode 100644 index 412a995c..00000000 --- a/clients/js/vault_client/instructions/slash.ts +++ /dev/null @@ -1,417 +0,0 @@ -/** - * This code was AUTOGENERATED using the kinobi library. - * Please DO NOT EDIT THIS FILE, instead use visitors - * to add features, then rerun kinobi to update it. - * - * @see https://github.com/kinobi-so/kinobi - */ - -import { - combineCodec, - getStructDecoder, - getStructEncoder, - getU64Decoder, - getU64Encoder, - getU8Decoder, - getU8Encoder, - transformEncoder, - type Address, - type Codec, - type Decoder, - type Encoder, - type IAccountMeta, - type IAccountSignerMeta, - type IInstruction, - type IInstructionWithAccounts, - type IInstructionWithData, - type ReadonlyAccount, - type ReadonlySignerAccount, - type TransactionSigner, - type WritableAccount, -} from '@solana/web3.js'; -import { JITO_VAULT_PROGRAM_ADDRESS } from '../programs'; -import { getAccountMetaFactory, type ResolvedAccount } from '../shared'; - -export const SLASH_DISCRIMINATOR = 31; - -export function getSlashDiscriminatorBytes() { - return getU8Encoder().encode(SLASH_DISCRIMINATOR); -} - -export type SlashInstruction< - TProgram extends string = typeof JITO_VAULT_PROGRAM_ADDRESS, - TAccountConfig extends string | IAccountMeta = string, - TAccountVault extends string | IAccountMeta = string, - TAccountNcn extends string | IAccountMeta = string, - TAccountOperator extends string | IAccountMeta = string, - TAccountSlasher extends string | IAccountMeta = string, - TAccountNcnOperatorState extends string | IAccountMeta = string, - TAccountNcnVaultTicket extends string | IAccountMeta = string, - TAccountOperatorVaultTicket extends string | IAccountMeta = string, - TAccountVaultNcnTicket extends string | IAccountMeta = string, - TAccountVaultOperatorDelegation extends - | string - | IAccountMeta = string, - TAccountNcnVaultSlasherTicket extends string | IAccountMeta = string, - TAccountVaultNcnSlasherTicket extends string | IAccountMeta = string, - TAccountVaultNcnSlasherOperatorTicket extends - | string - | IAccountMeta = string, - TAccountVaultTokenAccount extends string | IAccountMeta = string, - TAccountSlasherTokenAccount extends string | IAccountMeta = string, - TAccountTokenProgram extends - | string - | IAccountMeta = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', - TRemainingAccounts extends readonly IAccountMeta[] = [], -> = IInstruction & - IInstructionWithData & - IInstructionWithAccounts< - [ - TAccountConfig extends string - ? ReadonlyAccount - : TAccountConfig, - TAccountVault extends string - ? WritableAccount - : TAccountVault, - TAccountNcn extends string ? ReadonlyAccount : TAccountNcn, - TAccountOperator extends string - ? ReadonlyAccount - : TAccountOperator, - TAccountSlasher extends string - ? ReadonlySignerAccount & - IAccountSignerMeta - : TAccountSlasher, - TAccountNcnOperatorState extends string - ? ReadonlyAccount - : TAccountNcnOperatorState, - TAccountNcnVaultTicket extends string - ? ReadonlyAccount - : TAccountNcnVaultTicket, - TAccountOperatorVaultTicket extends string - ? ReadonlyAccount - : TAccountOperatorVaultTicket, - TAccountVaultNcnTicket extends string - ? ReadonlyAccount - : TAccountVaultNcnTicket, - TAccountVaultOperatorDelegation extends string - ? WritableAccount - : TAccountVaultOperatorDelegation, - TAccountNcnVaultSlasherTicket extends string - ? ReadonlyAccount - : TAccountNcnVaultSlasherTicket, - TAccountVaultNcnSlasherTicket extends string - ? ReadonlyAccount - : TAccountVaultNcnSlasherTicket, - TAccountVaultNcnSlasherOperatorTicket extends string - ? WritableAccount - : TAccountVaultNcnSlasherOperatorTicket, - TAccountVaultTokenAccount extends string - ? WritableAccount - : TAccountVaultTokenAccount, - TAccountSlasherTokenAccount extends string - ? ReadonlyAccount - : TAccountSlasherTokenAccount, - TAccountTokenProgram extends string - ? ReadonlyAccount - : TAccountTokenProgram, - ...TRemainingAccounts, - ] - >; - -export type SlashInstructionData = { discriminator: number; amount: bigint }; - -export type SlashInstructionDataArgs = { amount: number | bigint }; - -export function getSlashInstructionDataEncoder(): Encoder { - return transformEncoder( - getStructEncoder([ - ['discriminator', getU8Encoder()], - ['amount', getU64Encoder()], - ]), - (value) => ({ ...value, discriminator: SLASH_DISCRIMINATOR }) - ); -} - -export function getSlashInstructionDataDecoder(): Decoder { - return getStructDecoder([ - ['discriminator', getU8Decoder()], - ['amount', getU64Decoder()], - ]); -} - -export function getSlashInstructionDataCodec(): Codec< - SlashInstructionDataArgs, - SlashInstructionData -> { - return combineCodec( - getSlashInstructionDataEncoder(), - getSlashInstructionDataDecoder() - ); -} - -export type SlashInput< - TAccountConfig extends string = string, - TAccountVault extends string = string, - TAccountNcn extends string = string, - TAccountOperator extends string = string, - TAccountSlasher extends string = string, - TAccountNcnOperatorState extends string = string, - TAccountNcnVaultTicket extends string = string, - TAccountOperatorVaultTicket extends string = string, - TAccountVaultNcnTicket extends string = string, - TAccountVaultOperatorDelegation extends string = string, - TAccountNcnVaultSlasherTicket extends string = string, - TAccountVaultNcnSlasherTicket extends string = string, - TAccountVaultNcnSlasherOperatorTicket extends string = string, - TAccountVaultTokenAccount extends string = string, - TAccountSlasherTokenAccount extends string = string, - TAccountTokenProgram extends string = string, -> = { - config: Address; - vault: Address; - ncn: Address; - operator: Address; - slasher: TransactionSigner; - ncnOperatorState: Address; - ncnVaultTicket: Address; - operatorVaultTicket: Address; - vaultNcnTicket: Address; - vaultOperatorDelegation: Address; - ncnVaultSlasherTicket: Address; - vaultNcnSlasherTicket: Address; - vaultNcnSlasherOperatorTicket: Address; - vaultTokenAccount: Address; - slasherTokenAccount: Address; - tokenProgram?: Address; - amount: SlashInstructionDataArgs['amount']; -}; - -export function getSlashInstruction< - TAccountConfig extends string, - TAccountVault extends string, - TAccountNcn extends string, - TAccountOperator extends string, - TAccountSlasher extends string, - TAccountNcnOperatorState extends string, - TAccountNcnVaultTicket extends string, - TAccountOperatorVaultTicket extends string, - TAccountVaultNcnTicket extends string, - TAccountVaultOperatorDelegation extends string, - TAccountNcnVaultSlasherTicket extends string, - TAccountVaultNcnSlasherTicket extends string, - TAccountVaultNcnSlasherOperatorTicket extends string, - TAccountVaultTokenAccount extends string, - TAccountSlasherTokenAccount extends string, - TAccountTokenProgram extends string, ->( - input: SlashInput< - TAccountConfig, - TAccountVault, - TAccountNcn, - TAccountOperator, - TAccountSlasher, - TAccountNcnOperatorState, - TAccountNcnVaultTicket, - TAccountOperatorVaultTicket, - TAccountVaultNcnTicket, - TAccountVaultOperatorDelegation, - TAccountNcnVaultSlasherTicket, - TAccountVaultNcnSlasherTicket, - TAccountVaultNcnSlasherOperatorTicket, - TAccountVaultTokenAccount, - TAccountSlasherTokenAccount, - TAccountTokenProgram - > -): SlashInstruction< - typeof JITO_VAULT_PROGRAM_ADDRESS, - TAccountConfig, - TAccountVault, - TAccountNcn, - TAccountOperator, - TAccountSlasher, - TAccountNcnOperatorState, - TAccountNcnVaultTicket, - TAccountOperatorVaultTicket, - TAccountVaultNcnTicket, - TAccountVaultOperatorDelegation, - TAccountNcnVaultSlasherTicket, - TAccountVaultNcnSlasherTicket, - TAccountVaultNcnSlasherOperatorTicket, - TAccountVaultTokenAccount, - TAccountSlasherTokenAccount, - TAccountTokenProgram -> { - // Program address. - const programAddress = JITO_VAULT_PROGRAM_ADDRESS; - - // Original accounts. - const originalAccounts = { - config: { value: input.config ?? null, isWritable: false }, - vault: { value: input.vault ?? null, isWritable: true }, - ncn: { value: input.ncn ?? null, isWritable: false }, - operator: { value: input.operator ?? null, isWritable: false }, - slasher: { value: input.slasher ?? null, isWritable: false }, - ncnOperatorState: { - value: input.ncnOperatorState ?? null, - isWritable: false, - }, - ncnVaultTicket: { value: input.ncnVaultTicket ?? null, isWritable: false }, - operatorVaultTicket: { - value: input.operatorVaultTicket ?? null, - isWritable: false, - }, - vaultNcnTicket: { value: input.vaultNcnTicket ?? null, isWritable: false }, - vaultOperatorDelegation: { - value: input.vaultOperatorDelegation ?? null, - isWritable: true, - }, - ncnVaultSlasherTicket: { - value: input.ncnVaultSlasherTicket ?? null, - isWritable: false, - }, - vaultNcnSlasherTicket: { - value: input.vaultNcnSlasherTicket ?? null, - isWritable: false, - }, - vaultNcnSlasherOperatorTicket: { - value: input.vaultNcnSlasherOperatorTicket ?? null, - isWritable: true, - }, - vaultTokenAccount: { - value: input.vaultTokenAccount ?? null, - isWritable: true, - }, - slasherTokenAccount: { - value: input.slasherTokenAccount ?? null, - isWritable: false, - }, - tokenProgram: { value: input.tokenProgram ?? null, isWritable: false }, - }; - const accounts = originalAccounts as Record< - keyof typeof originalAccounts, - ResolvedAccount - >; - - // Original args. - const args = { ...input }; - - // Resolve default values. - if (!accounts.tokenProgram.value) { - accounts.tokenProgram.value = - 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA' as Address<'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'>; - } - - const getAccountMeta = getAccountMetaFactory(programAddress, 'programId'); - const instruction = { - accounts: [ - getAccountMeta(accounts.config), - getAccountMeta(accounts.vault), - getAccountMeta(accounts.ncn), - getAccountMeta(accounts.operator), - getAccountMeta(accounts.slasher), - getAccountMeta(accounts.ncnOperatorState), - getAccountMeta(accounts.ncnVaultTicket), - getAccountMeta(accounts.operatorVaultTicket), - getAccountMeta(accounts.vaultNcnTicket), - getAccountMeta(accounts.vaultOperatorDelegation), - getAccountMeta(accounts.ncnVaultSlasherTicket), - getAccountMeta(accounts.vaultNcnSlasherTicket), - getAccountMeta(accounts.vaultNcnSlasherOperatorTicket), - getAccountMeta(accounts.vaultTokenAccount), - getAccountMeta(accounts.slasherTokenAccount), - getAccountMeta(accounts.tokenProgram), - ], - programAddress, - data: getSlashInstructionDataEncoder().encode( - args as SlashInstructionDataArgs - ), - } as SlashInstruction< - typeof JITO_VAULT_PROGRAM_ADDRESS, - TAccountConfig, - TAccountVault, - TAccountNcn, - TAccountOperator, - TAccountSlasher, - TAccountNcnOperatorState, - TAccountNcnVaultTicket, - TAccountOperatorVaultTicket, - TAccountVaultNcnTicket, - TAccountVaultOperatorDelegation, - TAccountNcnVaultSlasherTicket, - TAccountVaultNcnSlasherTicket, - TAccountVaultNcnSlasherOperatorTicket, - TAccountVaultTokenAccount, - TAccountSlasherTokenAccount, - TAccountTokenProgram - >; - - return instruction; -} - -export type ParsedSlashInstruction< - TProgram extends string = typeof JITO_VAULT_PROGRAM_ADDRESS, - TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[], -> = { - programAddress: Address; - accounts: { - config: TAccountMetas[0]; - vault: TAccountMetas[1]; - ncn: TAccountMetas[2]; - operator: TAccountMetas[3]; - slasher: TAccountMetas[4]; - ncnOperatorState: TAccountMetas[5]; - ncnVaultTicket: TAccountMetas[6]; - operatorVaultTicket: TAccountMetas[7]; - vaultNcnTicket: TAccountMetas[8]; - vaultOperatorDelegation: TAccountMetas[9]; - ncnVaultSlasherTicket: TAccountMetas[10]; - vaultNcnSlasherTicket: TAccountMetas[11]; - vaultNcnSlasherOperatorTicket: TAccountMetas[12]; - vaultTokenAccount: TAccountMetas[13]; - slasherTokenAccount: TAccountMetas[14]; - tokenProgram: TAccountMetas[15]; - }; - data: SlashInstructionData; -}; - -export function parseSlashInstruction< - TProgram extends string, - TAccountMetas extends readonly IAccountMeta[], ->( - instruction: IInstruction & - IInstructionWithAccounts & - IInstructionWithData -): ParsedSlashInstruction { - if (instruction.accounts.length < 16) { - // TODO: Coded error. - throw new Error('Not enough accounts'); - } - let accountIndex = 0; - const getNextAccount = () => { - const accountMeta = instruction.accounts![accountIndex]!; - accountIndex += 1; - return accountMeta; - }; - return { - programAddress: instruction.programAddress, - accounts: { - config: getNextAccount(), - vault: getNextAccount(), - ncn: getNextAccount(), - operator: getNextAccount(), - slasher: getNextAccount(), - ncnOperatorState: getNextAccount(), - ncnVaultTicket: getNextAccount(), - operatorVaultTicket: getNextAccount(), - vaultNcnTicket: getNextAccount(), - vaultOperatorDelegation: getNextAccount(), - ncnVaultSlasherTicket: getNextAccount(), - vaultNcnSlasherTicket: getNextAccount(), - vaultNcnSlasherOperatorTicket: getNextAccount(), - vaultTokenAccount: getNextAccount(), - slasherTokenAccount: getNextAccount(), - tokenProgram: getNextAccount(), - }, - data: getSlashInstructionDataDecoder().decode(instruction.data), - }; -} diff --git a/clients/js/vault_client/programs/jitoVault.ts b/clients/js/vault_client/programs/jitoVault.ts index 89dbab06..2a3a1805 100644 --- a/clients/js/vault_client/programs/jitoVault.ts +++ b/clients/js/vault_client/programs/jitoVault.ts @@ -40,7 +40,6 @@ import { type ParsedSetProgramFeeInstruction, type ParsedSetProgramFeeWalletInstruction, type ParsedSetSecondaryAdminInstruction, - type ParsedSlashInstruction, type ParsedUpdateTokenMetadataInstruction, type ParsedUpdateVaultBalanceInstruction, type ParsedWarmupVaultNcnSlasherTicketInstruction, @@ -93,7 +92,6 @@ export enum JitoVaultInstruction { CloseVaultUpdateStateTracker, CreateTokenMetadata, UpdateTokenMetadata, - Slash, } export function identifyJitoVaultInstruction( @@ -193,9 +191,6 @@ export function identifyJitoVaultInstruction( if (containsBytes(data, getU8Encoder().encode(30), 0)) { return JitoVaultInstruction.UpdateTokenMetadata; } - if (containsBytes(data, getU8Encoder().encode(31), 0)) { - return JitoVaultInstruction.Slash; - } throw new Error( 'The provided instruction could not be identified as a jitoVault instruction.' ); @@ -296,7 +291,4 @@ export type ParsedJitoVaultInstruction< } & ParsedCreateTokenMetadataInstruction) | ({ instructionType: JitoVaultInstruction.UpdateTokenMetadata; - } & ParsedUpdateTokenMetadataInstruction) - | ({ - instructionType: JitoVaultInstruction.Slash; - } & ParsedSlashInstruction); + } & ParsedUpdateTokenMetadataInstruction); diff --git a/clients/rust/vault_client/src/generated/instructions/mod.rs b/clients/rust/vault_client/src/generated/instructions/mod.rs index d90ceddc..40f9b294 100644 --- a/clients/rust/vault_client/src/generated/instructions/mod.rs +++ b/clients/rust/vault_client/src/generated/instructions/mod.rs @@ -31,7 +31,6 @@ pub(crate) mod r#set_is_paused; pub(crate) mod r#set_program_fee; pub(crate) mod r#set_program_fee_wallet; pub(crate) mod r#set_secondary_admin; -pub(crate) mod r#slash; pub(crate) mod r#update_token_metadata; pub(crate) mod r#update_vault_balance; pub(crate) mod r#warmup_vault_ncn_slasher_ticket; @@ -47,7 +46,7 @@ pub use self::{ r#initialize_vault_ncn_ticket::*, r#initialize_vault_operator_delegation::*, r#initialize_vault_update_state_tracker::*, r#initialize_vault_with_mint::*, r#mint_to::*, r#set_admin::*, r#set_deposit_capacity::*, r#set_fees::*, r#set_is_paused::*, - r#set_program_fee::*, r#set_program_fee_wallet::*, r#set_secondary_admin::*, r#slash::*, + r#set_program_fee::*, r#set_program_fee_wallet::*, r#set_secondary_admin::*, r#update_token_metadata::*, r#update_vault_balance::*, r#warmup_vault_ncn_slasher_ticket::*, r#warmup_vault_ncn_ticket::*, }; diff --git a/clients/rust/vault_client/src/generated/instructions/slash.rs b/clients/rust/vault_client/src/generated/instructions/slash.rs deleted file mode 100644 index 4f69141c..00000000 --- a/clients/rust/vault_client/src/generated/instructions/slash.rs +++ /dev/null @@ -1,952 +0,0 @@ -//! This code was AUTOGENERATED using the kinobi library. -//! Please DO NOT EDIT THIS FILE, instead use visitors -//! to add features, then rerun kinobi to update it. -//! -//! - -use borsh::{BorshDeserialize, BorshSerialize}; - -/// Accounts. -pub struct Slash { - pub config: solana_program::pubkey::Pubkey, - - pub vault: solana_program::pubkey::Pubkey, - - pub ncn: solana_program::pubkey::Pubkey, - - pub operator: solana_program::pubkey::Pubkey, - - pub slasher: solana_program::pubkey::Pubkey, - - pub ncn_operator_state: solana_program::pubkey::Pubkey, - - pub ncn_vault_ticket: solana_program::pubkey::Pubkey, - - pub operator_vault_ticket: solana_program::pubkey::Pubkey, - - pub vault_ncn_ticket: solana_program::pubkey::Pubkey, - - pub vault_operator_delegation: solana_program::pubkey::Pubkey, - - pub ncn_vault_slasher_ticket: solana_program::pubkey::Pubkey, - - pub vault_ncn_slasher_ticket: solana_program::pubkey::Pubkey, - - pub vault_ncn_slasher_operator_ticket: solana_program::pubkey::Pubkey, - - pub vault_token_account: solana_program::pubkey::Pubkey, - - pub slasher_token_account: solana_program::pubkey::Pubkey, - - pub token_program: solana_program::pubkey::Pubkey, -} - -impl Slash { - pub fn instruction( - &self, - args: SlashInstructionArgs, - ) -> solana_program::instruction::Instruction { - self.instruction_with_remaining_accounts(args, &[]) - } - #[allow(clippy::vec_init_then_push)] - pub fn instruction_with_remaining_accounts( - &self, - args: SlashInstructionArgs, - remaining_accounts: &[solana_program::instruction::AccountMeta], - ) -> solana_program::instruction::Instruction { - let mut accounts = Vec::with_capacity(16 + remaining_accounts.len()); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.config, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new( - self.vault, false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.ncn, false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.operator, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.slasher, - true, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.ncn_operator_state, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.ncn_vault_ticket, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.operator_vault_ticket, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.vault_ncn_ticket, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new( - self.vault_operator_delegation, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.ncn_vault_slasher_ticket, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.vault_ncn_slasher_ticket, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new( - self.vault_ncn_slasher_operator_ticket, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new( - self.vault_token_account, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.slasher_token_account, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.token_program, - false, - )); - accounts.extend_from_slice(remaining_accounts); - let mut data = SlashInstructionData::new().try_to_vec().unwrap(); - let mut args = args.try_to_vec().unwrap(); - data.append(&mut args); - - solana_program::instruction::Instruction { - program_id: crate::JITO_VAULT_ID, - accounts, - data, - } - } -} - -#[derive(BorshDeserialize, BorshSerialize)] -pub struct SlashInstructionData { - discriminator: u8, -} - -impl SlashInstructionData { - pub fn new() -> Self { - Self { discriminator: 31 } - } -} - -impl Default for SlashInstructionData { - fn default() -> Self { - Self::new() - } -} - -#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct SlashInstructionArgs { - pub amount: u64, -} - -/// Instruction builder for `Slash`. -/// -/// ### Accounts: -/// -/// 0. `[]` config -/// 1. `[writable]` vault -/// 2. `[]` ncn -/// 3. `[]` operator -/// 4. `[signer]` slasher -/// 5. `[]` ncn_operator_state -/// 6. `[]` ncn_vault_ticket -/// 7. `[]` operator_vault_ticket -/// 8. `[]` vault_ncn_ticket -/// 9. `[writable]` vault_operator_delegation -/// 10. `[]` ncn_vault_slasher_ticket -/// 11. `[]` vault_ncn_slasher_ticket -/// 12. `[writable]` vault_ncn_slasher_operator_ticket -/// 13. `[writable]` vault_token_account -/// 14. `[]` slasher_token_account -/// 15. `[optional]` token_program (default to `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`) -#[derive(Clone, Debug, Default)] -pub struct SlashBuilder { - config: Option, - vault: Option, - ncn: Option, - operator: Option, - slasher: Option, - ncn_operator_state: Option, - ncn_vault_ticket: Option, - operator_vault_ticket: Option, - vault_ncn_ticket: Option, - vault_operator_delegation: Option, - ncn_vault_slasher_ticket: Option, - vault_ncn_slasher_ticket: Option, - vault_ncn_slasher_operator_ticket: Option, - vault_token_account: Option, - slasher_token_account: Option, - token_program: Option, - amount: Option, - __remaining_accounts: Vec, -} - -impl SlashBuilder { - pub fn new() -> Self { - Self::default() - } - #[inline(always)] - pub fn config(&mut self, config: solana_program::pubkey::Pubkey) -> &mut Self { - self.config = Some(config); - self - } - #[inline(always)] - pub fn vault(&mut self, vault: solana_program::pubkey::Pubkey) -> &mut Self { - self.vault = Some(vault); - self - } - #[inline(always)] - pub fn ncn(&mut self, ncn: solana_program::pubkey::Pubkey) -> &mut Self { - self.ncn = Some(ncn); - self - } - #[inline(always)] - pub fn operator(&mut self, operator: solana_program::pubkey::Pubkey) -> &mut Self { - self.operator = Some(operator); - self - } - #[inline(always)] - pub fn slasher(&mut self, slasher: solana_program::pubkey::Pubkey) -> &mut Self { - self.slasher = Some(slasher); - self - } - #[inline(always)] - pub fn ncn_operator_state( - &mut self, - ncn_operator_state: solana_program::pubkey::Pubkey, - ) -> &mut Self { - self.ncn_operator_state = Some(ncn_operator_state); - self - } - #[inline(always)] - pub fn ncn_vault_ticket( - &mut self, - ncn_vault_ticket: solana_program::pubkey::Pubkey, - ) -> &mut Self { - self.ncn_vault_ticket = Some(ncn_vault_ticket); - self - } - #[inline(always)] - pub fn operator_vault_ticket( - &mut self, - operator_vault_ticket: solana_program::pubkey::Pubkey, - ) -> &mut Self { - self.operator_vault_ticket = Some(operator_vault_ticket); - self - } - #[inline(always)] - pub fn vault_ncn_ticket( - &mut self, - vault_ncn_ticket: solana_program::pubkey::Pubkey, - ) -> &mut Self { - self.vault_ncn_ticket = Some(vault_ncn_ticket); - self - } - #[inline(always)] - pub fn vault_operator_delegation( - &mut self, - vault_operator_delegation: solana_program::pubkey::Pubkey, - ) -> &mut Self { - self.vault_operator_delegation = Some(vault_operator_delegation); - self - } - #[inline(always)] - pub fn ncn_vault_slasher_ticket( - &mut self, - ncn_vault_slasher_ticket: solana_program::pubkey::Pubkey, - ) -> &mut Self { - self.ncn_vault_slasher_ticket = Some(ncn_vault_slasher_ticket); - self - } - #[inline(always)] - pub fn vault_ncn_slasher_ticket( - &mut self, - vault_ncn_slasher_ticket: solana_program::pubkey::Pubkey, - ) -> &mut Self { - self.vault_ncn_slasher_ticket = Some(vault_ncn_slasher_ticket); - self - } - #[inline(always)] - pub fn vault_ncn_slasher_operator_ticket( - &mut self, - vault_ncn_slasher_operator_ticket: solana_program::pubkey::Pubkey, - ) -> &mut Self { - self.vault_ncn_slasher_operator_ticket = Some(vault_ncn_slasher_operator_ticket); - self - } - #[inline(always)] - pub fn vault_token_account( - &mut self, - vault_token_account: solana_program::pubkey::Pubkey, - ) -> &mut Self { - self.vault_token_account = Some(vault_token_account); - self - } - #[inline(always)] - pub fn slasher_token_account( - &mut self, - slasher_token_account: solana_program::pubkey::Pubkey, - ) -> &mut Self { - self.slasher_token_account = Some(slasher_token_account); - self - } - /// `[optional account, default to 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA']` - #[inline(always)] - pub fn token_program(&mut self, token_program: solana_program::pubkey::Pubkey) -> &mut Self { - self.token_program = Some(token_program); - self - } - #[inline(always)] - pub fn amount(&mut self, amount: u64) -> &mut Self { - self.amount = Some(amount); - self - } - /// Add an aditional account to the instruction. - #[inline(always)] - pub fn add_remaining_account( - &mut self, - account: solana_program::instruction::AccountMeta, - ) -> &mut Self { - self.__remaining_accounts.push(account); - self - } - /// Add additional accounts to the instruction. - #[inline(always)] - pub fn add_remaining_accounts( - &mut self, - accounts: &[solana_program::instruction::AccountMeta], - ) -> &mut Self { - self.__remaining_accounts.extend_from_slice(accounts); - self - } - #[allow(clippy::clone_on_copy)] - pub fn instruction(&self) -> solana_program::instruction::Instruction { - let accounts = Slash { - config: self.config.expect("config is not set"), - vault: self.vault.expect("vault is not set"), - ncn: self.ncn.expect("ncn is not set"), - operator: self.operator.expect("operator is not set"), - slasher: self.slasher.expect("slasher is not set"), - ncn_operator_state: self - .ncn_operator_state - .expect("ncn_operator_state is not set"), - ncn_vault_ticket: self.ncn_vault_ticket.expect("ncn_vault_ticket is not set"), - operator_vault_ticket: self - .operator_vault_ticket - .expect("operator_vault_ticket is not set"), - vault_ncn_ticket: self.vault_ncn_ticket.expect("vault_ncn_ticket is not set"), - vault_operator_delegation: self - .vault_operator_delegation - .expect("vault_operator_delegation is not set"), - ncn_vault_slasher_ticket: self - .ncn_vault_slasher_ticket - .expect("ncn_vault_slasher_ticket is not set"), - vault_ncn_slasher_ticket: self - .vault_ncn_slasher_ticket - .expect("vault_ncn_slasher_ticket is not set"), - vault_ncn_slasher_operator_ticket: self - .vault_ncn_slasher_operator_ticket - .expect("vault_ncn_slasher_operator_ticket is not set"), - vault_token_account: self - .vault_token_account - .expect("vault_token_account is not set"), - slasher_token_account: self - .slasher_token_account - .expect("slasher_token_account is not set"), - token_program: self.token_program.unwrap_or(solana_program::pubkey!( - "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" - )), - }; - let args = SlashInstructionArgs { - amount: self.amount.clone().expect("amount is not set"), - }; - - accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) - } -} - -/// `slash` CPI accounts. -pub struct SlashCpiAccounts<'a, 'b> { - pub config: &'b solana_program::account_info::AccountInfo<'a>, - - pub vault: &'b solana_program::account_info::AccountInfo<'a>, - - pub ncn: &'b solana_program::account_info::AccountInfo<'a>, - - pub operator: &'b solana_program::account_info::AccountInfo<'a>, - - pub slasher: &'b solana_program::account_info::AccountInfo<'a>, - - pub ncn_operator_state: &'b solana_program::account_info::AccountInfo<'a>, - - pub ncn_vault_ticket: &'b solana_program::account_info::AccountInfo<'a>, - - pub operator_vault_ticket: &'b solana_program::account_info::AccountInfo<'a>, - - pub vault_ncn_ticket: &'b solana_program::account_info::AccountInfo<'a>, - - pub vault_operator_delegation: &'b solana_program::account_info::AccountInfo<'a>, - - pub ncn_vault_slasher_ticket: &'b solana_program::account_info::AccountInfo<'a>, - - pub vault_ncn_slasher_ticket: &'b solana_program::account_info::AccountInfo<'a>, - - pub vault_ncn_slasher_operator_ticket: &'b solana_program::account_info::AccountInfo<'a>, - - pub vault_token_account: &'b solana_program::account_info::AccountInfo<'a>, - - pub slasher_token_account: &'b solana_program::account_info::AccountInfo<'a>, - - pub token_program: &'b solana_program::account_info::AccountInfo<'a>, -} - -/// `slash` CPI instruction. -pub struct SlashCpi<'a, 'b> { - /// The program to invoke. - pub __program: &'b solana_program::account_info::AccountInfo<'a>, - - pub config: &'b solana_program::account_info::AccountInfo<'a>, - - pub vault: &'b solana_program::account_info::AccountInfo<'a>, - - pub ncn: &'b solana_program::account_info::AccountInfo<'a>, - - pub operator: &'b solana_program::account_info::AccountInfo<'a>, - - pub slasher: &'b solana_program::account_info::AccountInfo<'a>, - - pub ncn_operator_state: &'b solana_program::account_info::AccountInfo<'a>, - - pub ncn_vault_ticket: &'b solana_program::account_info::AccountInfo<'a>, - - pub operator_vault_ticket: &'b solana_program::account_info::AccountInfo<'a>, - - pub vault_ncn_ticket: &'b solana_program::account_info::AccountInfo<'a>, - - pub vault_operator_delegation: &'b solana_program::account_info::AccountInfo<'a>, - - pub ncn_vault_slasher_ticket: &'b solana_program::account_info::AccountInfo<'a>, - - pub vault_ncn_slasher_ticket: &'b solana_program::account_info::AccountInfo<'a>, - - pub vault_ncn_slasher_operator_ticket: &'b solana_program::account_info::AccountInfo<'a>, - - pub vault_token_account: &'b solana_program::account_info::AccountInfo<'a>, - - pub slasher_token_account: &'b solana_program::account_info::AccountInfo<'a>, - - pub token_program: &'b solana_program::account_info::AccountInfo<'a>, - /// The arguments for the instruction. - pub __args: SlashInstructionArgs, -} - -impl<'a, 'b> SlashCpi<'a, 'b> { - pub fn new( - program: &'b solana_program::account_info::AccountInfo<'a>, - accounts: SlashCpiAccounts<'a, 'b>, - args: SlashInstructionArgs, - ) -> Self { - Self { - __program: program, - config: accounts.config, - vault: accounts.vault, - ncn: accounts.ncn, - operator: accounts.operator, - slasher: accounts.slasher, - ncn_operator_state: accounts.ncn_operator_state, - ncn_vault_ticket: accounts.ncn_vault_ticket, - operator_vault_ticket: accounts.operator_vault_ticket, - vault_ncn_ticket: accounts.vault_ncn_ticket, - vault_operator_delegation: accounts.vault_operator_delegation, - ncn_vault_slasher_ticket: accounts.ncn_vault_slasher_ticket, - vault_ncn_slasher_ticket: accounts.vault_ncn_slasher_ticket, - vault_ncn_slasher_operator_ticket: accounts.vault_ncn_slasher_operator_ticket, - vault_token_account: accounts.vault_token_account, - slasher_token_account: accounts.slasher_token_account, - token_program: accounts.token_program, - __args: args, - } - } - #[inline(always)] - pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult { - self.invoke_signed_with_remaining_accounts(&[], &[]) - } - #[inline(always)] - pub fn invoke_with_remaining_accounts( - &self, - remaining_accounts: &[( - &'b solana_program::account_info::AccountInfo<'a>, - bool, - bool, - )], - ) -> solana_program::entrypoint::ProgramResult { - self.invoke_signed_with_remaining_accounts(&[], remaining_accounts) - } - #[inline(always)] - pub fn invoke_signed( - &self, - signers_seeds: &[&[&[u8]]], - ) -> solana_program::entrypoint::ProgramResult { - self.invoke_signed_with_remaining_accounts(signers_seeds, &[]) - } - #[allow(clippy::clone_on_copy)] - #[allow(clippy::vec_init_then_push)] - pub fn invoke_signed_with_remaining_accounts( - &self, - signers_seeds: &[&[&[u8]]], - remaining_accounts: &[( - &'b solana_program::account_info::AccountInfo<'a>, - bool, - bool, - )], - ) -> solana_program::entrypoint::ProgramResult { - let mut accounts = Vec::with_capacity(16 + remaining_accounts.len()); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.config.key, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new( - *self.vault.key, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.ncn.key, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.operator.key, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.slasher.key, - true, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.ncn_operator_state.key, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.ncn_vault_ticket.key, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.operator_vault_ticket.key, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.vault_ncn_ticket.key, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new( - *self.vault_operator_delegation.key, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.ncn_vault_slasher_ticket.key, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.vault_ncn_slasher_ticket.key, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new( - *self.vault_ncn_slasher_operator_ticket.key, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new( - *self.vault_token_account.key, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.slasher_token_account.key, - false, - )); - accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.token_program.key, - false, - )); - remaining_accounts.iter().for_each(|remaining_account| { - accounts.push(solana_program::instruction::AccountMeta { - pubkey: *remaining_account.0.key, - is_signer: remaining_account.1, - is_writable: remaining_account.2, - }) - }); - let mut data = SlashInstructionData::new().try_to_vec().unwrap(); - let mut args = self.__args.try_to_vec().unwrap(); - data.append(&mut args); - - let instruction = solana_program::instruction::Instruction { - program_id: crate::JITO_VAULT_ID, - accounts, - data, - }; - let mut account_infos = Vec::with_capacity(16 + 1 + remaining_accounts.len()); - account_infos.push(self.__program.clone()); - account_infos.push(self.config.clone()); - account_infos.push(self.vault.clone()); - account_infos.push(self.ncn.clone()); - account_infos.push(self.operator.clone()); - account_infos.push(self.slasher.clone()); - account_infos.push(self.ncn_operator_state.clone()); - account_infos.push(self.ncn_vault_ticket.clone()); - account_infos.push(self.operator_vault_ticket.clone()); - account_infos.push(self.vault_ncn_ticket.clone()); - account_infos.push(self.vault_operator_delegation.clone()); - account_infos.push(self.ncn_vault_slasher_ticket.clone()); - account_infos.push(self.vault_ncn_slasher_ticket.clone()); - account_infos.push(self.vault_ncn_slasher_operator_ticket.clone()); - account_infos.push(self.vault_token_account.clone()); - account_infos.push(self.slasher_token_account.clone()); - account_infos.push(self.token_program.clone()); - remaining_accounts - .iter() - .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); - - if signers_seeds.is_empty() { - solana_program::program::invoke(&instruction, &account_infos) - } else { - solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds) - } - } -} - -/// Instruction builder for `Slash` via CPI. -/// -/// ### Accounts: -/// -/// 0. `[]` config -/// 1. `[writable]` vault -/// 2. `[]` ncn -/// 3. `[]` operator -/// 4. `[signer]` slasher -/// 5. `[]` ncn_operator_state -/// 6. `[]` ncn_vault_ticket -/// 7. `[]` operator_vault_ticket -/// 8. `[]` vault_ncn_ticket -/// 9. `[writable]` vault_operator_delegation -/// 10. `[]` ncn_vault_slasher_ticket -/// 11. `[]` vault_ncn_slasher_ticket -/// 12. `[writable]` vault_ncn_slasher_operator_ticket -/// 13. `[writable]` vault_token_account -/// 14. `[]` slasher_token_account -/// 15. `[]` token_program -#[derive(Clone, Debug)] -pub struct SlashCpiBuilder<'a, 'b> { - instruction: Box>, -} - -impl<'a, 'b> SlashCpiBuilder<'a, 'b> { - pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self { - let instruction = Box::new(SlashCpiBuilderInstruction { - __program: program, - config: None, - vault: None, - ncn: None, - operator: None, - slasher: None, - ncn_operator_state: None, - ncn_vault_ticket: None, - operator_vault_ticket: None, - vault_ncn_ticket: None, - vault_operator_delegation: None, - ncn_vault_slasher_ticket: None, - vault_ncn_slasher_ticket: None, - vault_ncn_slasher_operator_ticket: None, - vault_token_account: None, - slasher_token_account: None, - token_program: None, - amount: None, - __remaining_accounts: Vec::new(), - }); - Self { instruction } - } - #[inline(always)] - pub fn config( - &mut self, - config: &'b solana_program::account_info::AccountInfo<'a>, - ) -> &mut Self { - self.instruction.config = Some(config); - self - } - #[inline(always)] - pub fn vault(&mut self, vault: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self { - self.instruction.vault = Some(vault); - self - } - #[inline(always)] - pub fn ncn(&mut self, ncn: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self { - self.instruction.ncn = Some(ncn); - self - } - #[inline(always)] - pub fn operator( - &mut self, - operator: &'b solana_program::account_info::AccountInfo<'a>, - ) -> &mut Self { - self.instruction.operator = Some(operator); - self - } - #[inline(always)] - pub fn slasher( - &mut self, - slasher: &'b solana_program::account_info::AccountInfo<'a>, - ) -> &mut Self { - self.instruction.slasher = Some(slasher); - self - } - #[inline(always)] - pub fn ncn_operator_state( - &mut self, - ncn_operator_state: &'b solana_program::account_info::AccountInfo<'a>, - ) -> &mut Self { - self.instruction.ncn_operator_state = Some(ncn_operator_state); - self - } - #[inline(always)] - pub fn ncn_vault_ticket( - &mut self, - ncn_vault_ticket: &'b solana_program::account_info::AccountInfo<'a>, - ) -> &mut Self { - self.instruction.ncn_vault_ticket = Some(ncn_vault_ticket); - self - } - #[inline(always)] - pub fn operator_vault_ticket( - &mut self, - operator_vault_ticket: &'b solana_program::account_info::AccountInfo<'a>, - ) -> &mut Self { - self.instruction.operator_vault_ticket = Some(operator_vault_ticket); - self - } - #[inline(always)] - pub fn vault_ncn_ticket( - &mut self, - vault_ncn_ticket: &'b solana_program::account_info::AccountInfo<'a>, - ) -> &mut Self { - self.instruction.vault_ncn_ticket = Some(vault_ncn_ticket); - self - } - #[inline(always)] - pub fn vault_operator_delegation( - &mut self, - vault_operator_delegation: &'b solana_program::account_info::AccountInfo<'a>, - ) -> &mut Self { - self.instruction.vault_operator_delegation = Some(vault_operator_delegation); - self - } - #[inline(always)] - pub fn ncn_vault_slasher_ticket( - &mut self, - ncn_vault_slasher_ticket: &'b solana_program::account_info::AccountInfo<'a>, - ) -> &mut Self { - self.instruction.ncn_vault_slasher_ticket = Some(ncn_vault_slasher_ticket); - self - } - #[inline(always)] - pub fn vault_ncn_slasher_ticket( - &mut self, - vault_ncn_slasher_ticket: &'b solana_program::account_info::AccountInfo<'a>, - ) -> &mut Self { - self.instruction.vault_ncn_slasher_ticket = Some(vault_ncn_slasher_ticket); - self - } - #[inline(always)] - pub fn vault_ncn_slasher_operator_ticket( - &mut self, - vault_ncn_slasher_operator_ticket: &'b solana_program::account_info::AccountInfo<'a>, - ) -> &mut Self { - self.instruction.vault_ncn_slasher_operator_ticket = - Some(vault_ncn_slasher_operator_ticket); - self - } - #[inline(always)] - pub fn vault_token_account( - &mut self, - vault_token_account: &'b solana_program::account_info::AccountInfo<'a>, - ) -> &mut Self { - self.instruction.vault_token_account = Some(vault_token_account); - self - } - #[inline(always)] - pub fn slasher_token_account( - &mut self, - slasher_token_account: &'b solana_program::account_info::AccountInfo<'a>, - ) -> &mut Self { - self.instruction.slasher_token_account = Some(slasher_token_account); - self - } - #[inline(always)] - pub fn token_program( - &mut self, - token_program: &'b solana_program::account_info::AccountInfo<'a>, - ) -> &mut Self { - self.instruction.token_program = Some(token_program); - self - } - #[inline(always)] - pub fn amount(&mut self, amount: u64) -> &mut Self { - self.instruction.amount = Some(amount); - self - } - /// Add an additional account to the instruction. - #[inline(always)] - pub fn add_remaining_account( - &mut self, - account: &'b solana_program::account_info::AccountInfo<'a>, - is_writable: bool, - is_signer: bool, - ) -> &mut Self { - self.instruction - .__remaining_accounts - .push((account, is_writable, is_signer)); - self - } - /// Add additional accounts to the instruction. - /// - /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not, - /// and a `bool` indicating whether the account is a signer or not. - #[inline(always)] - pub fn add_remaining_accounts( - &mut self, - accounts: &[( - &'b solana_program::account_info::AccountInfo<'a>, - bool, - bool, - )], - ) -> &mut Self { - self.instruction - .__remaining_accounts - .extend_from_slice(accounts); - self - } - #[inline(always)] - pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult { - self.invoke_signed(&[]) - } - #[allow(clippy::clone_on_copy)] - #[allow(clippy::vec_init_then_push)] - pub fn invoke_signed( - &self, - signers_seeds: &[&[&[u8]]], - ) -> solana_program::entrypoint::ProgramResult { - let args = SlashInstructionArgs { - amount: self.instruction.amount.clone().expect("amount is not set"), - }; - let instruction = SlashCpi { - __program: self.instruction.__program, - - config: self.instruction.config.expect("config is not set"), - - vault: self.instruction.vault.expect("vault is not set"), - - ncn: self.instruction.ncn.expect("ncn is not set"), - - operator: self.instruction.operator.expect("operator is not set"), - - slasher: self.instruction.slasher.expect("slasher is not set"), - - ncn_operator_state: self - .instruction - .ncn_operator_state - .expect("ncn_operator_state is not set"), - - ncn_vault_ticket: self - .instruction - .ncn_vault_ticket - .expect("ncn_vault_ticket is not set"), - - operator_vault_ticket: self - .instruction - .operator_vault_ticket - .expect("operator_vault_ticket is not set"), - - vault_ncn_ticket: self - .instruction - .vault_ncn_ticket - .expect("vault_ncn_ticket is not set"), - - vault_operator_delegation: self - .instruction - .vault_operator_delegation - .expect("vault_operator_delegation is not set"), - - ncn_vault_slasher_ticket: self - .instruction - .ncn_vault_slasher_ticket - .expect("ncn_vault_slasher_ticket is not set"), - - vault_ncn_slasher_ticket: self - .instruction - .vault_ncn_slasher_ticket - .expect("vault_ncn_slasher_ticket is not set"), - - vault_ncn_slasher_operator_ticket: self - .instruction - .vault_ncn_slasher_operator_ticket - .expect("vault_ncn_slasher_operator_ticket is not set"), - - vault_token_account: self - .instruction - .vault_token_account - .expect("vault_token_account is not set"), - - slasher_token_account: self - .instruction - .slasher_token_account - .expect("slasher_token_account is not set"), - - token_program: self - .instruction - .token_program - .expect("token_program is not set"), - __args: args, - }; - instruction.invoke_signed_with_remaining_accounts( - signers_seeds, - &self.instruction.__remaining_accounts, - ) - } -} - -#[derive(Clone, Debug)] -struct SlashCpiBuilderInstruction<'a, 'b> { - __program: &'b solana_program::account_info::AccountInfo<'a>, - config: Option<&'b solana_program::account_info::AccountInfo<'a>>, - vault: Option<&'b solana_program::account_info::AccountInfo<'a>>, - ncn: Option<&'b solana_program::account_info::AccountInfo<'a>>, - operator: Option<&'b solana_program::account_info::AccountInfo<'a>>, - slasher: Option<&'b solana_program::account_info::AccountInfo<'a>>, - ncn_operator_state: Option<&'b solana_program::account_info::AccountInfo<'a>>, - ncn_vault_ticket: Option<&'b solana_program::account_info::AccountInfo<'a>>, - operator_vault_ticket: Option<&'b solana_program::account_info::AccountInfo<'a>>, - vault_ncn_ticket: Option<&'b solana_program::account_info::AccountInfo<'a>>, - vault_operator_delegation: Option<&'b solana_program::account_info::AccountInfo<'a>>, - ncn_vault_slasher_ticket: Option<&'b solana_program::account_info::AccountInfo<'a>>, - vault_ncn_slasher_ticket: Option<&'b solana_program::account_info::AccountInfo<'a>>, - vault_ncn_slasher_operator_ticket: Option<&'b solana_program::account_info::AccountInfo<'a>>, - vault_token_account: Option<&'b solana_program::account_info::AccountInfo<'a>>, - slasher_token_account: Option<&'b solana_program::account_info::AccountInfo<'a>>, - token_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, - amount: Option, - /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. - __remaining_accounts: Vec<( - &'b solana_program::account_info::AccountInfo<'a>, - bool, - bool, - )>, -} diff --git a/idl/jito_vault.json b/idl/jito_vault.json index 86857ce8..53ecc14f 100644 --- a/idl/jito_vault.json +++ b/idl/jito_vault.json @@ -1342,101 +1342,6 @@ "type": "u8", "value": 30 } - }, - { - "name": "Slash", - "accounts": [ - { - "name": "config", - "isMut": false, - "isSigner": false - }, - { - "name": "vault", - "isMut": true, - "isSigner": false - }, - { - "name": "ncn", - "isMut": false, - "isSigner": false - }, - { - "name": "operator", - "isMut": false, - "isSigner": false - }, - { - "name": "slasher", - "isMut": false, - "isSigner": true - }, - { - "name": "ncnOperatorState", - "isMut": false, - "isSigner": false - }, - { - "name": "ncnVaultTicket", - "isMut": false, - "isSigner": false - }, - { - "name": "operatorVaultTicket", - "isMut": false, - "isSigner": false - }, - { - "name": "vaultNcnTicket", - "isMut": false, - "isSigner": false - }, - { - "name": "vaultOperatorDelegation", - "isMut": true, - "isSigner": false - }, - { - "name": "ncnVaultSlasherTicket", - "isMut": false, - "isSigner": false - }, - { - "name": "vaultNcnSlasherTicket", - "isMut": false, - "isSigner": false - }, - { - "name": "vaultNcnSlasherOperatorTicket", - "isMut": true, - "isSigner": false - }, - { - "name": "vaultTokenAccount", - "isMut": true, - "isSigner": false - }, - { - "name": "slasherTokenAccount", - "isMut": false, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "amount", - "type": "u64" - } - ], - "discriminant": { - "type": "u8", - "value": 31 - } } ], "accounts": [ diff --git a/integration_tests/tests/fixtures/fixture.rs b/integration_tests/tests/fixtures/fixture.rs index 29143cd1..6f6433f5 100644 --- a/integration_tests/tests/fixtures/fixture.rs +++ b/integration_tests/tests/fixtures/fixture.rs @@ -19,7 +19,7 @@ use spl_token_2022::{ }; use crate::fixtures::{ - restaking_client::{NcnRoot, OperatorRoot, RestakingProgramClient}, + restaking_client::{OperatorRoot, RestakingProgramClient}, vault_client::{VaultProgramClient, VaultRoot}, TestResult, }; @@ -42,9 +42,7 @@ pub struct ConfiguredVault { pub vault_root: VaultRoot, #[allow(dead_code)] pub restaking_config_admin: Keypair, - pub ncn_root: NcnRoot, pub operator_roots: Vec, - pub slashers_amounts: Vec<(Keypair, u64)>, } impl TestBuilder { @@ -504,9 +502,7 @@ impl TestBuilder { vault_root, vault_config_admin, restaking_config_admin, - ncn_root, operator_roots, - slashers_amounts, }) } } diff --git a/integration_tests/tests/fixtures/vault_client.rs b/integration_tests/tests/fixtures/vault_client.rs index e3eaafc4..606a61d7 100644 --- a/integration_tests/tests/fixtures/vault_client.rs +++ b/integration_tests/tests/fixtures/vault_client.rs @@ -3,8 +3,8 @@ use std::{fmt, fmt::Debug}; use borsh::BorshDeserialize; use jito_bytemuck::AccountDeserialize; use jito_restaking_core::{ - ncn_operator_state::NcnOperatorState, ncn_vault_slasher_ticket::NcnVaultSlasherTicket, - ncn_vault_ticket::NcnVaultTicket, operator_vault_ticket::OperatorVaultTicket, + ncn_vault_slasher_ticket::NcnVaultSlasherTicket, ncn_vault_ticket::NcnVaultTicket, + operator_vault_ticket::OperatorVaultTicket, }; use jito_vault_core::{ config::Config, vault::Vault, vault_ncn_slasher_operator_ticket::VaultNcnSlasherOperatorTicket, @@ -31,7 +31,7 @@ use solana_program::{ rent::Rent, system_instruction::{create_account, transfer}, }; -use solana_program_test::{BanksClient, BanksClientError, ProgramTestBanksClientExt}; +use solana_program_test::{BanksClient, BanksClientError}; use solana_sdk::{ commitment_config::CommitmentLevel, instruction::InstructionError, @@ -495,104 +495,6 @@ impl VaultProgramClient { Ok(()) } - pub async fn do_slash( - &mut self, - vault_root: &VaultRoot, - ncn_pubkey: &Pubkey, - slasher: &Keypair, - operator_pubkey: &Pubkey, - amount: u64, - ) -> Result<(), TestError> { - let ncn_operator_state_pubkey = NcnOperatorState::find_program_address( - &jito_restaking_program::id(), - ncn_pubkey, - operator_pubkey, - ) - .0; - let ncn_vault_ticket_pubkey = NcnVaultTicket::find_program_address( - &jito_restaking_program::id(), - ncn_pubkey, - &vault_root.vault_pubkey, - ) - .0; - let operator_vault_ticket_pubkey = OperatorVaultTicket::find_program_address( - &jito_restaking_program::id(), - operator_pubkey, - &vault_root.vault_pubkey, - ) - .0; - let vault_ncn_ticket_pubkey = VaultNcnTicket::find_program_address( - &jito_vault_program::id(), - &vault_root.vault_pubkey, - ncn_pubkey, - ) - .0; - let vault_operator_delegation = VaultOperatorDelegation::find_program_address( - &jito_vault_program::id(), - &vault_root.vault_pubkey, - operator_pubkey, - ) - .0; - let ncn_slasher_ticket_pubkey = NcnVaultSlasherTicket::find_program_address( - &jito_restaking_program::id(), - ncn_pubkey, - &vault_root.vault_pubkey, - &slasher.pubkey(), - ) - .0; - let vault_slasher_ticket_pubkey = VaultNcnSlasherTicket::find_program_address( - &jito_vault_program::id(), - &vault_root.vault_pubkey, - ncn_pubkey, - &slasher.pubkey(), - ) - .0; - let config = self - .get_config(&Config::find_program_address(&jito_vault_program::id()).0) - .await - .unwrap(); - let clock: Clock = self.banks_client.get_sysvar().await?; - - let vault_ncn_slasher_operator_ticket = - VaultNcnSlasherOperatorTicket::find_program_address( - &jito_vault_program::id(), - &vault_root.vault_pubkey, - ncn_pubkey, - &slasher.pubkey(), - operator_pubkey, - clock.slot / config.epoch_length(), - ) - .0; - - let vault = self.get_vault(&vault_root.vault_pubkey).await.unwrap(); - let vault_token_account = - get_associated_token_address(&vault_root.vault_pubkey, &vault.supported_mint); - let slasher_token_account = - get_associated_token_address(&slasher.pubkey(), &vault.supported_mint); - - self.slash( - &Config::find_program_address(&jito_vault_program::id()).0, - &vault_root.vault_pubkey, - ncn_pubkey, - operator_pubkey, - slasher, - &ncn_operator_state_pubkey, - &ncn_vault_ticket_pubkey, - &operator_vault_ticket_pubkey, - &vault_ncn_ticket_pubkey, - &vault_operator_delegation, - &ncn_slasher_ticket_pubkey, - &vault_slasher_ticket_pubkey, - &vault_ncn_slasher_operator_ticket, - &vault_token_account, - &slasher_token_account, - amount, - ) - .await?; - - Ok(()) - } - pub async fn do_initialize_vault_operator_delegation( &mut self, vault_root: &VaultRoot, @@ -1481,53 +1383,6 @@ impl VaultProgramClient { .await } - pub async fn slash( - &mut self, - config: &Pubkey, - vault: &Pubkey, - ncn: &Pubkey, - operator: &Pubkey, - slasher: &Keypair, - ncn_operator_state: &Pubkey, - ncn_vault_ticket: &Pubkey, - operator_vault_ticket: &Pubkey, - vault_ncn_ticket: &Pubkey, - vault_operator_delegation: &Pubkey, - ncn_vault_slasher_ticket: &Pubkey, - vault_ncn_slasher_ticket: &Pubkey, - vault_ncn_slasher_operator_ticket: &Pubkey, - vault_token_account: &Pubkey, - slasher_token_account: &Pubkey, - amount: u64, - ) -> Result<(), TestError> { - let blockhash = self.banks_client.get_latest_blockhash().await?; - self._process_transaction(&Transaction::new_signed_with_payer( - &[jito_vault_sdk::sdk::slash( - &jito_vault_program::id(), - config, - vault, - ncn, - operator, - &slasher.pubkey(), - ncn_operator_state, - ncn_vault_ticket, - operator_vault_ticket, - vault_ncn_ticket, - vault_operator_delegation, - ncn_vault_slasher_ticket, - vault_ncn_slasher_ticket, - vault_ncn_slasher_operator_ticket, - vault_token_account, - slasher_token_account, - amount, - )], - Some(&slasher.pubkey()), - &[slasher], - blockhash, - )) - .await - } - pub async fn create_token_metadata( &mut self, vault: &Pubkey, @@ -1541,12 +1396,6 @@ impl VaultProgramClient { ) -> Result<(), TestError> { let blockhash = self.banks_client.get_latest_blockhash().await?; - let new_blockhash = self - .banks_client - .get_new_latest_blockhash(&blockhash) - .await - .unwrap(); - self._process_transaction(&Transaction::new_signed_with_payer( &[jito_vault_sdk::sdk::create_token_metadata( &jito_vault_program::id(), @@ -1561,7 +1410,7 @@ impl VaultProgramClient { )], Some(&payer.pubkey()), &[admin, payer], - new_blockhash, + blockhash, )) .await } diff --git a/integration_tests/tests/vault/burn_withdrawal_ticket.rs b/integration_tests/tests/vault/burn_withdrawal_ticket.rs index d87e4bf5..89dd4824 100644 --- a/integration_tests/tests/vault/burn_withdrawal_ticket.rs +++ b/integration_tests/tests/vault/burn_withdrawal_ticket.rs @@ -33,9 +33,7 @@ mod tests { vault_config_admin: _, vault_root, restaking_config_admin: _, - ncn_root: _, operator_roots, - slashers_amounts: _, } = fixture .setup_vault_with_ncn_and_operators( deposit_fee_bps, @@ -113,9 +111,7 @@ mod tests { vault_config_admin: _, vault_root, restaking_config_admin: _, - ncn_root: _, operator_roots, - slashers_amounts: _, } = fixture .setup_vault_with_ncn_and_operators( deposit_fee_bps, @@ -209,9 +205,7 @@ mod tests { vault_config_admin: _, vault_root, restaking_config_admin: _, - ncn_root: _, operator_roots, - slashers_amounts: _, } = fixture .setup_vault_with_ncn_and_operators( deposit_fee_bps, @@ -339,9 +333,7 @@ mod tests { vault_config_admin, vault_root, restaking_config_admin: _, - ncn_root: _, operator_roots, - slashers_amounts: _, } = fixture .setup_vault_with_ncn_and_operators( deposit_fee_bps, @@ -502,9 +494,7 @@ mod tests { vault_config_admin: _, vault_root, restaking_config_admin: _, - ncn_root: _, operator_roots, - slashers_amounts: _, } = fixture .setup_vault_with_ncn_and_operators( deposit_fee_bps, @@ -659,9 +649,7 @@ mod tests { vault_config_admin: _, vault_root, restaking_config_admin: _, - ncn_root: _, operator_roots, - slashers_amounts: _, } = fixture .setup_vault_with_ncn_and_operators( deposit_fee_bps, @@ -811,9 +799,7 @@ mod tests { vault_config_admin, vault_root, restaking_config_admin: _, - ncn_root: _, operator_roots, - slashers_amounts: _, } = fixture .setup_vault_with_ncn_and_operators( deposit_fee_bps, @@ -1006,9 +992,7 @@ mod tests { vault_config_admin, vault_root, restaking_config_admin: _, - ncn_root: _, operator_roots, - slashers_amounts: _, } = fixture .setup_vault_with_ncn_and_operators( deposit_fee_bps, @@ -1139,9 +1123,7 @@ mod tests { vault_config_admin: _, vault_root, restaking_config_admin: _, - ncn_root: _, operator_roots, - slashers_amounts: _, } = fixture .setup_vault_with_ncn_and_operators( deposit_fee_bps, diff --git a/integration_tests/tests/vault/crank_vault_update_state_tracker.rs b/integration_tests/tests/vault/crank_vault_update_state_tracker.rs index 90578998..1da973bd 100644 --- a/integration_tests/tests/vault/crank_vault_update_state_tracker.rs +++ b/integration_tests/tests/vault/crank_vault_update_state_tracker.rs @@ -655,9 +655,7 @@ mod tests { vault_config_admin: _, vault_root, restaking_config_admin: _, - ncn_root: _, operator_roots, - slashers_amounts: _, } = fixture .setup_vault_with_ncn_and_operators( deposit_fee_bps, @@ -796,9 +794,7 @@ mod tests { vault_config_admin: _, vault_root, restaking_config_admin: _, - ncn_root: _, operator_roots, - slashers_amounts: _, } = fixture .setup_vault_with_ncn_and_operators( deposit_fee_bps, @@ -917,9 +913,7 @@ mod tests { vault_config_admin: _, vault_root, restaking_config_admin: _, - ncn_root: _, operator_roots, - slashers_amounts: _, } = fixture .setup_vault_with_ncn_and_operators( deposit_fee_bps, diff --git a/integration_tests/tests/vault/mod.rs b/integration_tests/tests/vault/mod.rs index ec7e2ff4..8c8031a0 100644 --- a/integration_tests/tests/vault/mod.rs +++ b/integration_tests/tests/vault/mod.rs @@ -19,6 +19,5 @@ mod set_fees; mod set_is_paused; mod set_program_fee_wallet; mod set_secondary_admin; -mod slash; mod update_token_metadata; mod update_vault_balance; diff --git a/integration_tests/tests/vault/slash.rs b/integration_tests/tests/vault/slash.rs deleted file mode 100644 index 596f362c..00000000 --- a/integration_tests/tests/vault/slash.rs +++ /dev/null @@ -1,301 +0,0 @@ -#[cfg(test)] -mod tests { - use jito_vault_core::{ - config::Config, vault::Vault, - vault_ncn_slasher_operator_ticket::VaultNcnSlasherOperatorTicket, - vault_ncn_slasher_ticket::VaultNcnSlasherTicket, - }; - use jito_vault_sdk::error::VaultError; - use solana_sdk::signature::{Keypair, Signer}; - - use crate::fixtures::{ - fixture::{ConfiguredVault, TestBuilder}, - vault_client::assert_vault_error, - }; - - const MAX_SLASH_AMOUNT: u64 = 100; - const MINT_AMOUNT: u64 = 100_000; - const DELEGATION_AMOUNT: u64 = 10_000; - - const ZERO_DEPOSIT_FEE_BPS: u16 = 0; - const ZERO_WITHDRAW_FEE_BPS: u16 = 0; - const ZERO_REWARD_FEE_BPS: u16 = 0; - const ONE_OPERATOR: u16 = 1; - const SLASHER_AMOUNTS: &[u64] = &[MAX_SLASH_AMOUNT]; - - #[tokio::test] - async fn test_slash_ok() { - let mut fixture = TestBuilder::new().await; - - let ConfiguredVault { - mut vault_program_client, - vault_config_admin, - vault_root, - ncn_root, - operator_roots, - slashers_amounts, - .. - } = fixture - .setup_vault_with_ncn_and_operators( - ZERO_DEPOSIT_FEE_BPS, - ZERO_WITHDRAW_FEE_BPS, - ZERO_REWARD_FEE_BPS, - ONE_OPERATOR, - SLASHER_AMOUNTS, - ) - .await - .unwrap(); - - let depositor = Keypair::new(); - vault_program_client - .configure_depositor(&vault_root, &depositor.pubkey(), MINT_AMOUNT) - .await - .unwrap(); - vault_program_client - .do_mint_to(&vault_root, &depositor, MINT_AMOUNT, MINT_AMOUNT) - .await - .unwrap(); - - let operator_root = &operator_roots[0]; - vault_program_client - .do_add_delegation( - &vault_root, - &operator_root.operator_pubkey, - DELEGATION_AMOUNT, - ) - .await - .unwrap(); - - let config = vault_program_client - .get_config(&Config::find_program_address(&jito_vault_program::id()).0) - .await - .unwrap(); - fixture - .warp_slot_incremental(2 * config.epoch_length()) - .await - .unwrap(); - let operator_root_pubkeys: Vec<_> = - operator_roots.iter().map(|r| r.operator_pubkey).collect(); - vault_program_client - .do_full_vault_update(&vault_root.vault_pubkey, &operator_root_pubkeys) - .await - .unwrap(); - - let vault = vault_program_client - .get_vault(&vault_root.vault_pubkey) - .await - .unwrap(); - - // configure slasher and slash - let slasher = &slashers_amounts[0].0; - fixture - .create_ata(&vault.supported_mint, &slasher.pubkey()) - .await - .unwrap(); - let epoch = fixture.get_current_slot().await.unwrap() / config.epoch_length(); - vault_program_client - .initialize_vault_ncn_slasher_operator_ticket( - &Config::find_program_address(&jito_vault_program::id()).0, - &vault_root.vault_pubkey, - &ncn_root.ncn_pubkey, - &slasher.pubkey(), - &operator_root.operator_pubkey, - &VaultNcnSlasherTicket::find_program_address( - &jito_vault_program::id(), - &vault_root.vault_pubkey, - &ncn_root.ncn_pubkey, - &slasher.pubkey(), - ) - .0, - &VaultNcnSlasherOperatorTicket::find_program_address( - &jito_vault_program::id(), - &vault_root.vault_pubkey, - &ncn_root.ncn_pubkey, - &slasher.pubkey(), - &operator_root.operator_pubkey, - epoch, - ) - .0, - &vault_config_admin, - ) - .await - .unwrap(); - - vault_program_client - .do_slash( - &vault_root, - &ncn_root.ncn_pubkey, - slasher, - &operator_root.operator_pubkey, - MAX_SLASH_AMOUNT, - ) - .await - .unwrap(); - - let vault = vault_program_client - .get_vault(&vault_root.vault_pubkey) - .await - .unwrap(); - assert_eq!( - vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, - MINT_AMOUNT - MAX_SLASH_AMOUNT - ); - assert_eq!( - vault.delegation_state.total_security().unwrap(), - DELEGATION_AMOUNT - MAX_SLASH_AMOUNT - ); - - let vault_operator_delegation = vault_program_client - .get_vault_operator_delegation(&vault_root.vault_pubkey, &operator_root.operator_pubkey) - .await - .unwrap(); - - assert_eq!( - vault_operator_delegation - .delegation_state - .total_security() - .unwrap(), - DELEGATION_AMOUNT - MAX_SLASH_AMOUNT - ); - - let epoch = fixture.get_current_slot().await.unwrap() / config.epoch_length(); - let vault_ncn_slasher_operator_ticket = vault_program_client - .get_vault_ncn_slasher_operator_ticket( - &vault_root.vault_pubkey, - &ncn_root.ncn_pubkey, - &slasher.pubkey(), - &operator_root.operator_pubkey, - epoch, - ) - .await - .unwrap(); - assert_eq!(vault_ncn_slasher_operator_ticket.slashed(), 100); - assert_eq!(vault_ncn_slasher_operator_ticket.epoch(), epoch); - assert_eq!( - vault_ncn_slasher_operator_ticket.vault, - vault_root.vault_pubkey - ); - assert_eq!(vault_ncn_slasher_operator_ticket.ncn, ncn_root.ncn_pubkey); - assert_eq!(vault_ncn_slasher_operator_ticket.slasher, slasher.pubkey()); - assert_eq!( - vault_ncn_slasher_operator_ticket.operator, - operator_root.operator_pubkey - ); - } - - #[tokio::test] - async fn test_slash_vault_is_paused_fails() { - let mut fixture = TestBuilder::new().await; - - let ConfiguredVault { - mut vault_program_client, - vault_config_admin, - vault_root, - ncn_root, - operator_roots, - slashers_amounts, - .. - } = fixture - .setup_vault_with_ncn_and_operators( - ZERO_DEPOSIT_FEE_BPS, - ZERO_WITHDRAW_FEE_BPS, - ZERO_REWARD_FEE_BPS, - ONE_OPERATOR, - SLASHER_AMOUNTS, - ) - .await - .unwrap(); - - let depositor = Keypair::new(); - vault_program_client - .configure_depositor(&vault_root, &depositor.pubkey(), MINT_AMOUNT) - .await - .unwrap(); - vault_program_client - .do_mint_to(&vault_root, &depositor, MINT_AMOUNT, MINT_AMOUNT) - .await - .unwrap(); - - let operator_root = &operator_roots[0]; - vault_program_client - .do_add_delegation( - &vault_root, - &operator_root.operator_pubkey, - DELEGATION_AMOUNT, - ) - .await - .unwrap(); - - let config = vault_program_client - .get_config(&Config::find_program_address(&jito_vault_program::id()).0) - .await - .unwrap(); - fixture - .warp_slot_incremental(2 * config.epoch_length()) - .await - .unwrap(); - let operator_root_pubkeys: Vec<_> = - operator_roots.iter().map(|r| r.operator_pubkey).collect(); - vault_program_client - .do_full_vault_update(&vault_root.vault_pubkey, &operator_root_pubkeys) - .await - .unwrap(); - - let vault = vault_program_client - .get_vault(&vault_root.vault_pubkey) - .await - .unwrap(); - - // configure slasher and slash - let slasher = &slashers_amounts[0].0; - fixture - .create_ata(&vault.supported_mint, &slasher.pubkey()) - .await - .unwrap(); - let epoch = fixture.get_current_slot().await.unwrap() / config.epoch_length(); - vault_program_client - .initialize_vault_ncn_slasher_operator_ticket( - &Config::find_program_address(&jito_vault_program::id()).0, - &vault_root.vault_pubkey, - &ncn_root.ncn_pubkey, - &slasher.pubkey(), - &operator_root.operator_pubkey, - &VaultNcnSlasherTicket::find_program_address( - &jito_vault_program::id(), - &vault_root.vault_pubkey, - &ncn_root.ncn_pubkey, - &slasher.pubkey(), - ) - .0, - &VaultNcnSlasherOperatorTicket::find_program_address( - &jito_vault_program::id(), - &vault_root.vault_pubkey, - &ncn_root.ncn_pubkey, - &slasher.pubkey(), - &operator_root.operator_pubkey, - epoch, - ) - .0, - &vault_config_admin, - ) - .await - .unwrap(); - - vault_program_client - .set_is_paused(&vault_root.vault_pubkey, &vault_root.vault_admin, true) - .await - .unwrap(); - - let test_error = vault_program_client - .do_slash( - &vault_root, - &ncn_root.ncn_pubkey, - &slasher, - &operator_root.operator_pubkey, - MAX_SLASH_AMOUNT, - ) - .await; - - assert_vault_error(test_error, VaultError::VaultIsPaused); - } -} diff --git a/integration_tests/tests/vault/update_vault_balance.rs b/integration_tests/tests/vault/update_vault_balance.rs index c314fba2..f69ba809 100644 --- a/integration_tests/tests/vault/update_vault_balance.rs +++ b/integration_tests/tests/vault/update_vault_balance.rs @@ -28,9 +28,7 @@ mod tests { vault_config_admin: _, vault_root, restaking_config_admin: _, - ncn_root: _, operator_roots, - slashers_amounts: _, } = fixture .setup_vault_with_ncn_and_operators( DEPOSIT_FEE_BPS, diff --git a/vault_program/src/lib.rs b/vault_program/src/lib.rs index c24d245d..5b6d4032 100644 --- a/vault_program/src/lib.rs +++ b/vault_program/src/lib.rs @@ -25,7 +25,6 @@ mod set_is_paused; mod set_program_fee; mod set_program_fee_wallet; mod set_secondary_admin; -mod slash; mod update_token_metadata; mod update_vault_balance; mod warmup_vault_ncn_slasher_ticket; @@ -63,7 +62,7 @@ use crate::{ set_admin::process_set_admin, set_capacity::process_set_deposit_capacity, set_fees::process_set_fees, set_is_paused::process_set_is_paused, set_program_fee_wallet::process_set_program_fee_wallet, - set_secondary_admin::process_set_secondary_admin, slash::process_slash, + set_secondary_admin::process_set_secondary_admin, update_token_metadata::process_update_token_metadata, update_vault_balance::process_update_vault_balance, warmup_vault_ncn_slasher_ticket::process_warmup_vault_ncn_slasher_ticket, @@ -265,13 +264,6 @@ pub fn process_instruction( process_close_vault_update_state_tracker(program_id, accounts, ncn_epoch) } // ------------------------------------------ - // Vault slashing - // ------------------------------------------ - VaultInstruction::Slash { amount } => { - msg!("Instruction: Slash"); - process_slash(program_id, accounts, amount) - } - // ------------------------------------------ // VRT metadata // ------------------------------------------ VaultInstruction::CreateTokenMetadata { name, symbol, uri } => { diff --git a/vault_program/src/slash.rs b/vault_program/src/slash.rs deleted file mode 100644 index 00a3196e..00000000 --- a/vault_program/src/slash.rs +++ /dev/null @@ -1,272 +0,0 @@ -use jito_bytemuck::AccountDeserialize; -use jito_jsm_core::loader::{load_associated_token_account, load_signer, load_token_program}; -use jito_restaking_core::{ - ncn::Ncn, ncn_operator_state::NcnOperatorState, - ncn_vault_slasher_ticket::NcnVaultSlasherTicket, ncn_vault_ticket::NcnVaultTicket, - operator::Operator, operator_vault_ticket::OperatorVaultTicket, -}; -use jito_vault_core::{ - config::Config, vault::Vault, vault_ncn_slasher_operator_ticket::VaultNcnSlasherOperatorTicket, - vault_ncn_slasher_ticket::VaultNcnSlasherTicket, vault_ncn_ticket::VaultNcnTicket, - vault_operator_delegation::VaultOperatorDelegation, -}; -use jito_vault_sdk::error::VaultError; -use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, - program::invoke_signed, program_error::ProgramError, pubkey::Pubkey, sysvar::Sysvar, -}; -use spl_token::instruction::transfer; - -/// Processes the vault slash instruction: [`crate::VaultInstruction::Slash`] -pub fn process_slash( - program_id: &Pubkey, - accounts: &[AccountInfo], - slash_amount: u64, -) -> ProgramResult { - let [config, vault_info, ncn, operator, slasher, ncn_operator_state, ncn_vault_ticket, operator_vault_ticket, vault_ncn_ticket, vault_operator_delegation, ncn_vault_slasher_ticket, vault_ncn_slasher_ticket, vault_ncn_slasher_operator_ticket, vault_token_account, slasher_token_account, token_program] = - accounts - else { - return Err(ProgramError::NotEnoughAccountKeys); - }; - - Config::load(program_id, config, false)?; - let config_data = config.data.borrow(); - let config = Config::try_from_slice_unchecked(&config_data)?; - Vault::load(program_id, vault_info, true)?; - let mut vault_data = vault_info.data.borrow_mut(); - let vault = Vault::try_from_slice_unchecked_mut(&mut vault_data)?; - Ncn::load(&config.restaking_program, ncn, false)?; - Operator::load(&config.restaking_program, operator, false)?; - load_signer(slasher, false)?; - NcnOperatorState::load( - &config.restaking_program, - ncn_operator_state, - ncn, - operator, - false, - )?; - let ncn_operator_state_data = ncn_operator_state.data.borrow(); - let ncn_operator_state = NcnOperatorState::try_from_slice_unchecked(&ncn_operator_state_data)?; - NcnVaultTicket::load( - &config.restaking_program, - ncn_vault_ticket, - ncn, - vault_info, - false, - )?; - let ncn_vault_ticket_data = ncn_vault_ticket.data.borrow(); - let ncn_vault_ticket = NcnVaultTicket::try_from_slice_unchecked(&ncn_vault_ticket_data)?; - OperatorVaultTicket::load( - &config.restaking_program, - operator_vault_ticket, - operator, - vault_info, - false, - )?; - let operator_vault_ticket_data = operator_vault_ticket.data.borrow(); - let operator_vault_ticket = - OperatorVaultTicket::try_from_slice_unchecked(&operator_vault_ticket_data)?; - VaultNcnTicket::load(program_id, vault_ncn_ticket, vault_info, ncn, false)?; - let vault_ncn_ticket_data = vault_ncn_ticket.data.borrow(); - let vault_ncn_ticket = VaultNcnTicket::try_from_slice_unchecked(&vault_ncn_ticket_data)?; - VaultOperatorDelegation::load( - program_id, - vault_operator_delegation, - vault_info, - operator, - true, - )?; - let mut vault_operator_delegation_data = vault_operator_delegation.data.borrow_mut(); - let vault_operator_delegation = - VaultOperatorDelegation::try_from_slice_unchecked_mut(&mut vault_operator_delegation_data)?; - NcnVaultSlasherTicket::load( - &config.restaking_program, - ncn_vault_slasher_ticket, - ncn, - vault_info, - slasher, - false, - )?; - let ncn_vault_slasher_ticket_data = ncn_vault_slasher_ticket.data.borrow(); - let ncn_vault_slasher_ticket = - NcnVaultSlasherTicket::try_from_slice_unchecked(&ncn_vault_slasher_ticket_data)?; - VaultNcnSlasherTicket::load( - program_id, - vault_ncn_slasher_ticket, - vault_info, - ncn, - slasher, - false, - )?; - let vault_ncn_slasher_ticket_data = vault_ncn_slasher_ticket.data.borrow(); - let vault_ncn_slasher_ticket = - VaultNcnSlasherTicket::try_from_slice_unchecked(&vault_ncn_slasher_ticket_data)?; - let slot = Clock::get()?.slot; - let ncn_epoch = config.get_epoch_from_slot(slot)?; - - VaultNcnSlasherOperatorTicket::load( - program_id, - vault_ncn_slasher_operator_ticket, - vault_info, - ncn, - slasher, - operator, - ncn_epoch, - true, - )?; - let mut vault_ncn_slasher_operator_ticket_data = - vault_ncn_slasher_operator_ticket.data.borrow_mut(); - let vault_ncn_slasher_operator_ticket = - VaultNcnSlasherOperatorTicket::try_from_slice_unchecked_mut( - &mut vault_ncn_slasher_operator_ticket_data, - )?; - load_associated_token_account(vault_token_account, vault_info.key, &vault.supported_mint)?; - load_associated_token_account(slasher_token_account, slasher.key, &vault.supported_mint)?; - load_token_program(token_program)?; - - let slot = Clock::get()?.slot; - let epoch_length = config.epoch_length(); - - // The vault shall be up-to-date before slashing - vault.check_update_state_ok(Clock::get()?.slot, epoch_length)?; - vault.check_is_paused()?; - - // All ticket states shall be active or cooling down - check_states_active_or_cooling_down( - vault_ncn_slasher_ticket, - ncn_vault_slasher_ticket, - ncn_operator_state, - operator_vault_ticket, - vault_ncn_ticket, - ncn_vault_ticket, - slot, - epoch_length, - )?; - - // The amount slashed for this operator shall not exceed the maximum slashable amount per epoch - vault_ncn_slasher_operator_ticket.check_slashing_amount_not_exceeded( - slash_amount, - vault_ncn_slasher_ticket.max_slashable_per_epoch(), - )?; - - // The VaultOperatorDelegation shall be slashed and the vault amounts shall be updated - slash_and_update_vault( - vault, - vault_operator_delegation, - vault_ncn_slasher_operator_ticket, - slash_amount, - )?; - - // transfer the slashed funds - let mut vault_seeds = Vault::seeds(&vault.base); - vault_seeds.push(vec![vault.bump]); - let vault_seeds_slice = vault_seeds - .iter() - .map(|seed| seed.as_slice()) - .collect::>(); - drop(vault_data); - invoke_signed( - &transfer( - &spl_token::id(), - vault_token_account.key, - slasher_token_account.key, - vault_info.key, - &[], - slash_amount, - )?, - &[ - vault_token_account.clone(), - slasher_token_account.clone(), - vault_info.clone(), - ], - &[vault_seeds_slice.as_slice()], - )?; - - Ok(()) -} - -#[allow(clippy::too_many_arguments)] -fn check_states_active_or_cooling_down( - vault_ncn_slasher_ticket: &VaultNcnSlasherTicket, - ncn_vault_slasher_ticket: &NcnVaultSlasherTicket, - ncn_operator_state: &NcnOperatorState, - operator_vault_ticket: &OperatorVaultTicket, - vault_ncn_ticket: &VaultNcnTicket, - ncn_vault_ticket: &NcnVaultTicket, - slot: u64, - epoch_length: u64, -) -> ProgramResult { - if !vault_ncn_slasher_ticket - .state - .is_active_or_cooldown(slot, epoch_length) - { - msg!("Vault NCN slasher ticket is not active or in cooldown"); - return Err(VaultError::VaultNcnSlasherTicketUnslashable.into()); - } - if !ncn_vault_slasher_ticket - .state - .is_active_or_cooldown(slot, epoch_length) - { - msg!("NCN vault slasher ticket is not active or in cooldown"); - return Err(VaultError::NcnVaultSlasherTicketUnslashable.into()); - } - if !ncn_operator_state - .ncn_opt_in_state - .is_active_or_cooldown(slot, epoch_length) - { - msg!("NCN opt-in to operator is not active or in cooldown"); - return Err(VaultError::NcnOperatorStateUnslashable.into()); - } - if !ncn_operator_state - .operator_opt_in_state - .is_active_or_cooldown(slot, epoch_length) - { - msg!("Operator opt-in to NCN is not active or in cooldown"); - return Err(VaultError::NcnOperatorStateUnslashable.into()); - } - if !operator_vault_ticket - .state - .is_active_or_cooldown(slot, epoch_length) - { - msg!("Operator vault ticket is not active or in cooldown"); - return Err(VaultError::OperatorVaultTicketUnslashable.into()); - } - if !vault_ncn_ticket - .state - .is_active_or_cooldown(slot, epoch_length) - { - msg!("Vault NCN ticket is not active or in cooldown"); - return Err(VaultError::VaultNcnTicketUnslashable.into()); - } - if !ncn_vault_ticket - .state - .is_active_or_cooldown(slot, epoch_length) - { - msg!("NCN vault ticket is not active or in cooldown"); - return Err(VaultError::NcnVaultTicketUnslashable.into()); - } - Ok(()) -} - -/// Slashes the vault and updates the vault amounts based on the slashing amount. -fn slash_and_update_vault( - vault: &mut Vault, - vault_operator_delegation: &mut VaultOperatorDelegation, - vault_ncn_slasher_operator_ticket: &mut VaultNcnSlasherOperatorTicket, - slash_amount: u64, -) -> ProgramResult { - // undo the delegation, slash then accumulate the delegation - vault - .delegation_state - .subtract(&vault_operator_delegation.delegation_state)?; - vault_operator_delegation - .delegation_state - .slash(slash_amount)?; - vault - .delegation_state - .accumulate(&vault_operator_delegation.delegation_state)?; - - vault.decrement_tokens_deposited(slash_amount)?; - vault_ncn_slasher_operator_ticket.increment_slashed(slash_amount)?; - Ok(()) -} diff --git a/vault_sdk/src/instruction.rs b/vault_sdk/src/instruction.rs index 0c703196..c0f5514d 100644 --- a/vault_sdk/src/instruction.rs +++ b/vault_sdk/src/instruction.rs @@ -309,26 +309,7 @@ pub enum VaultInstruction { uri: String, }, - /// Slashes an amount of tokens from the vault - #[account(0, name = "config")] - #[account(1, writable, name = "vault")] - #[account(2, name = "ncn")] - #[account(3, name = "operator")] - #[account(4, signer, name = "slasher")] - #[account(5, name = "ncn_operator_state")] - #[account(6, name = "ncn_vault_ticket")] - #[account(7, name = "operator_vault_ticket")] - #[account(8, name = "vault_ncn_ticket")] - #[account(9, writable, name = "vault_operator_delegation")] - #[account(10, name = "ncn_vault_slasher_ticket")] - #[account(11, name = "vault_ncn_slasher_ticket")] - #[account(12, writable, name = "vault_ncn_slasher_operator_ticket")] - #[account(13, writable, name = "vault_token_account")] - #[account(14, name = "slasher_token_account")] - #[account(15, name = "token_program")] - Slash { - amount: u64 - }, + } #[derive(Debug, BorshSerialize, BorshDeserialize)] diff --git a/vault_sdk/src/sdk.rs b/vault_sdk/src/sdk.rs index 534e2026..38e9b44a 100644 --- a/vault_sdk/src/sdk.rs +++ b/vault_sdk/src/sdk.rs @@ -536,51 +536,6 @@ pub fn initialize_vault_ncn_slasher_operator_ticket( } } -#[allow(clippy::too_many_arguments)] -pub fn slash( - program_id: &Pubkey, - config: &Pubkey, - vault: &Pubkey, - ncn: &Pubkey, - operator: &Pubkey, - slasher: &Pubkey, - ncn_operator_state: &Pubkey, - ncn_vault_ticket: &Pubkey, - operator_vault_ticket: &Pubkey, - vault_ncn_ticket: &Pubkey, - vault_operator_delegation: &Pubkey, - ncn_vault_slasher_ticket: &Pubkey, - vault_ncn_slasher_ticket: &Pubkey, - vault_ncn_slasher_operator_ticket: &Pubkey, - vault_token_account: &Pubkey, - slasher_token_account: &Pubkey, - amount: u64, -) -> Instruction { - let accounts = vec![ - AccountMeta::new_readonly(*config, false), - AccountMeta::new(*vault, false), - AccountMeta::new_readonly(*ncn, false), - AccountMeta::new_readonly(*operator, false), - AccountMeta::new_readonly(*slasher, true), - AccountMeta::new_readonly(*ncn_operator_state, false), - AccountMeta::new_readonly(*ncn_vault_ticket, false), - AccountMeta::new_readonly(*operator_vault_ticket, false), - AccountMeta::new_readonly(*vault_ncn_ticket, false), - AccountMeta::new(*vault_operator_delegation, false), - AccountMeta::new_readonly(*ncn_vault_slasher_ticket, false), - AccountMeta::new_readonly(*vault_ncn_slasher_ticket, false), - AccountMeta::new(*vault_ncn_slasher_operator_ticket, false), - AccountMeta::new(*vault_token_account, false), - AccountMeta::new(*slasher_token_account, false), - AccountMeta::new_readonly(spl_token::id(), false), - ]; - Instruction { - program_id: *program_id, - accounts, - data: VaultInstruction::Slash { amount }.try_to_vec().unwrap(), - } -} - #[allow(clippy::too_many_arguments)] pub fn enqueue_withdrawal( program_id: &Pubkey, From 656eb54b00ea38dead4ec46151e26d9877fb6beb Mon Sep 17 00:00:00 2001 From: Evan B Date: Thu, 24 Oct 2024 18:43:36 -0400 Subject: [PATCH 05/11] Rotate operator update indices (#144) Rotates the operator index we start on based on epoch % num_operators. Internally just tracks a counter from 0..num_operators and increments that, converting to the expected index number in check_and_update_index(). --------- Co-authored-by: Coach Chuck <169060940+coachchucksol@users.noreply.github.com> --- .../tests/fixtures/vault_client.rs | 8 +- .../tests/vault/close_update_state_tracker.rs | 16 +- .../vault/crank_vault_update_state_tracker.rs | 45 ++++-- vault_core/src/vault_update_state_tracker.rs | 147 ++++++++++++++++-- .../src/close_update_state_tracker.rs | 3 +- .../src/crank_vault_update_state_tracker.rs | 3 +- 6 files changed, 185 insertions(+), 37 deletions(-) diff --git a/integration_tests/tests/fixtures/vault_client.rs b/integration_tests/tests/fixtures/vault_client.rs index 606a61d7..13958c07 100644 --- a/integration_tests/tests/fixtures/vault_client.rs +++ b/integration_tests/tests/fixtures/vault_client.rs @@ -968,16 +968,20 @@ impl VaultProgramClient { .get_config(&Config::find_program_address(&jito_vault_program::id()).0) .await?; + let ncn_epoch = slot / config.epoch_length(); + let vault_update_state_tracker = VaultUpdateStateTracker::find_program_address( &jito_vault_program::id(), vault_pubkey, - slot / config.epoch_length(), + ncn_epoch, ) .0; self.initialize_vault_update_state_tracker(vault_pubkey, &vault_update_state_tracker) .await?; - for operator in operators { + for i in 0..operators.len() { + let operator_index = (i + ncn_epoch as usize) % operators.len(); + let operator = &operators[operator_index]; self.crank_vault_update_state_tracker( vault_pubkey, operator, diff --git a/integration_tests/tests/vault/close_update_state_tracker.rs b/integration_tests/tests/vault/close_update_state_tracker.rs index b6d690dd..913a85e7 100644 --- a/integration_tests/tests/vault/close_update_state_tracker.rs +++ b/integration_tests/tests/vault/close_update_state_tracker.rs @@ -125,10 +125,12 @@ mod tests { .await .unwrap(); + let operator_index = (ncn_epoch % num_operators as u64) as usize; + vault_program_client .do_crank_vault_update_state_tracker( &vault_root.vault_pubkey, - &operator_roots[0].operator_pubkey, + &operator_roots[operator_index].operator_pubkey, ) .await .unwrap(); @@ -212,18 +214,22 @@ mod tests { .await .unwrap(); + let operator_index = (old_ncn_epoch % num_operators as u64) as usize; + vault_program_client .do_crank_vault_update_state_tracker( &vault_root.vault_pubkey, - &operator_roots[0].operator_pubkey, + &operator_roots[operator_index].operator_pubkey, ) .await .unwrap(); + let operator_index = (operator_index + 1) % num_operators as usize; + vault_program_client .do_crank_vault_update_state_tracker( &vault_root.vault_pubkey, - &operator_roots[1].operator_pubkey, + &operator_roots[operator_index].operator_pubkey, ) .await .unwrap(); @@ -393,10 +399,12 @@ mod tests { .await .unwrap(); + let operator_index = (ncn_epoch % num_operators as u64) as usize; + vault_program_client .do_crank_vault_update_state_tracker( &vault_root.vault_pubkey, - &operator_roots[0].operator_pubkey, + &operator_roots[operator_index].operator_pubkey, ) .await .unwrap(); diff --git a/integration_tests/tests/vault/crank_vault_update_state_tracker.rs b/integration_tests/tests/vault/crank_vault_update_state_tracker.rs index 1da973bd..dc49162d 100644 --- a/integration_tests/tests/vault/crank_vault_update_state_tracker.rs +++ b/integration_tests/tests/vault/crank_vault_update_state_tracker.rs @@ -180,6 +180,7 @@ mod tests { let slot = fixture.get_current_slot().await.unwrap(); let ncn_epoch = slot / config.epoch_length(); + let operator_index = (ncn_epoch % num_operators as u64) as usize; let vault_update_state_tracker_pubkey = VaultUpdateStateTracker::find_program_address( &jito_vault_program::id(), @@ -198,7 +199,7 @@ mod tests { vault_program_client .do_crank_vault_update_state_tracker( &vault_root.vault_pubkey, - &operator_roots[0].operator_pubkey, + &operator_roots[operator_index].operator_pubkey, ) .await .unwrap(); @@ -206,16 +207,21 @@ mod tests { .get_vault_update_state_tracker(&vault_root.vault_pubkey, ncn_epoch) .await .unwrap(); - assert_eq!(vault_update_state_tracker.last_updated_index(), 0); + assert_eq!( + vault_update_state_tracker.last_updated_index(), + operator_index as u64 + ); assert_eq!( vault_update_state_tracker.delegation_state, DelegationState::new(50000, 0, 0) ); + let operator_index = (operator_index + 1) % num_operators as usize; + vault_program_client .do_crank_vault_update_state_tracker( &vault_root.vault_pubkey, - &operator_roots[1].operator_pubkey, + &operator_roots[operator_index].operator_pubkey, ) .await .unwrap(); @@ -223,7 +229,10 @@ mod tests { .get_vault_update_state_tracker(&vault_root.vault_pubkey, ncn_epoch) .await .unwrap(); - assert_eq!(vault_update_state_tracker.last_updated_index(), 1); + assert_eq!( + vault_update_state_tracker.last_updated_index(), + operator_index as u64 + ); assert_eq!( vault_update_state_tracker.delegation_state, DelegationState::new(100000, 0, 0) @@ -286,6 +295,7 @@ mod tests { let slot = fixture.get_current_slot().await.unwrap(); let ncn_epoch = slot / config.epoch_length(); + let operator_index = (ncn_epoch % num_operators as u64) as usize; let vault_update_state_tracker_pubkey = VaultUpdateStateTracker::find_program_address( &jito_vault_program::id(), @@ -304,7 +314,7 @@ mod tests { vault_program_client .do_crank_vault_update_state_tracker( &vault_root.vault_pubkey, - &operator_roots[0].operator_pubkey, + &operator_roots[operator_index].operator_pubkey, ) .await .unwrap(); @@ -314,14 +324,14 @@ mod tests { let result = vault_program_client .do_crank_vault_update_state_tracker( &vault_root.vault_pubkey, - &operator_roots[0].operator_pubkey, + &operator_roots[operator_index].operator_pubkey, ) .await; assert_vault_error(result, VaultError::VaultOperatorDelegationIsUpdated); } #[tokio::test] - async fn test_crank_vault_update_state_tracker_skip_zero_fails() { + async fn test_crank_vault_update_state_tracker_skip_start_index_fails() { let mut fixture = TestBuilder::new().await; let deposit_fee_bps = 0; @@ -376,6 +386,7 @@ mod tests { let slot = fixture.get_current_slot().await.unwrap(); let ncn_epoch = slot / config.epoch_length(); + let operator_index = ((ncn_epoch + 1) % num_operators as u64) as usize; let vault_update_state_tracker_pubkey = VaultUpdateStateTracker::find_program_address( &jito_vault_program::id(), @@ -394,7 +405,7 @@ mod tests { let result = vault_program_client .do_crank_vault_update_state_tracker( &vault_root.vault_pubkey, - &operator_roots[1].operator_pubkey, + &operator_roots[operator_index].operator_pubkey, ) .await; assert_vault_error(result, VaultError::VaultUpdateIncorrectIndex); @@ -456,7 +467,7 @@ mod tests { let slot = fixture.get_current_slot().await.unwrap(); let ncn_epoch = slot / config.epoch_length(); - + let operator_index = (ncn_epoch % num_operators as u64) as usize; let vault_update_state_tracker_pubkey = VaultUpdateStateTracker::find_program_address( &jito_vault_program::id(), &vault_root.vault_pubkey, @@ -474,15 +485,17 @@ mod tests { vault_program_client .do_crank_vault_update_state_tracker( &vault_root.vault_pubkey, - &operator_roots[0].operator_pubkey, + &operator_roots[operator_index].operator_pubkey, ) .await .unwrap(); + let operator_index = (operator_index + 2) % num_operators as usize; + let result = vault_program_client .do_crank_vault_update_state_tracker( &vault_root.vault_pubkey, - &operator_roots[2].operator_pubkey, + &operator_roots[operator_index].operator_pubkey, ) .await; assert_vault_error(result, VaultError::VaultUpdateIncorrectIndex); @@ -555,6 +568,7 @@ mod tests { let slot = fixture.get_current_slot().await.unwrap(); let ncn_epoch = slot / config.epoch_length(); + let operator_index = (ncn_epoch % num_operators as u64) as usize; let vault_update_state_tracker_pubkey = VaultUpdateStateTracker::find_program_address( &jito_vault_program::id(), @@ -573,7 +587,7 @@ mod tests { vault_program_client .do_crank_vault_update_state_tracker( &vault_root.vault_pubkey, - &operator_roots[0].operator_pubkey, + &operator_roots[operator_index].operator_pubkey, ) .await .unwrap(); @@ -588,6 +602,7 @@ mod tests { let slot = fixture.get_current_slot().await.unwrap(); let ncn_epoch = slot / config.epoch_length(); + let operator_index = (ncn_epoch % num_operators as u64) as usize; let vault_update_state_tracker_pubkey = VaultUpdateStateTracker::find_program_address( &jito_vault_program::id(), &vault_root.vault_pubkey, @@ -604,7 +619,7 @@ mod tests { vault_program_client .do_crank_vault_update_state_tracker( &vault_root.vault_pubkey, - &operator_roots[0].operator_pubkey, + &operator_roots[operator_index].operator_pubkey, ) .await .unwrap(); @@ -618,11 +633,13 @@ mod tests { DelegationState::new(25000, 0, 0) ); + let operator_index = (operator_index + 1) % num_operators as usize; + // active -> cooldown (2 epochs since last update) vault_program_client .do_crank_vault_update_state_tracker( &vault_root.vault_pubkey, - &operator_roots[1].operator_pubkey, + &operator_roots[operator_index].operator_pubkey, ) .await .unwrap(); diff --git a/vault_core/src/vault_update_state_tracker.rs b/vault_core/src/vault_update_state_tracker.rs index 4b84007e..fbf53367 100644 --- a/vault_core/src/vault_update_state_tracker.rs +++ b/vault_core/src/vault_update_state_tracker.rs @@ -15,7 +15,7 @@ pub struct VaultUpdateStateTracker { /// The NCN epoch for which the delegations are to be updated ncn_epoch: PodU64, - /// The update index of the vault + /// The last updated index of the vault last_updated_index: PodU64, /// The amount of additional assets that need unstaking to fulfill VRT withdrawals @@ -71,25 +71,62 @@ impl VaultUpdateStateTracker { Ok(()) } - pub fn check_and_update_index(&mut self, index: u64) -> Result<(), VaultError> { + /// Checks and updates the index of the vault update state tracker + /// Index starts at different values depending on the NCN epoch to prevent + /// any single operator from getting starved + pub fn check_and_update_index( + &mut self, + index: u64, + num_operators: u64, + ) -> Result<(), VaultError> { if self.last_updated_index() == u64::MAX { - if index != 0 { + let start_index = self + .ncn_epoch() + .checked_rem(num_operators) + .ok_or(VaultError::DivisionByZero)?; + if index != start_index { msg!("VaultUpdateStateTracker incorrect index"); return Err(VaultError::VaultUpdateIncorrectIndex); } - } else if index - != self + } else { + let next_index = self .last_updated_index() .checked_add(1) - .ok_or(VaultError::ArithmeticOverflow)? - { - msg!("VaultUpdateStateTracker incorrect index"); - return Err(VaultError::VaultUpdateIncorrectIndex); + .and_then(|i| i.checked_rem(num_operators)) + .ok_or(VaultError::ArithmeticOverflow)?; + + if index != next_index { + msg!("VaultUpdateStateTracker incorrect index"); + return Err(VaultError::VaultUpdateIncorrectIndex); + } } self.last_updated_index = PodU64::from(index); Ok(()) } + pub fn all_operators_updated(&self, num_operators: u64) -> Result { + if self.last_updated_index() == u64::MAX { + return Ok(false); + } + + let start_index = self + .ncn_epoch() + .checked_rem(num_operators) + .ok_or(VaultError::DivisionByZero)?; + + if start_index == 0 { + return Ok(self.last_updated_index() + == num_operators + .checked_sub(1) + .ok_or(VaultError::ArithmeticUnderflow)?); + } + + Ok(self.last_updated_index() + == start_index + .checked_sub(1) + .ok_or(VaultError::ArithmeticUnderflow)?) + } + /// Returns the seeds for the PDA /// /// # Arguments @@ -175,7 +212,9 @@ mod tests { let mut vault_update_state_tracker = VaultUpdateStateTracker::new(Pubkey::new_unique(), 0, 0, 0); - assert!(vault_update_state_tracker.check_and_update_index(0).is_ok()); + assert!(vault_update_state_tracker + .check_and_update_index(0, 1) + .is_ok()); } #[test] @@ -183,7 +222,7 @@ mod tests { let mut vault_update_state_tracker = VaultUpdateStateTracker::new(Pubkey::new_unique(), 0, 0, 0); assert_eq!( - vault_update_state_tracker.check_and_update_index(1), + vault_update_state_tracker.check_and_update_index(1, 2), Err(VaultError::VaultUpdateIncorrectIndex) ); } @@ -192,15 +231,95 @@ mod tests { fn test_update_index_skip_index_fails() { let mut vault_update_state_tracker = VaultUpdateStateTracker::new(Pubkey::new_unique(), 0, 0, 0); + let n = 4; vault_update_state_tracker - .check_and_update_index(0) + .check_and_update_index(0, n) .unwrap(); vault_update_state_tracker - .check_and_update_index(1) + .check_and_update_index(1, n) .unwrap(); assert_eq!( - vault_update_state_tracker.check_and_update_index(3), + vault_update_state_tracker.check_and_update_index(3, n), + Err(VaultError::VaultUpdateIncorrectIndex) + ); + } + + #[test] + fn test_update_index_wraparound() { + let n = 4; + // Epoch 6, offset is 6 % 4 = 2 + let mut vault_update_state_tracker = + VaultUpdateStateTracker::new(Pubkey::new_unique(), 6, 0, 0); + + assert_eq!( + vault_update_state_tracker.check_and_update_index(0, n), Err(VaultError::VaultUpdateIncorrectIndex) ); + + vault_update_state_tracker + .check_and_update_index(2, n) + .unwrap(); + assert_eq!(vault_update_state_tracker.last_updated_index(), 2); + + vault_update_state_tracker + .check_and_update_index(3, n) + .unwrap(); + + vault_update_state_tracker + .check_and_update_index(0, n) + .unwrap(); + assert_eq!(vault_update_state_tracker.last_updated_index(), 0); + + vault_update_state_tracker + .check_and_update_index(1, n) + .unwrap(); + assert_eq!(vault_update_state_tracker.last_updated_index(), 1); + + assert!(vault_update_state_tracker.all_operators_updated(n).unwrap()); + } + + #[test] + fn test_all_operators_updated() { + let n = 4; + + // Cranking not started + let mut tracker = VaultUpdateStateTracker::new(Pubkey::new_unique(), 0, 0, 0); + assert_eq!(tracker.all_operators_updated(n).unwrap(), false); + + // Middle of cranking + tracker.last_updated_index = PodU64::from(1); + assert_eq!(tracker.all_operators_updated(n).unwrap(), false); + + // All operators updated, start_index = 0 + tracker.last_updated_index = PodU64::from(3); + assert_eq!(tracker.all_operators_updated(n).unwrap(), true); + + // start_index = operators - 1 + let mut tracker = VaultUpdateStateTracker::new(Pubkey::new_unique(), n - 1, 0, 0); + tracker.last_updated_index = PodU64::from(n - 2); + assert_eq!(tracker.all_operators_updated(n).unwrap(), true); + + // start_index = operators, last_updated = start_index - 1 + let mut tracker = VaultUpdateStateTracker::new(Pubkey::new_unique(), n, 0, 0); + tracker.last_updated_index = PodU64::from(0); + assert_eq!(tracker.all_operators_updated(n).unwrap(), false); + + // All operators updated, start_index != 0 + let mut tracker = VaultUpdateStateTracker::new(Pubkey::new_unique(), 1, 0, 0); + tracker.last_updated_index = PodU64::from(0); + assert_eq!(tracker.all_operators_updated(n).unwrap(), true); + + // Single operator + let mut tracker = VaultUpdateStateTracker::new(Pubkey::new_unique(), 2, 0, 0); + tracker.last_updated_index = PodU64::from(0); + assert_eq!(tracker.all_operators_updated(1).unwrap(), true); + + // Error - division by zero + let mut tracker = VaultUpdateStateTracker::new(Pubkey::new_unique(), 0, 0, 0); + tracker.last_updated_index = PodU64::from(0); + assert_eq!( + tracker.all_operators_updated(0), + Err(VaultError::DivisionByZero) + ); } } diff --git a/vault_program/src/close_update_state_tracker.rs b/vault_program/src/close_update_state_tracker.rs index f77f8507..7573e129 100644 --- a/vault_program/src/close_update_state_tracker.rs +++ b/vault_program/src/close_update_state_tracker.rs @@ -55,8 +55,7 @@ pub fn process_close_vault_update_state_tracker( } else { // The VaultUpdateStateTracker shall have updated every operator ticket before closing if vault.operator_count() > 0 - && vault_update_state_tracker.last_updated_index() - != vault.operator_count().saturating_sub(1) + && !vault_update_state_tracker.all_operators_updated(vault.operator_count())? { msg!("VaultUpdateStateTracker is not fully updated"); return Err(VaultError::VaultUpdateStateNotFinishedUpdating.into()); diff --git a/vault_program/src/crank_vault_update_state_tracker.rs b/vault_program/src/crank_vault_update_state_tracker.rs index 2b4cd2ca..4a442245 100644 --- a/vault_program/src/crank_vault_update_state_tracker.rs +++ b/vault_program/src/crank_vault_update_state_tracker.rs @@ -58,7 +58,8 @@ pub fn process_crank_vault_update_state_tracker( vault.check_is_paused()?; vault_operator_delegation.check_is_already_updated(slot, config.epoch_length())?; - vault_update_state_tracker.check_and_update_index(vault_operator_delegation.index())?; + vault_update_state_tracker + .check_and_update_index(vault_operator_delegation.index(), vault.operator_count())?; match WithdrawalAllocationMethod::try_from( vault_update_state_tracker.withdrawal_allocation_method, From cba7121de7ed5d5dd1d3b0bfc388963faff31a18 Mon Sep 17 00:00:00 2001 From: Christian Krueger Date: Wed, 11 Dec 2024 10:06:07 -0700 Subject: [PATCH 06/11] tests passing --- clients/js/vault_client/errors/jitoVault.ts | 8 ++++++-- .../src/generated/errors/jito_vault.rs | 7 +++++-- idl/jito_vault.json | 5 +++++ .../vault/crank_vault_update_state_tracker.rs | 12 +++++++++--- .../initialize_vault_update_state_tracker.rs | 19 +++++++++++++++---- 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/clients/js/vault_client/errors/jitoVault.ts b/clients/js/vault_client/errors/jitoVault.ts index 0eb12ee9..f930eb68 100644 --- a/clients/js/vault_client/errors/jitoVault.ts +++ b/clients/js/vault_client/errors/jitoVault.ts @@ -128,8 +128,10 @@ export const JITO_VAULT_ERROR__INVALID_EPOCH_LENGTH = 0x41e; // 1054 export const JITO_VAULT_ERROR__VAULT_REWARD_FEE_DELTA_TOO_LARGE = 0x41f; // 1055 /** VaultRewardFeeIsZero: VaultRewardFeeIsZero */ export const JITO_VAULT_ERROR__VAULT_REWARD_FEE_IS_ZERO = 0x420; // 1056 +/** VrtOutCannotBeZero: VrtOutCannotBeZero */ +export const JITO_VAULT_ERROR__VRT_OUT_CANNOT_BE_ZERO = 0x421; // 1057 /** NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate: NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate */ -export const JITO_VAULT_ERROR__NON_ZERO_ADDITIONAL_ASSETS_NEEDED_FOR_WITHDRAWAL_AT_END_OF_UPDATE = 0x421; // 1057 +export const JITO_VAULT_ERROR__NON_ZERO_ADDITIONAL_ASSETS_NEEDED_FOR_WITHDRAWAL_AT_END_OF_UPDATE = 0x422; // 1058 /** ArithmeticOverflow: ArithmeticOverflow */ export const JITO_VAULT_ERROR__ARITHMETIC_OVERFLOW = 0xbb8; // 3000 /** ArithmeticUnderflow: ArithmeticUnderflow */ @@ -198,7 +200,8 @@ export type JitoVaultError = | typeof JITO_VAULT_ERROR__VAULT_UNDERFLOW | typeof JITO_VAULT_ERROR__VAULT_UPDATE_INCORRECT_INDEX | typeof JITO_VAULT_ERROR__VAULT_UPDATE_NEEDED - | typeof JITO_VAULT_ERROR__VAULT_UPDATE_STATE_NOT_FINISHED_UPDATING; + | typeof JITO_VAULT_ERROR__VAULT_UPDATE_STATE_NOT_FINISHED_UPDATING + | typeof JITO_VAULT_ERROR__VRT_OUT_CANNOT_BE_ZERO; let jitoVaultErrorMessages: Record | undefined; if (process.env.NODE_ENV !== 'production') { @@ -264,6 +267,7 @@ if (process.env.NODE_ENV !== 'production') { [JITO_VAULT_ERROR__VAULT_UPDATE_INCORRECT_INDEX]: `VaultUpdateIncorrectIndex`, [JITO_VAULT_ERROR__VAULT_UPDATE_NEEDED]: `VaultUpdateNeeded`, [JITO_VAULT_ERROR__VAULT_UPDATE_STATE_NOT_FINISHED_UPDATING]: `VaultUpdateStateNotFinishedUpdating`, + [JITO_VAULT_ERROR__VRT_OUT_CANNOT_BE_ZERO]: `VrtOutCannotBeZero`, }; } diff --git a/clients/rust/vault_client/src/generated/errors/jito_vault.rs b/clients/rust/vault_client/src/generated/errors/jito_vault.rs index 50fd5817..2b83d2cc 100644 --- a/clients/rust/vault_client/src/generated/errors/jito_vault.rs +++ b/clients/rust/vault_client/src/generated/errors/jito_vault.rs @@ -180,9 +180,12 @@ pub enum JitoVaultError { /// 1056 - VaultRewardFeeIsZero #[error("VaultRewardFeeIsZero")] VaultRewardFeeIsZero = 0x420, - /// 1057 - NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate + /// 1057 - VrtOutCannotBeZero + #[error("VrtOutCannotBeZero")] + VrtOutCannotBeZero = 0x421, + /// 1058 - NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate #[error("NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate")] - NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate = 0x421, + NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate = 0x422, /// 3000 - ArithmeticOverflow #[error("ArithmeticOverflow")] ArithmeticOverflow = 0xBB8, diff --git a/idl/jito_vault.json b/idl/jito_vault.json index 921a5733..33c05a23 100644 --- a/idl/jito_vault.json +++ b/idl/jito_vault.json @@ -2425,6 +2425,11 @@ }, { "code": 1057, + "name": "VrtOutCannotBeZero", + "msg": "VrtOutCannotBeZero" + }, + { + "code": 1058, "name": "NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate", "msg": "NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate" }, diff --git a/integration_tests/tests/vault/crank_vault_update_state_tracker.rs b/integration_tests/tests/vault/crank_vault_update_state_tracker.rs index c890ad6e..989f377e 100644 --- a/integration_tests/tests/vault/crank_vault_update_state_tracker.rs +++ b/integration_tests/tests/vault/crank_vault_update_state_tracker.rs @@ -746,7 +746,7 @@ mod tests { .await .unwrap(); - assert_eq!(vault.additional_assets_need_unstaking(), 75_000); + assert_eq!(vault.additional_assets_need_unstaking(), 65_000); vault_program_client .do_crank_vault_update_state_tracker( @@ -771,7 +771,10 @@ mod tests { let last_full_update_epoch = vault.last_full_state_update_slot() / config.epoch_length(); assert_eq!(last_full_update_epoch, 0); - assert_eq!(vault.additional_assets_need_unstaking(), 25_000); + assert_eq!( + vault.additional_assets_need_unstaking(), + 25_000 - Vault::INITIALIZATION_TOKEN_AMOUNT + ); let first_operator_delegation = vault_program_client .get_vault_operator_delegation( @@ -871,7 +874,10 @@ mod tests { .await .unwrap(); - assert_eq!(vault.delegation_state.staked_amount(), 25_000); + assert_eq!( + vault.delegation_state.staked_amount(), + 25_000 + Vault::INITIALIZATION_TOKEN_AMOUNT + ); assert_eq!(vault.delegation_state.enqueued_for_cooldown_amount(), 0); assert_eq!(vault.vrt_ready_to_claim_amount(), 75_000); } diff --git a/integration_tests/tests/vault/initialize_vault_update_state_tracker.rs b/integration_tests/tests/vault/initialize_vault_update_state_tracker.rs index 914a802e..94c5433c 100644 --- a/integration_tests/tests/vault/initialize_vault_update_state_tracker.rs +++ b/integration_tests/tests/vault/initialize_vault_update_state_tracker.rs @@ -1,6 +1,8 @@ #[cfg(test)] mod tests { - use jito_vault_core::{config::Config, vault_update_state_tracker::VaultUpdateStateTracker}; + use jito_vault_core::{ + config::Config, vault::Vault, vault_update_state_tracker::VaultUpdateStateTracker, + }; use jito_vault_sdk::error::VaultError; use solana_program::instruction::InstructionError; use solana_sdk::{signature::Keypair, signer::Signer}; @@ -453,7 +455,10 @@ mod tests { .await .unwrap(); - assert_eq!(vault.additional_assets_need_unstaking(), 75_000); + assert_eq!( + vault.additional_assets_need_unstaking(), + 75_000 - Vault::INITIALIZATION_TOKEN_AMOUNT + ); // Update fees let new_withdrawal_fee_bps = 10; @@ -490,7 +495,10 @@ mod tests { .unwrap(); // no assets cooled down, additional_assets_need_unstaking = 75_000 - assert_eq!(vault.additional_assets_need_unstaking(), 75_000); + assert_eq!( + vault.additional_assets_need_unstaking(), + 75_000 - Vault::INITIALIZATION_TOKEN_AMOUNT + ); let vault_update_state_tracker_pubkey = VaultUpdateStateTracker::find_program_address( &jito_vault_program::id(), @@ -545,7 +553,10 @@ mod tests { .await .unwrap(); - assert_eq!(vault.delegation_state.staked_amount(), 25_000); + assert_eq!( + vault.delegation_state.staked_amount(), + 25_000 + Vault::INITIALIZATION_TOKEN_AMOUNT + ); assert_eq!(vault.delegation_state.enqueued_for_cooldown_amount(), 0); assert_eq!(vault.vrt_ready_to_claim_amount(), 75_000); } From 21a32c3347479e64d1872794a6bcf316d9ce6149 Mon Sep 17 00:00:00 2001 From: Christian Krueger Date: Fri, 13 Dec 2024 09:35:54 -0700 Subject: [PATCH 07/11] working --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2dbdfd62..d3adb10c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -24,7 +24,7 @@ jobs: uses: baptiste0928/cargo-install@v3 with: crate: cargo-audit - - run: cargo audit --ignore RUSTSEC-2022-0093 --ignore RUSTSEC-2023-0065 --ignore RUSTSEC-2024-0344 + - run: cargo audit --ignore RUSTSEC-2022-0093 --ignore RUSTSEC-2023-0065 --ignore RUSTSEC-2024-0344 --ignore RUSTSEC-2024-0421 code_gen: # cargo b && ./target/debug/jito-restaking-cli --markdown-help > ./docs/_tools/00_cli.md && ./target/debug/jito-shank-cli && yarn generate-clients && cargo b From 937a85f54dea42e09459164e82f2c975fed07b29 Mon Sep 17 00:00:00 2001 From: Christian Krueger Date: Fri, 20 Dec 2024 13:21:22 -0700 Subject: [PATCH 08/11] working --- Cargo.lock | 1 + idl/jito_vault.json | 138 +++++++++------- .../tests/fixtures/vault_client.rs | 36 ++++- .../tests/vault/add_delegation.rs | 6 +- .../tests/vault/burn_withdrawal_ticket.rs | 23 +-- .../vault/crank_vault_update_state_tracker.rs | 25 ++- .../tests/vault/initialize_vault.rs | 14 +- .../initialize_vault_update_state_tracker.rs | 6 +- integration_tests/tests/vault/reward_fee.rs | 19 ++- .../tests/vault/update_vault_balance.rs | 4 +- vault_core/src/burn_vault.rs | 62 ++++++++ vault_core/src/lib.rs | 1 + vault_core/src/vault.rs | 23 ++- vault_program/src/initialize_vault.rs | 149 +++++++++++++----- vault_program/src/lib.rs | 2 + vault_sdk/Cargo.toml | 1 + vault_sdk/src/error.rs | 2 + vault_sdk/src/instruction.rs | 12 +- vault_sdk/src/sdk.rs | 7 + 19 files changed, 390 insertions(+), 141 deletions(-) create mode 100644 vault_core/src/burn_vault.rs diff --git a/Cargo.lock b/Cargo.lock index b0153275..2cbce35e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2616,6 +2616,7 @@ dependencies = [ "solana-program", "solana-program-test", "solana-sdk", + "spl-associated-token-account", "spl-token", "thiserror", "tokio", diff --git a/idl/jito_vault.json b/idl/jito_vault.json index 33c05a23..c554ecb5 100644 --- a/idl/jito_vault.json +++ b/idl/jito_vault.json @@ -75,6 +75,16 @@ "isMut": true, "isSigner": false }, + { + "name": "burnVault", + "isMut": true, + "isSigner": false + }, + { + "name": "burnVaultVrtTokenAccount", + "isMut": true, + "isSigner": false + }, { "name": "admin", "isMut": true, @@ -94,6 +104,11 @@ "name": "tokenProgram", "isMut": false, "isSigner": false + }, + { + "name": "associatedTokenProgram", + "isMut": false, + "isSigner": false } ], "args": [ @@ -112,6 +127,10 @@ { "name": "decimals", "type": "u8" + }, + { + "name": "initializeTokenAmount", + "type": "u64" } ], "discriminant": { @@ -2145,291 +2164,296 @@ }, { "code": 1001, + "name": "VaultInitialAmountFailed", + "msg": "VaultInitialAmountFailed" + }, + { + "code": 1002, "name": "VaultInsufficientFunds", "msg": "VaultInsufficientFunds" }, { - "code": 1002, + "code": 1003, "name": "VaultOverflow", "msg": "VaultOverflow" }, { - "code": 1003, + "code": 1004, "name": "VaultOperatorAdminInvalid", "msg": "VaultOperatorAdminInvalid" }, { - "code": 1004, + "code": 1005, "name": "VaultAdminInvalid", "msg": "VaultAdminInvalid" }, { - "code": 1005, + "code": 1006, "name": "VaultCapacityAdminInvalid", "msg": "VaultCapacityAdminInvalid" }, { - "code": 1006, + "code": 1007, "name": "VaultMintBurnAdminInvalid", "msg": "VaultMintBurnAdminInvalid" }, { - "code": 1007, + "code": 1008, "name": "VaultDelegationAdminInvalid", "msg": "VaultDelegationAdminInvalid" }, { - "code": 1008, + "code": 1009, "name": "VaultDelegateAssetAdminInvalid", "msg": "VaultDelegateAssetAdminInvalid" }, { - "code": 1009, + "code": 1010, "name": "VaultCapacityExceeded", "msg": "VaultCapacityExceeded" }, { - "code": 1010, + "code": 1011, "name": "VaultSlasherAdminInvalid", "msg": "VaultSlasherAdminInvalid" }, { - "code": 1011, + "code": 1012, "name": "VaultNcnAdminInvalid", "msg": "VaultNcnAdminInvalid" }, { - "code": 1012, + "code": 1013, "name": "VaultFeeAdminInvalid", "msg": "VaultFeeAdminInvalid" }, { - "code": 1013, + "code": 1014, "name": "ConfigAdminInvalid", "msg": "ConfigAdminInvalid" }, { - "code": 1014, + "code": 1015, "name": "ConfigFeeAdminInvalid", "msg": "ConfigFeeAdminInvalid" }, { - "code": 1015, + "code": 1016, "name": "VaultFeeCapExceeded", "msg": "VaultFeeCapExceeded" }, { - "code": 1016, + "code": 1017, "name": "VaultFeeChangeTooSoon", "msg": "VaultFeeChangeTooSoon" }, { - "code": 1017, + "code": 1018, "name": "VaultFeeBumpTooLarge", "msg": "VaultFeeBumpTooLarge" }, { - "code": 1018, + "code": 1019, "name": "VaultUnderflow", "msg": "VaultUnderflow" }, { - "code": 1019, + "code": 1020, "name": "VaultUpdateNeeded", "msg": "VaultUpdateNeeded" }, { - "code": 1020, + "code": 1021, "name": "VaultIsUpdated", "msg": "VaultIsUpdated" }, { - "code": 1021, + "code": 1022, "name": "VaultOperatorDelegationUpdateNeeded", "msg": "VaultOperatorDelegationUpdateNeeded" }, { - "code": 1022, + "code": 1023, "name": "VaultOperatorDelegationIsUpdated", "msg": "VaultOperatorDelegationIsUpdated" }, { - "code": 1023, + "code": 1024, "name": "VaultUpdateIncorrectIndex", "msg": "VaultUpdateIncorrectIndex" }, { - "code": 1024, + "code": 1025, "name": "VaultUpdateStateNotFinishedUpdating", "msg": "VaultUpdateStateNotFinishedUpdating" }, { - "code": 1025, + "code": 1026, "name": "VaultSecurityOverflow", "msg": "VaultSecurityOverflow" }, { - "code": 1026, + "code": 1027, "name": "VaultSlashIncomplete", "msg": "VaultSlashIncomplete" }, { - "code": 1027, + "code": 1028, "name": "VaultSecurityUnderflow", "msg": "VaultSecurityUnderflow" }, { - "code": 1028, + "code": 1029, "name": "SlippageError", "msg": "SlippageError" }, { - "code": 1029, + "code": 1030, "name": "VaultStakerWithdrawalTicketNotWithdrawable", "msg": "VaultStakerWithdrawalTicketNotWithdrawable" }, { - "code": 1030, + "code": 1031, "name": "VaultNcnSlasherTicketFailedCooldown", "msg": "VaultNcnSlasherTicketFailedCooldown" }, { - "code": 1031, + "code": 1032, "name": "VaultNcnSlasherTicketFailedWarmup", "msg": "VaultNcnSlasherTicketFailedWarmup" }, { - "code": 1032, + "code": 1033, "name": "VaultNcnTicketFailedCooldown", "msg": "VaultNcnTicketFailedCooldown" }, { - "code": 1033, + "code": 1034, "name": "VaultNcnTicketFailedWarmup", "msg": "VaultNcnTicketFailedWarmup" }, { - "code": 1034, + "code": 1035, "name": "VaultNcnTicketUnslashable", "msg": "VaultNcnTicketUnslashable" }, { - "code": 1035, + "code": 1036, "name": "OperatorVaultTicketUnslashable", "msg": "OperatorVaultTicketUnslashable" }, { - "code": 1036, + "code": 1037, "name": "NcnOperatorStateUnslashable", "msg": "NcnOperatorStateUnslashable" }, { - "code": 1037, + "code": 1038, "name": "VaultNcnSlasherTicketUnslashable", "msg": "VaultNcnSlasherTicketUnslashable" }, { - "code": 1038, + "code": 1039, "name": "NcnVaultTicketUnslashable", "msg": "NcnVaultTicketUnslashable" }, { - "code": 1039, + "code": 1040, "name": "NcnVaultSlasherTicketUnslashable", "msg": "NcnVaultSlasherTicketUnslashable" }, { - "code": 1040, + "code": 1041, "name": "VaultMaxSlashedPerOperatorExceeded", "msg": "VaultMaxSlashedPerOperatorExceeded" }, { - "code": 1041, + "code": 1042, "name": "VaultStakerWithdrawalTicketInvalidStaker", "msg": "VaultStakerWithdrawalTicketInvalidStaker" }, { - "code": 1042, + "code": 1043, "name": "SlasherOverflow", "msg": "SlasherOverflow" }, { - "code": 1043, + "code": 1044, "name": "NcnOverflow", "msg": "NcnOverflow" }, { - "code": 1044, + "code": 1045, "name": "OperatorOverflow", "msg": "OperatorOverflow" }, { - "code": 1045, + "code": 1046, "name": "VaultDelegationZero", "msg": "VaultDelegationZero" }, { - "code": 1046, + "code": 1047, "name": "VaultCooldownZero", "msg": "VaultCooldownZero" }, { - "code": 1047, + "code": 1048, "name": "VaultBurnZero", "msg": "VaultBurnZero" }, { - "code": 1048, + "code": 1049, "name": "VaultEnqueueWithdrawalAmountZero", "msg": "VaultEnqueueWithdrawalAmountZero" }, { - "code": 1049, + "code": 1050, "name": "VaultMintZero", "msg": "VaultMintZero" }, { - "code": 1050, + "code": 1051, "name": "VaultIsPaused", "msg": "VaultIsPaused" }, { - "code": 1051, + "code": 1052, "name": "InvalidDepositor", "msg": "InvalidDepositor" }, { - "code": 1052, + "code": 1053, "name": "InvalidDepositTokenAccount", "msg": "InvalidDepositTokenAccount" }, { - "code": 1053, + "code": 1054, "name": "NoSupportedMintBalanceChange", "msg": "NoSupportedMintBalanceChange" }, { - "code": 1054, + "code": 1055, "name": "InvalidEpochLength", "msg": "InvalidEpochLength" }, { - "code": 1055, + "code": 1056, "name": "VaultRewardFeeDeltaTooLarge", "msg": "VaultRewardFeeDeltaTooLarge" }, { - "code": 1056, + "code": 1057, "name": "VaultRewardFeeIsZero", "msg": "VaultRewardFeeIsZero" }, { - "code": 1057, + "code": 1058, "name": "VrtOutCannotBeZero", "msg": "VrtOutCannotBeZero" }, { - "code": 1058, + "code": 1059, "name": "NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate", "msg": "NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate" }, diff --git a/integration_tests/tests/fixtures/vault_client.rs b/integration_tests/tests/fixtures/vault_client.rs index 02d97fa0..f8683331 100644 --- a/integration_tests/tests/fixtures/vault_client.rs +++ b/integration_tests/tests/fixtures/vault_client.rs @@ -7,7 +7,8 @@ use jito_restaking_core::{ operator_vault_ticket::OperatorVaultTicket, }; use jito_vault_core::{ - config::Config, vault::Vault, vault_ncn_slasher_operator_ticket::VaultNcnSlasherOperatorTicket, + burn_vault::BurnVault, config::Config, vault::Vault, + vault_ncn_slasher_operator_ticket::VaultNcnSlasherOperatorTicket, vault_ncn_slasher_ticket::VaultNcnSlasherTicket, vault_ncn_ticket::VaultNcnTicket, vault_operator_delegation::VaultOperatorDelegation, vault_staker_withdrawal_ticket::VaultStakerWithdrawalTicket, @@ -294,6 +295,8 @@ impl VaultProgramClient { ) -> Result { let vault_base = Keypair::new(); + let initialize_token_amount = Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT; + let vault_pubkey = Vault::find_program_address(&jito_vault_program::id(), &vault_base.pubkey()).0; @@ -304,17 +307,27 @@ impl VaultProgramClient { self.airdrop(&vault_admin.pubkey(), 100.0).await?; self.create_token_mint(&st_mint, &spl_token::id()).await?; + let admin_st_token_account = + get_associated_token_address(&vault_admin.pubkey(), &st_mint.pubkey()); + let vault_st_token_account = get_associated_token_address(&vault_pubkey, &st_mint.pubkey()); + + let burn_vault = + BurnVault::find_program_address(&jito_vault_program::id(), &vault_base.pubkey()).0; + + let burn_vault_vrt_token_account = + get_associated_token_address(&burn_vault, &vrt_mint.pubkey()); + // Needs to be created before initialize vault self.create_ata(&st_mint.pubkey(), &vault_pubkey).await?; self.create_ata(&st_mint.pubkey(), &vault_admin.pubkey()) .await?; - self.mint_spl_to(&st_mint.pubkey(), &vault_admin.pubkey(), 10_000) - .await?; - - let admin_st_token_account = - get_associated_token_address(&vault_admin.pubkey(), &st_mint.pubkey()); - let vault_st_token_account = get_associated_token_address(&vault_pubkey, &st_mint.pubkey()); + self.mint_spl_to( + &st_mint.pubkey(), + &vault_admin.pubkey(), + initialize_token_amount, + ) + .await?; self.initialize_vault( &Config::find_program_address(&jito_vault_program::id()).0, @@ -323,12 +336,15 @@ impl VaultProgramClient { &st_mint, &admin_st_token_account, &vault_st_token_account, + &burn_vault, + &burn_vault_vrt_token_account, &vault_admin, &vault_base, deposit_fee_bps, withdrawal_fee_bps, reward_fee_bps, decimals, + initialize_token_amount, ) .await?; @@ -702,12 +718,15 @@ impl VaultProgramClient { st_mint: &Keypair, admin_st_token_account: &Pubkey, vault_st_token_account: &Pubkey, + burn_vault: &Pubkey, + burn_vault_vrt_token_account: &Pubkey, vault_admin: &Keypair, vault_base: &Keypair, deposit_fee_bps: u16, withdrawal_fee_bps: u16, reward_fee_bps: u16, decimals: u8, + initialize_token_amount: u64, ) -> Result<(), TestError> { let blockhash = self.banks_client.get_latest_blockhash().await?; @@ -720,12 +739,15 @@ impl VaultProgramClient { &st_mint.pubkey(), admin_st_token_account, vault_st_token_account, + burn_vault, + burn_vault_vrt_token_account, &vault_admin.pubkey(), &vault_base.pubkey(), deposit_fee_bps, withdrawal_fee_bps, reward_fee_bps, decimals, + initialize_token_amount, )], Some(&vault_admin.pubkey()), &[&vault_admin, &vrt_mint, &vault_base], diff --git a/integration_tests/tests/vault/add_delegation.rs b/integration_tests/tests/vault/add_delegation.rs index 83ada921..8b94dfd2 100644 --- a/integration_tests/tests/vault/add_delegation.rs +++ b/integration_tests/tests/vault/add_delegation.rs @@ -82,11 +82,11 @@ mod tests { vault_operator_delegation.delegation_state ); assert_eq!( - vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, AMOUNT_IN ); assert_eq!( - vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, AMOUNT_IN ); } @@ -134,7 +134,7 @@ mod tests { .do_add_delegation( &vault_root, &operator_roots[0].operator_pubkey, - 50_000 + Vault::INITIALIZATION_TOKEN_AMOUNT, + 50_000 + Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, ) .await .unwrap(); diff --git a/integration_tests/tests/vault/burn_withdrawal_ticket.rs b/integration_tests/tests/vault/burn_withdrawal_ticket.rs index 89dd4824..fddab05e 100644 --- a/integration_tests/tests/vault/burn_withdrawal_ticket.rs +++ b/integration_tests/tests/vault/burn_withdrawal_ticket.rs @@ -294,10 +294,13 @@ mod tests { .await .unwrap(); assert_eq!( - vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + 0 + ); + assert_eq!( + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, 0 ); - assert_eq!(vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, 0); assert_eq!(vault.delegation_state, DelegationState::default()); assert_eq!(vault.vrt_enqueued_for_cooldown_amount(), 0); assert_eq!(vault.vrt_ready_to_claim_amount(), 0); @@ -462,11 +465,11 @@ mod tests { .await .unwrap(); assert_eq!( - vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, expected_fee ); assert_eq!( - vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, expected_fee ); assert_eq!(vault.delegation_state, DelegationState::default()); @@ -617,11 +620,11 @@ mod tests { .await .unwrap(); assert_eq!( - vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, expected_fee ); assert_eq!( - vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, expected_fee ); assert_eq!(vault.delegation_state, DelegationState::default()); @@ -740,11 +743,11 @@ mod tests { .await .unwrap(); assert_eq!( - vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, MINT_AMOUNT - AMOUNT_TO_WITHDRAWAL ); assert_eq!( - vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, MINT_AMOUNT - AMOUNT_TO_WITHDRAWAL ); assert_eq!(vault.delegation_state, DelegationState::default()); @@ -958,11 +961,11 @@ mod tests { // Vault balance state assert_eq!( - vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, expected_remaining_supply ); assert_eq!( - vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, expected_remaining_supply ); diff --git a/integration_tests/tests/vault/crank_vault_update_state_tracker.rs b/integration_tests/tests/vault/crank_vault_update_state_tracker.rs index 989f377e..bffe9d85 100644 --- a/integration_tests/tests/vault/crank_vault_update_state_tracker.rs +++ b/integration_tests/tests/vault/crank_vault_update_state_tracker.rs @@ -773,7 +773,7 @@ mod tests { assert_eq!(last_full_update_epoch, 0); assert_eq!( vault.additional_assets_need_unstaking(), - 25_000 - Vault::INITIALIZATION_TOKEN_AMOUNT + 25_000 - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT ); let first_operator_delegation = vault_program_client @@ -876,7 +876,7 @@ mod tests { assert_eq!( vault.delegation_state.staked_amount(), - 25_000 + Vault::INITIALIZATION_TOKEN_AMOUNT + 25_000 + Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT ); assert_eq!(vault.delegation_state.enqueued_for_cooldown_amount(), 0); assert_eq!(vault.vrt_ready_to_claim_amount(), 75_000); @@ -1002,10 +1002,13 @@ mod tests { .await .unwrap(); assert_eq!( - vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + 0 + ); + assert_eq!( + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, 0 ); - assert_eq!(vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, 0); assert_eq!(vault.delegation_state, DelegationState::default()); assert_eq!(vault.vrt_enqueued_for_cooldown_amount(), 0); assert_eq!(vault.vrt_ready_to_claim_amount(), 0); @@ -1121,10 +1124,13 @@ mod tests { .await .unwrap(); assert_eq!( - vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + 0 + ); + assert_eq!( + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, 0 ); - assert_eq!(vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, 0); assert_eq!(vault.delegation_state, DelegationState::default()); assert_eq!(vault.vrt_enqueued_for_cooldown_amount(), 0); assert_eq!(vault.vrt_ready_to_claim_amount(), 0); @@ -1240,10 +1246,13 @@ mod tests { .await .unwrap(); assert_eq!( - vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + 0 + ); + assert_eq!( + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, 0 ); - assert_eq!(vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, 0); assert_eq!(vault.delegation_state, DelegationState::default()); assert_eq!(vault.vrt_enqueued_for_cooldown_amount(), 0); assert_eq!(vault.vrt_ready_to_claim_amount(), 0); diff --git a/integration_tests/tests/vault/initialize_vault.rs b/integration_tests/tests/vault/initialize_vault.rs index 410907a0..7efff52a 100644 --- a/integration_tests/tests/vault/initialize_vault.rs +++ b/integration_tests/tests/vault/initialize_vault.rs @@ -41,8 +41,14 @@ mod tests { assert_eq!(vault.vault_index(), 0); // Min initial deposit is 10_000 - assert_eq!(vault.vrt_supply(), Vault::INITIALIZATION_TOKEN_AMOUNT); - assert_eq!(vault.tokens_deposited(), Vault::INITIALIZATION_TOKEN_AMOUNT); + assert_eq!( + vault.vrt_supply(), + Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT + ); + assert_eq!( + vault.tokens_deposited(), + Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT + ); assert_eq!(vault.deposit_fee_bps(), 99); assert_eq!(vault.withdrawal_fee_bps(), 100); assert_eq!(vault.ncn_count(), 0); @@ -53,6 +59,10 @@ mod tests { let token_mint = fixture.get_token_mint(&vault.vrt_mint).await.unwrap(); assert_eq!(token_mint.decimals, 9); + assert_eq!( + token_mint.supply, + Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT + ); } #[tokio::test] diff --git a/integration_tests/tests/vault/initialize_vault_update_state_tracker.rs b/integration_tests/tests/vault/initialize_vault_update_state_tracker.rs index 94c5433c..ddff6b65 100644 --- a/integration_tests/tests/vault/initialize_vault_update_state_tracker.rs +++ b/integration_tests/tests/vault/initialize_vault_update_state_tracker.rs @@ -457,7 +457,7 @@ mod tests { assert_eq!( vault.additional_assets_need_unstaking(), - 75_000 - Vault::INITIALIZATION_TOKEN_AMOUNT + 75_000 - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT ); // Update fees @@ -497,7 +497,7 @@ mod tests { // no assets cooled down, additional_assets_need_unstaking = 75_000 assert_eq!( vault.additional_assets_need_unstaking(), - 75_000 - Vault::INITIALIZATION_TOKEN_AMOUNT + 75_000 - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT ); let vault_update_state_tracker_pubkey = VaultUpdateStateTracker::find_program_address( @@ -555,7 +555,7 @@ mod tests { assert_eq!( vault.delegation_state.staked_amount(), - 25_000 + Vault::INITIALIZATION_TOKEN_AMOUNT + 25_000 + Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT ); assert_eq!(vault.delegation_state.enqueued_for_cooldown_amount(), 0); assert_eq!(vault.vrt_ready_to_claim_amount(), 75_000); diff --git a/integration_tests/tests/vault/reward_fee.rs b/integration_tests/tests/vault/reward_fee.rs index cf85a54a..2c760015 100644 --- a/integration_tests/tests/vault/reward_fee.rs +++ b/integration_tests/tests/vault/reward_fee.rs @@ -80,12 +80,12 @@ mod tests { assert_eq!( MINT_AMOUNT * 2, - vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, ); assert_eq!(EXPECTED_FEE, reward_fee_account.amount); assert_eq!( MINT_AMOUNT + EXPECTED_FEE, - vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, ); } @@ -162,12 +162,12 @@ mod tests { assert_eq!( MINT_AMOUNT * 2, - vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, ); assert_eq!(MINT_AMOUNT, reward_fee_account.amount); assert_eq!( MINT_AMOUNT * 2, - vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, ); } @@ -238,10 +238,13 @@ mod tests { assert_eq!( MINT_AMOUNT, - vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT ); assert_eq!(0, reward_fee_account.amount); - assert_eq!(0, vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT); + assert_eq!( + 0, + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT + ); } #[tokio::test] @@ -332,12 +335,12 @@ mod tests { assert_eq!( MINT_AMOUNT * 2, - vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, ); assert_eq!(EXPECTED_FEE, reward_fee_account.amount); assert_eq!( MINT_AMOUNT + EXPECTED_FEE, - vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, ); } } diff --git a/integration_tests/tests/vault/update_vault_balance.rs b/integration_tests/tests/vault/update_vault_balance.rs index 9c6d6763..125c4bd3 100644 --- a/integration_tests/tests/vault/update_vault_balance.rs +++ b/integration_tests/tests/vault/update_vault_balance.rs @@ -149,12 +149,12 @@ mod tests { .unwrap(); assert_eq!( - vault.tokens_deposited() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, MINT_AMOUNT * 2 ); assert_eq!(reward_fee_account.amount, EXPECTED_FEE); assert_eq!( - vault.vrt_supply() - Vault::INITIALIZATION_TOKEN_AMOUNT, + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, MINT_AMOUNT + EXPECTED_FEE ); } diff --git a/vault_core/src/burn_vault.rs b/vault_core/src/burn_vault.rs new file mode 100644 index 00000000..ef8ae9e1 --- /dev/null +++ b/vault_core/src/burn_vault.rs @@ -0,0 +1,62 @@ +use solana_program::{account_info::AccountInfo, msg, program_error::ProgramError, pubkey::Pubkey}; + +/// Uninitiatilized, no-data account used to hold SOL for ClaimStatus rent +/// Must be empty and uninitialized to be used as a payer or `transfer` instructions fail +pub struct BurnVault {} + +impl BurnVault { + // ------------------------------------------ + // Serialization & Deserialization + // ------------------------------------------ + + /// Returns the seeds for the PDA + /// + /// # Returns + /// * `Vec>` - containing the seed vectors + pub fn seeds(base: &Pubkey) -> Vec> { + vec![b"burn_vault".as_ref().to_vec(), base.to_bytes().to_vec()] + } + + /// Find the program address for the Vault + /// + /// # Arguments + /// * `program_id` - The program ID + /// * `base` - The base account used as a PDA seed + /// + /// # Returns + /// * [`Pubkey`] - The program address + /// * `u8` - The bump seed + /// * `Vec>` - The seeds used to generate the PDA + pub fn find_program_address(program_id: &Pubkey, base: &Pubkey) -> (Pubkey, u8, Vec>) { + let seeds = Self::seeds(base); + let seeds_iter: Vec<_> = seeds.iter().map(|s| s.as_slice()).collect(); + let (pda, bump) = Pubkey::find_program_address(&seeds_iter, program_id); + (pda, bump, seeds) + } + + pub fn load( + program_id: &Pubkey, + base: &Pubkey, + account: &AccountInfo, + expect_writable: bool, + ) -> Result<(), ProgramError> { + if account.owner.ne(&solana_program::system_program::ID) { + msg!("Burn Vault account has an invalid owner"); + return Err(ProgramError::InvalidAccountOwner); + } + + if expect_writable && !account.is_writable { + msg!("Burn Vault account is not writable"); + return Err(ProgramError::InvalidAccountData); + } + + if account + .key + .ne(&Self::find_program_address(program_id, base).0) + { + msg!("Burn Vault account is not at the correct PDA"); + return Err(ProgramError::InvalidAccountData); + } + Ok(()) + } +} diff --git a/vault_core/src/lib.rs b/vault_core/src/lib.rs index 1ce87bdb..f8e2b02f 100644 --- a/vault_core/src/lib.rs +++ b/vault_core/src/lib.rs @@ -1,3 +1,4 @@ +pub mod burn_vault; pub mod config; pub mod delegation_state; pub mod discriminators; diff --git a/vault_core/src/vault.rs b/vault_core/src/vault.rs index ee39a8f1..ca113eb5 100644 --- a/vault_core/src/vault.rs +++ b/vault_core/src/vault.rs @@ -154,7 +154,7 @@ pub struct Vault { impl Vault { pub const MAX_REWARD_DELTA_BPS: u16 = 50; // 0.5% pub const MIN_WITHDRAWAL_SLIPPAGE_BPS: u16 = 50; // 0.5% - pub const INITIALIZATION_TOKEN_AMOUNT: u64 = 10_000; + pub const DEFAULT_INITIALIZATION_TOKEN_AMOUNT: u64 = 10_000; #[allow(clippy::too_many_arguments)] pub fn new( @@ -443,6 +443,27 @@ impl Vault { self.is_paused = PodBool::from_bool(is_paused); } + // Only to be used in initialize_vault + pub fn initialize_vault_override_deposit_fee_bps( + &mut self, + deposit_fee_bps: u16, + base: &AccountInfo, + ) -> Result<(), ProgramError> { + if !base.is_signer { + msg!("Base account must be a signer"); + return Err(ProgramError::MissingRequiredSignature); + } + + if deposit_fee_bps > MAX_FEE_BPS { + msg!("Deposit fee exceeds maximum allowed of {}", MAX_FEE_BPS); + return Err(ProgramError::InvalidArgument); + } + + self.deposit_fee_bps = PodU16::from(deposit_fee_bps); + + Ok(()) + } + /// Checks whether the vault is currently paused. /// /// # Returns diff --git a/vault_program/src/initialize_vault.rs b/vault_program/src/initialize_vault.rs index ac598339..0a89338e 100644 --- a/vault_program/src/initialize_vault.rs +++ b/vault_program/src/initialize_vault.rs @@ -8,14 +8,26 @@ use jito_jsm_core::{ load_token_program, }, }; -use jito_vault_core::{config::Config, vault::Vault, MAX_FEE_BPS}; +use jito_vault_core::{burn_vault::BurnVault, config::Config, vault::Vault, MAX_FEE_BPS}; use jito_vault_sdk::error::VaultError; use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, program::invoke, - program_error::ProgramError, program_pack::Pack, pubkey::Pubkey, rent::Rent, - system_instruction, sysvar::Sysvar, + account_info::AccountInfo, + clock::Clock, + entrypoint::ProgramResult, + msg, + program::{invoke, invoke_signed}, + program_error::ProgramError, + program_pack::Pack, + pubkey::Pubkey, + rent::Rent, + system_instruction, + sysvar::Sysvar, +}; +use spl_associated_token_account::instruction::create_associated_token_account; +use spl_token::{ + instruction::{mint_to, transfer}, + state::Mint, }; -use spl_token::{instruction::transfer, state::Mint}; /// Processes the create instruction: [`crate::VaultInstruction::InitializeVault`] pub fn process_initialize_vault( @@ -25,8 +37,9 @@ pub fn process_initialize_vault( withdrawal_fee_bps: u16, reward_fee_bps: u16, decimals: u8, + initialize_token_amount: u64, ) -> ProgramResult { - let [config, vault, vrt_mint, st_mint, admin_st_token_account, vault_st_token_account, admin, base, system_program, token_program] = + let [config, vault, vrt_mint, st_mint, admin_st_token_account, vault_st_token_account, burn_vault, burn_vault_vrt_token_account, admin, base, system_program, token_program, associated_token_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); @@ -51,12 +64,14 @@ pub fn process_initialize_vault( st_mint.key, token_program, )?; - load_token_account( - vault_st_token_account, - vault.key, - st_mint.key, - token_program, - )?; + + load_system_account(burn_vault, true)?; + load_system_account(burn_vault_vrt_token_account, true)?; + + if initialize_token_amount == 0 { + msg!("Initialize token amount must be greater than zero"); + return Err(ProgramError::InvalidArgument); + } // The vault account shall be at the canonical PDA let (vault_pubkey, vault_bump, mut vault_seeds) = @@ -67,6 +82,10 @@ pub fn process_initialize_vault( return Err(ProgramError::InvalidAccountData); } + let (_, burn_vault_bump, mut burn_vault_seeds) = + BurnVault::find_program_address(program_id, base.key); + burn_vault_seeds.push(vec![burn_vault_bump]); + if deposit_fee_bps > config.deposit_withdrawal_fee_cap_bps() || withdrawal_fee_bps > config.deposit_withdrawal_fee_cap_bps() || reward_fee_bps > MAX_FEE_BPS @@ -106,25 +125,6 @@ pub fn process_initialize_vault( )?; } - // Deposit min ST - { - invoke( - &transfer( - token_program.key, - admin_st_token_account.key, - vault_st_token_account.key, - admin.key, - &[], - Vault::INITIALIZATION_TOKEN_AMOUNT, - )?, - &[ - vault_st_token_account.clone(), - admin_st_token_account.clone(), - admin.clone(), - ], - )?; - } - let slot = Clock::get()?.slot; // Initialize vault @@ -144,9 +144,9 @@ pub fn process_initialize_vault( let mut vault_data = vault.try_borrow_mut_data()?; vault_data[0] = Vault::DISCRIMINATOR; - let vault = Vault::try_from_slice_unchecked_mut(&mut vault_data)?; + let vault_account = Vault::try_from_slice_unchecked_mut(&mut vault_data)?; - *vault = Vault::new( + *vault_account = Vault::new( *vrt_mint.key, *st_mint.key, *admin.key, @@ -160,9 +160,86 @@ pub fn process_initialize_vault( slot, )?; - // Mint initial VRT supply - vault.set_vrt_supply(Vault::INITIALIZATION_TOKEN_AMOUNT); - vault.set_tokens_deposited(Vault::INITIALIZATION_TOKEN_AMOUNT); + { + // "Mint" the initial VRT supply + vault_account.initialize_vault_override_deposit_fee_bps(0, base)?; + + let mint_summary = + vault_account.mint_with_fee(initialize_token_amount, initialize_token_amount)?; + if mint_summary.vrt_to_depositor != initialize_token_amount + || mint_summary.vrt_to_fee_wallet != 0 + { + msg!("Minted VRT to depositor does not match expected amount"); + return Err(VaultError::VaultInitialAmountFailed.into()); + } + + vault_account.initialize_vault_override_deposit_fee_bps(deposit_fee_bps, base)?; + } + + // Deposit min ST + { + invoke( + &transfer( + token_program.key, + admin_st_token_account.key, + vault_st_token_account.key, + admin.key, + &[], + initialize_token_amount, + )?, + &[ + vault_st_token_account.clone(), + admin_st_token_account.clone(), + admin.clone(), + ], + )?; + } + + // Create ATA + { + invoke( + &create_associated_token_account( + admin.key, // funding account + burn_vault.key, // wallet address (ATA owner) + vrt_mint.key, // mint address + &spl_token::id(), // token program + ), + &[ + admin.clone(), + burn_vault_vrt_token_account.clone(), // The ATA address itself + burn_vault.clone(), + vrt_mint.clone(), + system_program.clone(), // Don't forget system program + token_program.clone(), + associated_token_program.clone(), + ], + )?; + } + + // Mint VRT to burn vault + { + let signing_seeds = vault_account.signing_seeds(); + let seed_slices: Vec<&[u8]> = + signing_seeds.iter().map(|seed| seed.as_slice()).collect(); + drop(vault_data); + + invoke_signed( + &mint_to( + token_program.key, + vrt_mint.key, + burn_vault_vrt_token_account.key, + vault.key, + &[], + initialize_token_amount, + )?, + &[ + vrt_mint.clone(), + burn_vault_vrt_token_account.clone(), + vault.clone(), + ], + &[&seed_slices], + )?; + } } config.increment_num_vaults()?; diff --git a/vault_program/src/lib.rs b/vault_program/src/lib.rs index fb0dbb8f..4c48f7d9 100644 --- a/vault_program/src/lib.rs +++ b/vault_program/src/lib.rs @@ -111,6 +111,7 @@ pub fn process_instruction( withdrawal_fee_bps, reward_fee_bps, decimals, + initialize_token_amount, } => { msg!("Instruction: InitializeVault"); process_initialize_vault( @@ -120,6 +121,7 @@ pub fn process_instruction( withdrawal_fee_bps, reward_fee_bps, decimals, + initialize_token_amount, ) } VaultInstruction::InitializeVaultWithMint => { diff --git a/vault_sdk/Cargo.toml b/vault_sdk/Cargo.toml index 4f46b570..29452fad 100644 --- a/vault_sdk/Cargo.toml +++ b/vault_sdk/Cargo.toml @@ -13,6 +13,7 @@ readme = { workspace = true } borsh = { workspace = true } shank = { workspace = true } solana-program = { workspace = true } +spl-associated-token-account = { workspace = true } spl-token = { workspace = true } thiserror = { workspace = true } diff --git a/vault_sdk/src/error.rs b/vault_sdk/src/error.rs index 8214e184..19f6dfa5 100644 --- a/vault_sdk/src/error.rs +++ b/vault_sdk/src/error.rs @@ -5,6 +5,8 @@ use thiserror::Error; pub enum VaultError { #[error("VaultSlashUnderflow")] VaultSlashUnderflow = 1000, + #[error("VaultInitialAmountFailed")] + VaultInitialAmountFailed, #[error("VaultInsufficientFunds")] VaultInsufficientFunds, #[error("VaultOverflow")] diff --git a/vault_sdk/src/instruction.rs b/vault_sdk/src/instruction.rs index 6f3f330a..1a3ef02d 100644 --- a/vault_sdk/src/instruction.rs +++ b/vault_sdk/src/instruction.rs @@ -22,15 +22,19 @@ pub enum VaultInstruction { #[account(3, name = "st_mint")] #[account(4, writable, name = "admin_st_token_account")] #[account(5, writable, name = "vault_st_token_account")] - #[account(6, writable, signer, name = "admin")] - #[account(7, signer, name = "base")] - #[account(8, name = "system_program")] - #[account(9, name = "token_program")] + #[account(6, writable, name = "burn_vault")] + #[account(7, writable, name = "burn_vault_vrt_token_account")] + #[account(8, writable, signer, name = "admin")] + #[account(9, signer, name = "base")] + #[account(10, name = "system_program")] + #[account(11, name = "token_program")] + #[account(12, name = "associated_token_program")] InitializeVault { deposit_fee_bps: u16, withdrawal_fee_bps: u16, reward_fee_bps: u16, decimals: u8, + initialize_token_amount: u64, }, /// Initializes a vault with an already-created VRT mint diff --git a/vault_sdk/src/sdk.rs b/vault_sdk/src/sdk.rs index 2657ef71..28bd8227 100644 --- a/vault_sdk/src/sdk.rs +++ b/vault_sdk/src/sdk.rs @@ -43,12 +43,15 @@ pub fn initialize_vault( st_mint: &Pubkey, admin_st_token_account: &Pubkey, vault_st_token_account: &Pubkey, + burn_vault: &Pubkey, + burn_vault_vrt_token_account: &Pubkey, admin: &Pubkey, base: &Pubkey, deposit_fee_bps: u16, withdrawal_fee_bps: u16, reward_fee_bps: u16, decimals: u8, + initialize_token_amount: u64, ) -> Instruction { let accounts = vec![ AccountMeta::new(*config, false), @@ -57,10 +60,13 @@ pub fn initialize_vault( AccountMeta::new_readonly(*st_mint, false), AccountMeta::new(*admin_st_token_account, false), AccountMeta::new(*vault_st_token_account, false), + AccountMeta::new(*burn_vault, false), + AccountMeta::new(*burn_vault_vrt_token_account, false), AccountMeta::new(*admin, true), AccountMeta::new_readonly(*base, true), AccountMeta::new_readonly(system_program::id(), false), AccountMeta::new_readonly(spl_token::id(), false), + AccountMeta::new_readonly(spl_associated_token_account::id(), false), ]; Instruction { program_id: *program_id, @@ -70,6 +76,7 @@ pub fn initialize_vault( withdrawal_fee_bps, reward_fee_bps, decimals, + initialize_token_amount, } .try_to_vec() .unwrap(), From 09dfe436cf2102b4547033d42118c227e88c5cc1 Mon Sep 17 00:00:00 2001 From: Christian Krueger Date: Fri, 20 Dec 2024 14:03:53 -0700 Subject: [PATCH 09/11] added tests --- core/src/loader.rs | 16 ++ idl/jito_vault.json | 2 +- .../tests/vault/initialize_vault.rs | 76 +++++++++- rust-toolchain.toml | 1 - vault_core/src/burn_vault.rs | 138 ++++++++++++++++++ vault_core/src/vault.rs | 76 ++++++++++ vault_program/src/initialize_vault.rs | 28 ++-- vault_sdk/src/instruction.rs | 2 +- vault_sdk/src/sdk.rs | 2 +- 9 files changed, 323 insertions(+), 18 deletions(-) diff --git a/core/src/loader.rs b/core/src/loader.rs index f57a0fcd..39144eac 100644 --- a/core/src/loader.rs +++ b/core/src/loader.rs @@ -43,6 +43,22 @@ pub fn load_system_program(info: &AccountInfo) -> Result<(), ProgramError> { Ok(()) } +/// Loads the account as the `spl_token` program, returning an error if it is not. +/// +/// # Arguments +/// * `info` - The account to load the token program from +/// +/// # Returns +/// * `Result<(), ProgramError>` - The result of the operation +pub fn load_associated_token_account_program(info: &AccountInfo) -> Result<(), ProgramError> { + if info.key.ne(&spl_associated_token_account::id()) { + msg!("Account is not the spl associated token program"); + return Err(ProgramError::IncorrectProgramId); + } + + Ok(()) +} + /// Loads the account as the `spl_token` program, returning an error if it is not. /// /// # Arguments diff --git a/idl/jito_vault.json b/idl/jito_vault.json index c554ecb5..8411ae85 100644 --- a/idl/jito_vault.json +++ b/idl/jito_vault.json @@ -77,7 +77,7 @@ }, { "name": "burnVault", - "isMut": true, + "isMut": false, "isSigner": false }, { diff --git a/integration_tests/tests/vault/initialize_vault.rs b/integration_tests/tests/vault/initialize_vault.rs index 7efff52a..a49a11b1 100644 --- a/integration_tests/tests/vault/initialize_vault.rs +++ b/integration_tests/tests/vault/initialize_vault.rs @@ -1,9 +1,10 @@ #[cfg(test)] mod tests { - use jito_vault_core::{config::Config, vault::Vault}; + use jito_vault_core::{burn_vault::BurnVault, config::Config, vault::Vault}; use jito_vault_sdk::error::VaultError; use solana_program::pubkey::Pubkey; - use solana_sdk::signature::Signer; + use solana_sdk::signature::{Keypair, Signer}; + use spl_associated_token_account::get_associated_token_address; use crate::fixtures::{ fixture::TestBuilder, @@ -65,6 +66,77 @@ mod tests { ); } + #[tokio::test] + async fn test_initialize_vault_no_initial_token() { + let mut fixture = TestBuilder::new().await; + + let expect_slot = 100; + fixture.warp_to_slot(expect_slot).await.unwrap(); + + let mut vault_program_client = fixture.vault_program_client(); + vault_program_client.do_initialize_config().await.unwrap(); + + let vault_base = Keypair::new(); + + let vault_pubkey = + Vault::find_program_address(&jito_vault_program::id(), &vault_base.pubkey()).0; + + let vrt_mint = Keypair::new(); + let vault_admin = Keypair::new(); + let st_mint = Keypair::new(); + + vault_program_client + .airdrop(&vault_admin.pubkey(), 100.0) + .await + .unwrap(); + vault_program_client + .create_token_mint(&st_mint, &spl_token::id()) + .await + .unwrap(); + + let admin_st_token_account = + get_associated_token_address(&vault_admin.pubkey(), &st_mint.pubkey()); + let vault_st_token_account = get_associated_token_address(&vault_pubkey, &st_mint.pubkey()); + + let burn_vault = + BurnVault::find_program_address(&jito_vault_program::id(), &vault_base.pubkey()).0; + + let burn_vault_vrt_token_account = + get_associated_token_address(&burn_vault, &vrt_mint.pubkey()); + + // Needs to be created before initialize vault + vault_program_client + .create_ata(&st_mint.pubkey(), &vault_pubkey) + .await + .unwrap(); + vault_program_client + .create_ata(&st_mint.pubkey(), &vault_admin.pubkey()) + .await + .unwrap(); + + let result = vault_program_client + .initialize_vault( + &Config::find_program_address(&jito_vault_program::id()).0, + &vault_pubkey, + &vrt_mint, + &st_mint, + &admin_st_token_account, + &vault_st_token_account, + &burn_vault, + &burn_vault_vrt_token_account, + &vault_admin, + &vault_base, + 0, + 0, + 0, + 9, + 0, + ) + .await; + + assert_vault_error(result, VaultError::VaultInitialAmountFailed); + } + #[tokio::test] async fn test_initialize_vault_deposit_fee_bps_too_high() { let fixture = TestBuilder::new().await; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 3877eebc..54e31d7d 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,3 @@ # note: this file doesn't play nicely with solana-verify build [toolchain] -channel = "1.80.0" components = ["rustfmt", "rustc-dev", "clippy", "cargo"] diff --git a/vault_core/src/burn_vault.rs b/vault_core/src/burn_vault.rs index ef8ae9e1..2048f952 100644 --- a/vault_core/src/burn_vault.rs +++ b/vault_core/src/burn_vault.rs @@ -60,3 +60,141 @@ impl BurnVault { Ok(()) } } + +#[cfg(test)] +mod tests { + use solana_program::{account_info::AccountInfo, rent::Rent, system_program}; + + use super::*; + + #[test] + fn test_seeds_generation() { + let base = Pubkey::new_unique(); + let seeds = BurnVault::seeds(&base); + + assert_eq!(seeds.len(), 2); + assert_eq!(seeds[0], b"burn_vault".as_ref()); + assert_eq!(seeds[1], base.to_bytes().to_vec()); + } + + #[test] + fn test_find_program_address() { + let program_id = Pubkey::new_unique(); + let base = Pubkey::new_unique(); + + let (pda, bump, seeds) = BurnVault::find_program_address(&program_id, &base); + + // Verify seeds are correct + assert_eq!(seeds, BurnVault::seeds(&base)); + + // Verify PDA derivation + let seeds_with_bump = seeds.iter().map(|s| s.as_slice()).collect::>(); + let (expected_pda, expected_bump) = + Pubkey::find_program_address(&seeds_with_bump, &program_id); + + assert_eq!(pda, expected_pda); + assert_eq!(bump, expected_bump); + } + + #[test] + fn test_load_valid_account() { + let program_id = Pubkey::new_unique(); + let base = Pubkey::new_unique(); + let (pda, _bump, _) = BurnVault::find_program_address(&program_id, &base); + + // Create a valid account at the PDA + let mut lamports = 1000000; + let mut data = vec![]; + let account = AccountInfo::new( + &pda, + false, + true, + &mut lamports, + &mut data, + &system_program::ID, + false, + Rent::default().minimum_balance(0), + ); + + // Test load with writable=false + let result = BurnVault::load(&program_id, &base, &account, false); + assert!(result.is_ok()); + + // Test load with writable=true + let result = BurnVault::load(&program_id, &base, &account, true); + assert!(result.is_ok()); + } + + #[test] + fn test_load_invalid_owner() { + let program_id = Pubkey::new_unique(); + let base = Pubkey::new_unique(); + let (pda, _bump, _) = BurnVault::find_program_address(&program_id, &base); + + // Create account with invalid owner + let mut lamports = 1000000; + let mut data = vec![]; + let invalid_owner = Pubkey::new_unique(); + let account = AccountInfo::new( + &pda, + false, + true, + &mut lamports, + &mut data, + &invalid_owner, + false, + Rent::default().minimum_balance(0), + ); + + let result = BurnVault::load(&program_id, &base, &account, false); + assert_eq!(result.unwrap_err(), ProgramError::InvalidAccountOwner); + } + + #[test] + fn test_load_invalid_pda() { + let program_id = Pubkey::new_unique(); + let base = Pubkey::new_unique(); + let invalid_pda = Pubkey::new_unique(); + + // Create account at wrong address + let mut lamports = 1000000; + let mut data = vec![]; + let account = AccountInfo::new( + &invalid_pda, + false, + true, + &mut lamports, + &mut data, + &system_program::ID, + false, + Rent::default().minimum_balance(0), + ); + + let result = BurnVault::load(&program_id, &base, &account, false); + assert_eq!(result.unwrap_err(), ProgramError::InvalidAccountData); + } + + #[test] + fn test_load_not_writable() { + let program_id = Pubkey::new_unique(); + let base = Pubkey::new_unique(); + let (pda, _bump, _) = BurnVault::find_program_address(&program_id, &base); + + // Create non-writable account + let mut lamports = 1000000; + let mut data = vec![]; + let account = AccountInfo::new( + &pda, + false, + false, // not writable + &mut lamports, + &mut data, + &system_program::ID, + false, + Rent::default().minimum_balance(0), + ); + + let result = BurnVault::load(&program_id, &base, &account, true); + assert_eq!(result.unwrap_err(), ProgramError::InvalidAccountData); + } +} diff --git a/vault_core/src/vault.rs b/vault_core/src/vault.rs index ca113eb5..307e0102 100644 --- a/vault_core/src/vault.rs +++ b/vault_core/src/vault.rs @@ -2529,4 +2529,80 @@ mod tests { let result = check_fee(10000, 10000, 1000, 1000, MAX_FEE_BPS + 1); assert_eq!(result, Err(VaultError::VaultFeeCapExceeded)); } + + #[test] + fn test_initialize_vault_override_deposit_fee_bps() { + use solana_program::{account_info::AccountInfo, program_error::ProgramError}; + + // Create a basic vault + let mut vault = Vault::new( + Pubkey::new_unique(), + Pubkey::new_unique(), + Pubkey::new_unique(), + 0, + Pubkey::new_unique(), + 0, + 0, + 0, + 0, + 0, + 0, + ) + .unwrap(); + + // Create account info for tests + let key = Pubkey::new_unique(); + let owner = Pubkey::new_unique(); + let mut lamports = 0; + let mut data = vec![0; 32]; + + // Test 1: Non-signer account should fail + let non_signer_account = AccountInfo::new( + &key, + false, // is_signer = false + true, + &mut lamports, + &mut data, + &owner, + false, + 0, + ); + + assert_eq!( + vault.initialize_vault_override_deposit_fee_bps(100, &non_signer_account), + Err(ProgramError::MissingRequiredSignature) + ); + + // Test 2: Fee exceeding MAX_FEE_BPS should fail + let signer_account = AccountInfo::new( + &key, + true, // is_signer = true + true, + &mut lamports, + &mut data, + &owner, + false, + 0, + ); + + assert_eq!( + vault.initialize_vault_override_deposit_fee_bps(MAX_FEE_BPS + 1, &signer_account), + Err(ProgramError::InvalidArgument) + ); + + // Test 3: Valid parameters should succeed + let valid_fee = 100; + assert_eq!( + vault.initialize_vault_override_deposit_fee_bps(valid_fee, &signer_account), + Ok(()) + ); + assert_eq!(vault.deposit_fee_bps(), valid_fee); + + // Test 4: Maximum allowed fee should succeed + assert_eq!( + vault.initialize_vault_override_deposit_fee_bps(MAX_FEE_BPS, &signer_account), + Ok(()) + ); + assert_eq!(vault.deposit_fee_bps(), MAX_FEE_BPS); + } } diff --git a/vault_program/src/initialize_vault.rs b/vault_program/src/initialize_vault.rs index 0a89338e..ad711c57 100644 --- a/vault_program/src/initialize_vault.rs +++ b/vault_program/src/initialize_vault.rs @@ -4,8 +4,8 @@ use jito_bytemuck::{AccountDeserialize, Discriminator}; use jito_jsm_core::{ create_account, loader::{ - load_signer, load_system_account, load_system_program, load_token_account, load_token_mint, - load_token_program, + load_associated_token_account_program, load_signer, load_system_account, + load_system_program, load_token_account, load_token_mint, load_token_program, }, }; use jito_vault_core::{burn_vault::BurnVault, config::Config, vault::Vault, MAX_FEE_BPS}; @@ -52,25 +52,29 @@ pub fn process_initialize_vault( load_system_account(vrt_mint, true)?; load_signer(vrt_mint, true)?; load_token_mint(st_mint)?; - load_signer(admin, true)?; - load_signer(base, false)?; - load_system_program(system_program)?; - - // Only the original spl token program is allowed - load_token_program(token_program)?; load_token_account( admin_st_token_account, admin.key, st_mint.key, token_program, )?; - - load_system_account(burn_vault, true)?; + load_token_account( + vault_st_token_account, + vault.key, + st_mint.key, + token_program, + )?; + BurnVault::load(program_id, base.key, burn_vault, false)?; load_system_account(burn_vault_vrt_token_account, true)?; + load_signer(admin, true)?; + load_signer(base, false)?; + load_system_program(system_program)?; + load_token_program(token_program)?; + load_associated_token_account_program(associated_token_program)?; if initialize_token_amount == 0 { msg!("Initialize token amount must be greater than zero"); - return Err(ProgramError::InvalidArgument); + return Err(VaultError::VaultInitialAmountFailed.into()); } // The vault account shall be at the canonical PDA @@ -188,8 +192,8 @@ pub fn process_initialize_vault( initialize_token_amount, )?, &[ - vault_st_token_account.clone(), admin_st_token_account.clone(), + vault_st_token_account.clone(), admin.clone(), ], )?; diff --git a/vault_sdk/src/instruction.rs b/vault_sdk/src/instruction.rs index 1a3ef02d..e29892d6 100644 --- a/vault_sdk/src/instruction.rs +++ b/vault_sdk/src/instruction.rs @@ -22,7 +22,7 @@ pub enum VaultInstruction { #[account(3, name = "st_mint")] #[account(4, writable, name = "admin_st_token_account")] #[account(5, writable, name = "vault_st_token_account")] - #[account(6, writable, name = "burn_vault")] + #[account(6, name = "burn_vault")] #[account(7, writable, name = "burn_vault_vrt_token_account")] #[account(8, writable, signer, name = "admin")] #[account(9, signer, name = "base")] diff --git a/vault_sdk/src/sdk.rs b/vault_sdk/src/sdk.rs index 28bd8227..c4faaea6 100644 --- a/vault_sdk/src/sdk.rs +++ b/vault_sdk/src/sdk.rs @@ -60,7 +60,7 @@ pub fn initialize_vault( AccountMeta::new_readonly(*st_mint, false), AccountMeta::new(*admin_st_token_account, false), AccountMeta::new(*vault_st_token_account, false), - AccountMeta::new(*burn_vault, false), + AccountMeta::new_readonly(*burn_vault, false), AccountMeta::new(*burn_vault_vrt_token_account, false), AccountMeta::new(*admin, true), AccountMeta::new_readonly(*base, true), From 691782134ee216f058d68c3d30d1cf9372b5639f Mon Sep 17 00:00:00 2001 From: Christian Krueger Date: Mon, 23 Dec 2024 09:49:29 -0700 Subject: [PATCH 10/11] comments addressed --- rust-toolchain.toml | 1 + vault_core/src/burn_vault.rs | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 54e31d7d..3877eebc 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,4 @@ # note: this file doesn't play nicely with solana-verify build [toolchain] +channel = "1.80.0" components = ["rustfmt", "rustc-dev", "clippy", "cargo"] diff --git a/vault_core/src/burn_vault.rs b/vault_core/src/burn_vault.rs index 2048f952..9e6b97ba 100644 --- a/vault_core/src/burn_vault.rs +++ b/vault_core/src/burn_vault.rs @@ -1,7 +1,6 @@ use solana_program::{account_info::AccountInfo, msg, program_error::ProgramError, pubkey::Pubkey}; -/// Uninitiatilized, no-data account used to hold SOL for ClaimStatus rent -/// Must be empty and uninitialized to be used as a payer or `transfer` instructions fail +// Empty PDA to send tokens to "burn" pub struct BurnVault {} impl BurnVault { From 1d42507022b2f49c25c3a3fdf6bb42522b11de4a Mon Sep 17 00:00:00 2001 From: Christian Krueger Date: Mon, 23 Dec 2024 10:04:32 -0700 Subject: [PATCH 11/11] updated clients --- clients/js/vault_client/errors/jitoVault.ts | 120 ++++----- .../instructions/initializeVault.ts | 73 +++++- .../src/generated/errors/jito_vault.rs | 235 +++++++++--------- .../instructions/initialize_vault.rs | 172 ++++++++++++- 4 files changed, 407 insertions(+), 193 deletions(-) diff --git a/clients/js/vault_client/errors/jitoVault.ts b/clients/js/vault_client/errors/jitoVault.ts index f930eb68..5ecee29f 100644 --- a/clients/js/vault_client/errors/jitoVault.ts +++ b/clients/js/vault_client/errors/jitoVault.ts @@ -16,122 +16,124 @@ import { JITO_VAULT_PROGRAM_ADDRESS } from '../programs'; /** VaultSlashUnderflow: VaultSlashUnderflow */ export const JITO_VAULT_ERROR__VAULT_SLASH_UNDERFLOW = 0x3e8; // 1000 +/** VaultInitialAmountFailed: VaultInitialAmountFailed */ +export const JITO_VAULT_ERROR__VAULT_INITIAL_AMOUNT_FAILED = 0x3e9; // 1001 /** VaultInsufficientFunds: VaultInsufficientFunds */ -export const JITO_VAULT_ERROR__VAULT_INSUFFICIENT_FUNDS = 0x3e9; // 1001 +export const JITO_VAULT_ERROR__VAULT_INSUFFICIENT_FUNDS = 0x3ea; // 1002 /** VaultOverflow: VaultOverflow */ -export const JITO_VAULT_ERROR__VAULT_OVERFLOW = 0x3ea; // 1002 +export const JITO_VAULT_ERROR__VAULT_OVERFLOW = 0x3eb; // 1003 /** VaultOperatorAdminInvalid: VaultOperatorAdminInvalid */ -export const JITO_VAULT_ERROR__VAULT_OPERATOR_ADMIN_INVALID = 0x3eb; // 1003 +export const JITO_VAULT_ERROR__VAULT_OPERATOR_ADMIN_INVALID = 0x3ec; // 1004 /** VaultAdminInvalid: VaultAdminInvalid */ -export const JITO_VAULT_ERROR__VAULT_ADMIN_INVALID = 0x3ec; // 1004 +export const JITO_VAULT_ERROR__VAULT_ADMIN_INVALID = 0x3ed; // 1005 /** VaultCapacityAdminInvalid: VaultCapacityAdminInvalid */ -export const JITO_VAULT_ERROR__VAULT_CAPACITY_ADMIN_INVALID = 0x3ed; // 1005 +export const JITO_VAULT_ERROR__VAULT_CAPACITY_ADMIN_INVALID = 0x3ee; // 1006 /** VaultMintBurnAdminInvalid: VaultMintBurnAdminInvalid */ -export const JITO_VAULT_ERROR__VAULT_MINT_BURN_ADMIN_INVALID = 0x3ee; // 1006 +export const JITO_VAULT_ERROR__VAULT_MINT_BURN_ADMIN_INVALID = 0x3ef; // 1007 /** VaultDelegationAdminInvalid: VaultDelegationAdminInvalid */ -export const JITO_VAULT_ERROR__VAULT_DELEGATION_ADMIN_INVALID = 0x3ef; // 1007 +export const JITO_VAULT_ERROR__VAULT_DELEGATION_ADMIN_INVALID = 0x3f0; // 1008 /** VaultDelegateAssetAdminInvalid: VaultDelegateAssetAdminInvalid */ -export const JITO_VAULT_ERROR__VAULT_DELEGATE_ASSET_ADMIN_INVALID = 0x3f0; // 1008 +export const JITO_VAULT_ERROR__VAULT_DELEGATE_ASSET_ADMIN_INVALID = 0x3f1; // 1009 /** VaultCapacityExceeded: VaultCapacityExceeded */ -export const JITO_VAULT_ERROR__VAULT_CAPACITY_EXCEEDED = 0x3f1; // 1009 +export const JITO_VAULT_ERROR__VAULT_CAPACITY_EXCEEDED = 0x3f2; // 1010 /** VaultSlasherAdminInvalid: VaultSlasherAdminInvalid */ -export const JITO_VAULT_ERROR__VAULT_SLASHER_ADMIN_INVALID = 0x3f2; // 1010 +export const JITO_VAULT_ERROR__VAULT_SLASHER_ADMIN_INVALID = 0x3f3; // 1011 /** VaultNcnAdminInvalid: VaultNcnAdminInvalid */ -export const JITO_VAULT_ERROR__VAULT_NCN_ADMIN_INVALID = 0x3f3; // 1011 +export const JITO_VAULT_ERROR__VAULT_NCN_ADMIN_INVALID = 0x3f4; // 1012 /** VaultFeeAdminInvalid: VaultFeeAdminInvalid */ -export const JITO_VAULT_ERROR__VAULT_FEE_ADMIN_INVALID = 0x3f4; // 1012 +export const JITO_VAULT_ERROR__VAULT_FEE_ADMIN_INVALID = 0x3f5; // 1013 /** ConfigAdminInvalid: ConfigAdminInvalid */ -export const JITO_VAULT_ERROR__CONFIG_ADMIN_INVALID = 0x3f5; // 1013 +export const JITO_VAULT_ERROR__CONFIG_ADMIN_INVALID = 0x3f6; // 1014 /** ConfigFeeAdminInvalid: ConfigFeeAdminInvalid */ -export const JITO_VAULT_ERROR__CONFIG_FEE_ADMIN_INVALID = 0x3f6; // 1014 +export const JITO_VAULT_ERROR__CONFIG_FEE_ADMIN_INVALID = 0x3f7; // 1015 /** VaultFeeCapExceeded: VaultFeeCapExceeded */ -export const JITO_VAULT_ERROR__VAULT_FEE_CAP_EXCEEDED = 0x3f7; // 1015 +export const JITO_VAULT_ERROR__VAULT_FEE_CAP_EXCEEDED = 0x3f8; // 1016 /** VaultFeeChangeTooSoon: VaultFeeChangeTooSoon */ -export const JITO_VAULT_ERROR__VAULT_FEE_CHANGE_TOO_SOON = 0x3f8; // 1016 +export const JITO_VAULT_ERROR__VAULT_FEE_CHANGE_TOO_SOON = 0x3f9; // 1017 /** VaultFeeBumpTooLarge: VaultFeeBumpTooLarge */ -export const JITO_VAULT_ERROR__VAULT_FEE_BUMP_TOO_LARGE = 0x3f9; // 1017 +export const JITO_VAULT_ERROR__VAULT_FEE_BUMP_TOO_LARGE = 0x3fa; // 1018 /** VaultUnderflow: VaultUnderflow */ -export const JITO_VAULT_ERROR__VAULT_UNDERFLOW = 0x3fa; // 1018 +export const JITO_VAULT_ERROR__VAULT_UNDERFLOW = 0x3fb; // 1019 /** VaultUpdateNeeded: VaultUpdateNeeded */ -export const JITO_VAULT_ERROR__VAULT_UPDATE_NEEDED = 0x3fb; // 1019 +export const JITO_VAULT_ERROR__VAULT_UPDATE_NEEDED = 0x3fc; // 1020 /** VaultIsUpdated: VaultIsUpdated */ -export const JITO_VAULT_ERROR__VAULT_IS_UPDATED = 0x3fc; // 1020 +export const JITO_VAULT_ERROR__VAULT_IS_UPDATED = 0x3fd; // 1021 /** VaultOperatorDelegationUpdateNeeded: VaultOperatorDelegationUpdateNeeded */ -export const JITO_VAULT_ERROR__VAULT_OPERATOR_DELEGATION_UPDATE_NEEDED = 0x3fd; // 1021 +export const JITO_VAULT_ERROR__VAULT_OPERATOR_DELEGATION_UPDATE_NEEDED = 0x3fe; // 1022 /** VaultOperatorDelegationIsUpdated: VaultOperatorDelegationIsUpdated */ -export const JITO_VAULT_ERROR__VAULT_OPERATOR_DELEGATION_IS_UPDATED = 0x3fe; // 1022 +export const JITO_VAULT_ERROR__VAULT_OPERATOR_DELEGATION_IS_UPDATED = 0x3ff; // 1023 /** VaultUpdateIncorrectIndex: VaultUpdateIncorrectIndex */ -export const JITO_VAULT_ERROR__VAULT_UPDATE_INCORRECT_INDEX = 0x3ff; // 1023 +export const JITO_VAULT_ERROR__VAULT_UPDATE_INCORRECT_INDEX = 0x400; // 1024 /** VaultUpdateStateNotFinishedUpdating: VaultUpdateStateNotFinishedUpdating */ -export const JITO_VAULT_ERROR__VAULT_UPDATE_STATE_NOT_FINISHED_UPDATING = 0x400; // 1024 +export const JITO_VAULT_ERROR__VAULT_UPDATE_STATE_NOT_FINISHED_UPDATING = 0x401; // 1025 /** VaultSecurityOverflow: VaultSecurityOverflow */ -export const JITO_VAULT_ERROR__VAULT_SECURITY_OVERFLOW = 0x401; // 1025 +export const JITO_VAULT_ERROR__VAULT_SECURITY_OVERFLOW = 0x402; // 1026 /** VaultSlashIncomplete: VaultSlashIncomplete */ -export const JITO_VAULT_ERROR__VAULT_SLASH_INCOMPLETE = 0x402; // 1026 +export const JITO_VAULT_ERROR__VAULT_SLASH_INCOMPLETE = 0x403; // 1027 /** VaultSecurityUnderflow: VaultSecurityUnderflow */ -export const JITO_VAULT_ERROR__VAULT_SECURITY_UNDERFLOW = 0x403; // 1027 +export const JITO_VAULT_ERROR__VAULT_SECURITY_UNDERFLOW = 0x404; // 1028 /** SlippageError: SlippageError */ -export const JITO_VAULT_ERROR__SLIPPAGE_ERROR = 0x404; // 1028 +export const JITO_VAULT_ERROR__SLIPPAGE_ERROR = 0x405; // 1029 /** VaultStakerWithdrawalTicketNotWithdrawable: VaultStakerWithdrawalTicketNotWithdrawable */ -export const JITO_VAULT_ERROR__VAULT_STAKER_WITHDRAWAL_TICKET_NOT_WITHDRAWABLE = 0x405; // 1029 +export const JITO_VAULT_ERROR__VAULT_STAKER_WITHDRAWAL_TICKET_NOT_WITHDRAWABLE = 0x406; // 1030 /** VaultNcnSlasherTicketFailedCooldown: VaultNcnSlasherTicketFailedCooldown */ -export const JITO_VAULT_ERROR__VAULT_NCN_SLASHER_TICKET_FAILED_COOLDOWN = 0x406; // 1030 +export const JITO_VAULT_ERROR__VAULT_NCN_SLASHER_TICKET_FAILED_COOLDOWN = 0x407; // 1031 /** VaultNcnSlasherTicketFailedWarmup: VaultNcnSlasherTicketFailedWarmup */ -export const JITO_VAULT_ERROR__VAULT_NCN_SLASHER_TICKET_FAILED_WARMUP = 0x407; // 1031 +export const JITO_VAULT_ERROR__VAULT_NCN_SLASHER_TICKET_FAILED_WARMUP = 0x408; // 1032 /** VaultNcnTicketFailedCooldown: VaultNcnTicketFailedCooldown */ -export const JITO_VAULT_ERROR__VAULT_NCN_TICKET_FAILED_COOLDOWN = 0x408; // 1032 +export const JITO_VAULT_ERROR__VAULT_NCN_TICKET_FAILED_COOLDOWN = 0x409; // 1033 /** VaultNcnTicketFailedWarmup: VaultNcnTicketFailedWarmup */ -export const JITO_VAULT_ERROR__VAULT_NCN_TICKET_FAILED_WARMUP = 0x409; // 1033 +export const JITO_VAULT_ERROR__VAULT_NCN_TICKET_FAILED_WARMUP = 0x40a; // 1034 /** VaultNcnTicketUnslashable: VaultNcnTicketUnslashable */ -export const JITO_VAULT_ERROR__VAULT_NCN_TICKET_UNSLASHABLE = 0x40a; // 1034 +export const JITO_VAULT_ERROR__VAULT_NCN_TICKET_UNSLASHABLE = 0x40b; // 1035 /** OperatorVaultTicketUnslashable: OperatorVaultTicketUnslashable */ -export const JITO_VAULT_ERROR__OPERATOR_VAULT_TICKET_UNSLASHABLE = 0x40b; // 1035 +export const JITO_VAULT_ERROR__OPERATOR_VAULT_TICKET_UNSLASHABLE = 0x40c; // 1036 /** NcnOperatorStateUnslashable: NcnOperatorStateUnslashable */ -export const JITO_VAULT_ERROR__NCN_OPERATOR_STATE_UNSLASHABLE = 0x40c; // 1036 +export const JITO_VAULT_ERROR__NCN_OPERATOR_STATE_UNSLASHABLE = 0x40d; // 1037 /** VaultNcnSlasherTicketUnslashable: VaultNcnSlasherTicketUnslashable */ -export const JITO_VAULT_ERROR__VAULT_NCN_SLASHER_TICKET_UNSLASHABLE = 0x40d; // 1037 +export const JITO_VAULT_ERROR__VAULT_NCN_SLASHER_TICKET_UNSLASHABLE = 0x40e; // 1038 /** NcnVaultTicketUnslashable: NcnVaultTicketUnslashable */ -export const JITO_VAULT_ERROR__NCN_VAULT_TICKET_UNSLASHABLE = 0x40e; // 1038 +export const JITO_VAULT_ERROR__NCN_VAULT_TICKET_UNSLASHABLE = 0x40f; // 1039 /** NcnVaultSlasherTicketUnslashable: NcnVaultSlasherTicketUnslashable */ -export const JITO_VAULT_ERROR__NCN_VAULT_SLASHER_TICKET_UNSLASHABLE = 0x40f; // 1039 +export const JITO_VAULT_ERROR__NCN_VAULT_SLASHER_TICKET_UNSLASHABLE = 0x410; // 1040 /** VaultMaxSlashedPerOperatorExceeded: VaultMaxSlashedPerOperatorExceeded */ -export const JITO_VAULT_ERROR__VAULT_MAX_SLASHED_PER_OPERATOR_EXCEEDED = 0x410; // 1040 +export const JITO_VAULT_ERROR__VAULT_MAX_SLASHED_PER_OPERATOR_EXCEEDED = 0x411; // 1041 /** VaultStakerWithdrawalTicketInvalidStaker: VaultStakerWithdrawalTicketInvalidStaker */ -export const JITO_VAULT_ERROR__VAULT_STAKER_WITHDRAWAL_TICKET_INVALID_STAKER = 0x411; // 1041 +export const JITO_VAULT_ERROR__VAULT_STAKER_WITHDRAWAL_TICKET_INVALID_STAKER = 0x412; // 1042 /** SlasherOverflow: SlasherOverflow */ -export const JITO_VAULT_ERROR__SLASHER_OVERFLOW = 0x412; // 1042 +export const JITO_VAULT_ERROR__SLASHER_OVERFLOW = 0x413; // 1043 /** NcnOverflow: NcnOverflow */ -export const JITO_VAULT_ERROR__NCN_OVERFLOW = 0x413; // 1043 +export const JITO_VAULT_ERROR__NCN_OVERFLOW = 0x414; // 1044 /** OperatorOverflow: OperatorOverflow */ -export const JITO_VAULT_ERROR__OPERATOR_OVERFLOW = 0x414; // 1044 +export const JITO_VAULT_ERROR__OPERATOR_OVERFLOW = 0x415; // 1045 /** VaultDelegationZero: VaultDelegationZero */ -export const JITO_VAULT_ERROR__VAULT_DELEGATION_ZERO = 0x415; // 1045 +export const JITO_VAULT_ERROR__VAULT_DELEGATION_ZERO = 0x416; // 1046 /** VaultCooldownZero: VaultCooldownZero */ -export const JITO_VAULT_ERROR__VAULT_COOLDOWN_ZERO = 0x416; // 1046 +export const JITO_VAULT_ERROR__VAULT_COOLDOWN_ZERO = 0x417; // 1047 /** VaultBurnZero: VaultBurnZero */ -export const JITO_VAULT_ERROR__VAULT_BURN_ZERO = 0x417; // 1047 +export const JITO_VAULT_ERROR__VAULT_BURN_ZERO = 0x418; // 1048 /** VaultEnqueueWithdrawalAmountZero: VaultEnqueueWithdrawalAmountZero */ -export const JITO_VAULT_ERROR__VAULT_ENQUEUE_WITHDRAWAL_AMOUNT_ZERO = 0x418; // 1048 +export const JITO_VAULT_ERROR__VAULT_ENQUEUE_WITHDRAWAL_AMOUNT_ZERO = 0x419; // 1049 /** VaultMintZero: VaultMintZero */ -export const JITO_VAULT_ERROR__VAULT_MINT_ZERO = 0x419; // 1049 +export const JITO_VAULT_ERROR__VAULT_MINT_ZERO = 0x41a; // 1050 /** VaultIsPaused: VaultIsPaused */ -export const JITO_VAULT_ERROR__VAULT_IS_PAUSED = 0x41a; // 1050 +export const JITO_VAULT_ERROR__VAULT_IS_PAUSED = 0x41b; // 1051 /** InvalidDepositor: InvalidDepositor */ -export const JITO_VAULT_ERROR__INVALID_DEPOSITOR = 0x41b; // 1051 +export const JITO_VAULT_ERROR__INVALID_DEPOSITOR = 0x41c; // 1052 /** InvalidDepositTokenAccount: InvalidDepositTokenAccount */ -export const JITO_VAULT_ERROR__INVALID_DEPOSIT_TOKEN_ACCOUNT = 0x41c; // 1052 +export const JITO_VAULT_ERROR__INVALID_DEPOSIT_TOKEN_ACCOUNT = 0x41d; // 1053 /** NoSupportedMintBalanceChange: NoSupportedMintBalanceChange */ -export const JITO_VAULT_ERROR__NO_SUPPORTED_MINT_BALANCE_CHANGE = 0x41d; // 1053 +export const JITO_VAULT_ERROR__NO_SUPPORTED_MINT_BALANCE_CHANGE = 0x41e; // 1054 /** InvalidEpochLength: InvalidEpochLength */ -export const JITO_VAULT_ERROR__INVALID_EPOCH_LENGTH = 0x41e; // 1054 +export const JITO_VAULT_ERROR__INVALID_EPOCH_LENGTH = 0x41f; // 1055 /** VaultRewardFeeDeltaTooLarge: VaultRewardFeeDeltaTooLarge */ -export const JITO_VAULT_ERROR__VAULT_REWARD_FEE_DELTA_TOO_LARGE = 0x41f; // 1055 +export const JITO_VAULT_ERROR__VAULT_REWARD_FEE_DELTA_TOO_LARGE = 0x420; // 1056 /** VaultRewardFeeIsZero: VaultRewardFeeIsZero */ -export const JITO_VAULT_ERROR__VAULT_REWARD_FEE_IS_ZERO = 0x420; // 1056 +export const JITO_VAULT_ERROR__VAULT_REWARD_FEE_IS_ZERO = 0x421; // 1057 /** VrtOutCannotBeZero: VrtOutCannotBeZero */ -export const JITO_VAULT_ERROR__VRT_OUT_CANNOT_BE_ZERO = 0x421; // 1057 +export const JITO_VAULT_ERROR__VRT_OUT_CANNOT_BE_ZERO = 0x422; // 1058 /** NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate: NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate */ -export const JITO_VAULT_ERROR__NON_ZERO_ADDITIONAL_ASSETS_NEEDED_FOR_WITHDRAWAL_AT_END_OF_UPDATE = 0x422; // 1058 +export const JITO_VAULT_ERROR__NON_ZERO_ADDITIONAL_ASSETS_NEEDED_FOR_WITHDRAWAL_AT_END_OF_UPDATE = 0x423; // 1059 /** ArithmeticOverflow: ArithmeticOverflow */ export const JITO_VAULT_ERROR__ARITHMETIC_OVERFLOW = 0xbb8; // 3000 /** ArithmeticUnderflow: ArithmeticUnderflow */ @@ -171,6 +173,7 @@ export type JitoVaultError = | typeof JITO_VAULT_ERROR__VAULT_FEE_BUMP_TOO_LARGE | typeof JITO_VAULT_ERROR__VAULT_FEE_CAP_EXCEEDED | typeof JITO_VAULT_ERROR__VAULT_FEE_CHANGE_TOO_SOON + | typeof JITO_VAULT_ERROR__VAULT_INITIAL_AMOUNT_FAILED | typeof JITO_VAULT_ERROR__VAULT_INSUFFICIENT_FUNDS | typeof JITO_VAULT_ERROR__VAULT_IS_PAUSED | typeof JITO_VAULT_ERROR__VAULT_IS_UPDATED @@ -237,6 +240,7 @@ if (process.env.NODE_ENV !== 'production') { [JITO_VAULT_ERROR__VAULT_FEE_BUMP_TOO_LARGE]: `VaultFeeBumpTooLarge`, [JITO_VAULT_ERROR__VAULT_FEE_CAP_EXCEEDED]: `VaultFeeCapExceeded`, [JITO_VAULT_ERROR__VAULT_FEE_CHANGE_TOO_SOON]: `VaultFeeChangeTooSoon`, + [JITO_VAULT_ERROR__VAULT_INITIAL_AMOUNT_FAILED]: `VaultInitialAmountFailed`, [JITO_VAULT_ERROR__VAULT_INSUFFICIENT_FUNDS]: `VaultInsufficientFunds`, [JITO_VAULT_ERROR__VAULT_IS_PAUSED]: `VaultIsPaused`, [JITO_VAULT_ERROR__VAULT_IS_UPDATED]: `VaultIsUpdated`, diff --git a/clients/js/vault_client/instructions/initializeVault.ts b/clients/js/vault_client/instructions/initializeVault.ts index 5d7e4bc0..b52bc738 100644 --- a/clients/js/vault_client/instructions/initializeVault.ts +++ b/clients/js/vault_client/instructions/initializeVault.ts @@ -12,6 +12,8 @@ import { getStructEncoder, getU16Decoder, getU16Encoder, + getU64Decoder, + getU64Encoder, getU8Decoder, getU8Encoder, transformEncoder, @@ -47,6 +49,10 @@ export type InitializeVaultInstruction< TAccountStMint extends string | IAccountMeta = string, TAccountAdminStTokenAccount extends string | IAccountMeta = string, TAccountVaultStTokenAccount extends string | IAccountMeta = string, + TAccountBurnVault extends string | IAccountMeta = string, + TAccountBurnVaultVrtTokenAccount extends + | string + | IAccountMeta = string, TAccountAdmin extends string | IAccountMeta = string, TAccountBase extends string | IAccountMeta = string, TAccountSystemProgram extends @@ -55,6 +61,7 @@ export type InitializeVaultInstruction< TAccountTokenProgram extends | string | IAccountMeta = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', + TAccountAssociatedTokenProgram extends string | IAccountMeta = string, TRemainingAccounts extends readonly IAccountMeta[] = [], > = IInstruction & IInstructionWithData & @@ -79,6 +86,12 @@ export type InitializeVaultInstruction< TAccountVaultStTokenAccount extends string ? WritableAccount : TAccountVaultStTokenAccount, + TAccountBurnVault extends string + ? ReadonlyAccount + : TAccountBurnVault, + TAccountBurnVaultVrtTokenAccount extends string + ? WritableAccount + : TAccountBurnVaultVrtTokenAccount, TAccountAdmin extends string ? WritableSignerAccount & IAccountSignerMeta @@ -92,6 +105,9 @@ export type InitializeVaultInstruction< TAccountTokenProgram extends string ? ReadonlyAccount : TAccountTokenProgram, + TAccountAssociatedTokenProgram extends string + ? ReadonlyAccount + : TAccountAssociatedTokenProgram, ...TRemainingAccounts, ] >; @@ -102,6 +118,7 @@ export type InitializeVaultInstructionData = { withdrawalFeeBps: number; rewardFeeBps: number; decimals: number; + initializeTokenAmount: bigint; }; export type InitializeVaultInstructionDataArgs = { @@ -109,6 +126,7 @@ export type InitializeVaultInstructionDataArgs = { withdrawalFeeBps: number; rewardFeeBps: number; decimals: number; + initializeTokenAmount: number | bigint; }; export function getInitializeVaultInstructionDataEncoder(): Encoder { @@ -119,6 +137,7 @@ export function getInitializeVaultInstructionDataEncoder(): Encoder ({ ...value, discriminator: INITIALIZE_VAULT_DISCRIMINATOR }) ); @@ -131,6 +150,7 @@ export function getInitializeVaultInstructionDataDecoder(): Decoder = { config: Address; vault: Address; @@ -162,14 +185,18 @@ export type InitializeVaultInput< stMint: Address; adminStTokenAccount: Address; vaultStTokenAccount: Address; + burnVault: Address; + burnVaultVrtTokenAccount: Address; admin: TransactionSigner; base: TransactionSigner; systemProgram?: Address; tokenProgram?: Address; + associatedTokenProgram: Address; depositFeeBps: InitializeVaultInstructionDataArgs['depositFeeBps']; withdrawalFeeBps: InitializeVaultInstructionDataArgs['withdrawalFeeBps']; rewardFeeBps: InitializeVaultInstructionDataArgs['rewardFeeBps']; decimals: InitializeVaultInstructionDataArgs['decimals']; + initializeTokenAmount: InitializeVaultInstructionDataArgs['initializeTokenAmount']; }; export function getInitializeVaultInstruction< @@ -179,10 +206,13 @@ export function getInitializeVaultInstruction< TAccountStMint extends string, TAccountAdminStTokenAccount extends string, TAccountVaultStTokenAccount extends string, + TAccountBurnVault extends string, + TAccountBurnVaultVrtTokenAccount extends string, TAccountAdmin extends string, TAccountBase extends string, TAccountSystemProgram extends string, TAccountTokenProgram extends string, + TAccountAssociatedTokenProgram extends string, >( input: InitializeVaultInput< TAccountConfig, @@ -191,10 +221,13 @@ export function getInitializeVaultInstruction< TAccountStMint, TAccountAdminStTokenAccount, TAccountVaultStTokenAccount, + TAccountBurnVault, + TAccountBurnVaultVrtTokenAccount, TAccountAdmin, TAccountBase, TAccountSystemProgram, - TAccountTokenProgram + TAccountTokenProgram, + TAccountAssociatedTokenProgram > ): InitializeVaultInstruction< typeof JITO_VAULT_PROGRAM_ADDRESS, @@ -204,10 +237,13 @@ export function getInitializeVaultInstruction< TAccountStMint, TAccountAdminStTokenAccount, TAccountVaultStTokenAccount, + TAccountBurnVault, + TAccountBurnVaultVrtTokenAccount, TAccountAdmin, TAccountBase, TAccountSystemProgram, - TAccountTokenProgram + TAccountTokenProgram, + TAccountAssociatedTokenProgram > { // Program address. const programAddress = JITO_VAULT_PROGRAM_ADDRESS; @@ -226,10 +262,19 @@ export function getInitializeVaultInstruction< value: input.vaultStTokenAccount ?? null, isWritable: true, }, + burnVault: { value: input.burnVault ?? null, isWritable: false }, + burnVaultVrtTokenAccount: { + value: input.burnVaultVrtTokenAccount ?? null, + isWritable: true, + }, admin: { value: input.admin ?? null, isWritable: true }, base: { value: input.base ?? null, isWritable: false }, systemProgram: { value: input.systemProgram ?? null, isWritable: false }, tokenProgram: { value: input.tokenProgram ?? null, isWritable: false }, + associatedTokenProgram: { + value: input.associatedTokenProgram ?? null, + isWritable: false, + }, }; const accounts = originalAccounts as Record< keyof typeof originalAccounts, @@ -258,10 +303,13 @@ export function getInitializeVaultInstruction< getAccountMeta(accounts.stMint), getAccountMeta(accounts.adminStTokenAccount), getAccountMeta(accounts.vaultStTokenAccount), + getAccountMeta(accounts.burnVault), + getAccountMeta(accounts.burnVaultVrtTokenAccount), getAccountMeta(accounts.admin), getAccountMeta(accounts.base), getAccountMeta(accounts.systemProgram), getAccountMeta(accounts.tokenProgram), + getAccountMeta(accounts.associatedTokenProgram), ], programAddress, data: getInitializeVaultInstructionDataEncoder().encode( @@ -275,10 +323,13 @@ export function getInitializeVaultInstruction< TAccountStMint, TAccountAdminStTokenAccount, TAccountVaultStTokenAccount, + TAccountBurnVault, + TAccountBurnVaultVrtTokenAccount, TAccountAdmin, TAccountBase, TAccountSystemProgram, - TAccountTokenProgram + TAccountTokenProgram, + TAccountAssociatedTokenProgram >; return instruction; @@ -296,10 +347,13 @@ export type ParsedInitializeVaultInstruction< stMint: TAccountMetas[3]; adminStTokenAccount: TAccountMetas[4]; vaultStTokenAccount: TAccountMetas[5]; - admin: TAccountMetas[6]; - base: TAccountMetas[7]; - systemProgram: TAccountMetas[8]; - tokenProgram: TAccountMetas[9]; + burnVault: TAccountMetas[6]; + burnVaultVrtTokenAccount: TAccountMetas[7]; + admin: TAccountMetas[8]; + base: TAccountMetas[9]; + systemProgram: TAccountMetas[10]; + tokenProgram: TAccountMetas[11]; + associatedTokenProgram: TAccountMetas[12]; }; data: InitializeVaultInstructionData; }; @@ -312,7 +366,7 @@ export function parseInitializeVaultInstruction< IInstructionWithAccounts & IInstructionWithData ): ParsedInitializeVaultInstruction { - if (instruction.accounts.length < 10) { + if (instruction.accounts.length < 13) { // TODO: Coded error. throw new Error('Not enough accounts'); } @@ -331,10 +385,13 @@ export function parseInitializeVaultInstruction< stMint: getNextAccount(), adminStTokenAccount: getNextAccount(), vaultStTokenAccount: getNextAccount(), + burnVault: getNextAccount(), + burnVaultVrtTokenAccount: getNextAccount(), admin: getNextAccount(), base: getNextAccount(), systemProgram: getNextAccount(), tokenProgram: getNextAccount(), + associatedTokenProgram: getNextAccount(), }, data: getInitializeVaultInstructionDataDecoder().decode(instruction.data), }; diff --git a/clients/rust/vault_client/src/generated/errors/jito_vault.rs b/clients/rust/vault_client/src/generated/errors/jito_vault.rs index 2b83d2cc..18c67454 100644 --- a/clients/rust/vault_client/src/generated/errors/jito_vault.rs +++ b/clients/rust/vault_client/src/generated/errors/jito_vault.rs @@ -12,180 +12,183 @@ pub enum JitoVaultError { /// 1000 - VaultSlashUnderflow #[error("VaultSlashUnderflow")] VaultSlashUnderflow = 0x3E8, - /// 1001 - VaultInsufficientFunds + /// 1001 - VaultInitialAmountFailed + #[error("VaultInitialAmountFailed")] + VaultInitialAmountFailed = 0x3E9, + /// 1002 - VaultInsufficientFunds #[error("VaultInsufficientFunds")] - VaultInsufficientFunds = 0x3E9, - /// 1002 - VaultOverflow + VaultInsufficientFunds = 0x3EA, + /// 1003 - VaultOverflow #[error("VaultOverflow")] - VaultOverflow = 0x3EA, - /// 1003 - VaultOperatorAdminInvalid + VaultOverflow = 0x3EB, + /// 1004 - VaultOperatorAdminInvalid #[error("VaultOperatorAdminInvalid")] - VaultOperatorAdminInvalid = 0x3EB, - /// 1004 - VaultAdminInvalid + VaultOperatorAdminInvalid = 0x3EC, + /// 1005 - VaultAdminInvalid #[error("VaultAdminInvalid")] - VaultAdminInvalid = 0x3EC, - /// 1005 - VaultCapacityAdminInvalid + VaultAdminInvalid = 0x3ED, + /// 1006 - VaultCapacityAdminInvalid #[error("VaultCapacityAdminInvalid")] - VaultCapacityAdminInvalid = 0x3ED, - /// 1006 - VaultMintBurnAdminInvalid + VaultCapacityAdminInvalid = 0x3EE, + /// 1007 - VaultMintBurnAdminInvalid #[error("VaultMintBurnAdminInvalid")] - VaultMintBurnAdminInvalid = 0x3EE, - /// 1007 - VaultDelegationAdminInvalid + VaultMintBurnAdminInvalid = 0x3EF, + /// 1008 - VaultDelegationAdminInvalid #[error("VaultDelegationAdminInvalid")] - VaultDelegationAdminInvalid = 0x3EF, - /// 1008 - VaultDelegateAssetAdminInvalid + VaultDelegationAdminInvalid = 0x3F0, + /// 1009 - VaultDelegateAssetAdminInvalid #[error("VaultDelegateAssetAdminInvalid")] - VaultDelegateAssetAdminInvalid = 0x3F0, - /// 1009 - VaultCapacityExceeded + VaultDelegateAssetAdminInvalid = 0x3F1, + /// 1010 - VaultCapacityExceeded #[error("VaultCapacityExceeded")] - VaultCapacityExceeded = 0x3F1, - /// 1010 - VaultSlasherAdminInvalid + VaultCapacityExceeded = 0x3F2, + /// 1011 - VaultSlasherAdminInvalid #[error("VaultSlasherAdminInvalid")] - VaultSlasherAdminInvalid = 0x3F2, - /// 1011 - VaultNcnAdminInvalid + VaultSlasherAdminInvalid = 0x3F3, + /// 1012 - VaultNcnAdminInvalid #[error("VaultNcnAdminInvalid")] - VaultNcnAdminInvalid = 0x3F3, - /// 1012 - VaultFeeAdminInvalid + VaultNcnAdminInvalid = 0x3F4, + /// 1013 - VaultFeeAdminInvalid #[error("VaultFeeAdminInvalid")] - VaultFeeAdminInvalid = 0x3F4, - /// 1013 - ConfigAdminInvalid + VaultFeeAdminInvalid = 0x3F5, + /// 1014 - ConfigAdminInvalid #[error("ConfigAdminInvalid")] - ConfigAdminInvalid = 0x3F5, - /// 1014 - ConfigFeeAdminInvalid + ConfigAdminInvalid = 0x3F6, + /// 1015 - ConfigFeeAdminInvalid #[error("ConfigFeeAdminInvalid")] - ConfigFeeAdminInvalid = 0x3F6, - /// 1015 - VaultFeeCapExceeded + ConfigFeeAdminInvalid = 0x3F7, + /// 1016 - VaultFeeCapExceeded #[error("VaultFeeCapExceeded")] - VaultFeeCapExceeded = 0x3F7, - /// 1016 - VaultFeeChangeTooSoon + VaultFeeCapExceeded = 0x3F8, + /// 1017 - VaultFeeChangeTooSoon #[error("VaultFeeChangeTooSoon")] - VaultFeeChangeTooSoon = 0x3F8, - /// 1017 - VaultFeeBumpTooLarge + VaultFeeChangeTooSoon = 0x3F9, + /// 1018 - VaultFeeBumpTooLarge #[error("VaultFeeBumpTooLarge")] - VaultFeeBumpTooLarge = 0x3F9, - /// 1018 - VaultUnderflow + VaultFeeBumpTooLarge = 0x3FA, + /// 1019 - VaultUnderflow #[error("VaultUnderflow")] - VaultUnderflow = 0x3FA, - /// 1019 - VaultUpdateNeeded + VaultUnderflow = 0x3FB, + /// 1020 - VaultUpdateNeeded #[error("VaultUpdateNeeded")] - VaultUpdateNeeded = 0x3FB, - /// 1020 - VaultIsUpdated + VaultUpdateNeeded = 0x3FC, + /// 1021 - VaultIsUpdated #[error("VaultIsUpdated")] - VaultIsUpdated = 0x3FC, - /// 1021 - VaultOperatorDelegationUpdateNeeded + VaultIsUpdated = 0x3FD, + /// 1022 - VaultOperatorDelegationUpdateNeeded #[error("VaultOperatorDelegationUpdateNeeded")] - VaultOperatorDelegationUpdateNeeded = 0x3FD, - /// 1022 - VaultOperatorDelegationIsUpdated + VaultOperatorDelegationUpdateNeeded = 0x3FE, + /// 1023 - VaultOperatorDelegationIsUpdated #[error("VaultOperatorDelegationIsUpdated")] - VaultOperatorDelegationIsUpdated = 0x3FE, - /// 1023 - VaultUpdateIncorrectIndex + VaultOperatorDelegationIsUpdated = 0x3FF, + /// 1024 - VaultUpdateIncorrectIndex #[error("VaultUpdateIncorrectIndex")] - VaultUpdateIncorrectIndex = 0x3FF, - /// 1024 - VaultUpdateStateNotFinishedUpdating + VaultUpdateIncorrectIndex = 0x400, + /// 1025 - VaultUpdateStateNotFinishedUpdating #[error("VaultUpdateStateNotFinishedUpdating")] - VaultUpdateStateNotFinishedUpdating = 0x400, - /// 1025 - VaultSecurityOverflow + VaultUpdateStateNotFinishedUpdating = 0x401, + /// 1026 - VaultSecurityOverflow #[error("VaultSecurityOverflow")] - VaultSecurityOverflow = 0x401, - /// 1026 - VaultSlashIncomplete + VaultSecurityOverflow = 0x402, + /// 1027 - VaultSlashIncomplete #[error("VaultSlashIncomplete")] - VaultSlashIncomplete = 0x402, - /// 1027 - VaultSecurityUnderflow + VaultSlashIncomplete = 0x403, + /// 1028 - VaultSecurityUnderflow #[error("VaultSecurityUnderflow")] - VaultSecurityUnderflow = 0x403, - /// 1028 - SlippageError + VaultSecurityUnderflow = 0x404, + /// 1029 - SlippageError #[error("SlippageError")] - SlippageError = 0x404, - /// 1029 - VaultStakerWithdrawalTicketNotWithdrawable + SlippageError = 0x405, + /// 1030 - VaultStakerWithdrawalTicketNotWithdrawable #[error("VaultStakerWithdrawalTicketNotWithdrawable")] - VaultStakerWithdrawalTicketNotWithdrawable = 0x405, - /// 1030 - VaultNcnSlasherTicketFailedCooldown + VaultStakerWithdrawalTicketNotWithdrawable = 0x406, + /// 1031 - VaultNcnSlasherTicketFailedCooldown #[error("VaultNcnSlasherTicketFailedCooldown")] - VaultNcnSlasherTicketFailedCooldown = 0x406, - /// 1031 - VaultNcnSlasherTicketFailedWarmup + VaultNcnSlasherTicketFailedCooldown = 0x407, + /// 1032 - VaultNcnSlasherTicketFailedWarmup #[error("VaultNcnSlasherTicketFailedWarmup")] - VaultNcnSlasherTicketFailedWarmup = 0x407, - /// 1032 - VaultNcnTicketFailedCooldown + VaultNcnSlasherTicketFailedWarmup = 0x408, + /// 1033 - VaultNcnTicketFailedCooldown #[error("VaultNcnTicketFailedCooldown")] - VaultNcnTicketFailedCooldown = 0x408, - /// 1033 - VaultNcnTicketFailedWarmup + VaultNcnTicketFailedCooldown = 0x409, + /// 1034 - VaultNcnTicketFailedWarmup #[error("VaultNcnTicketFailedWarmup")] - VaultNcnTicketFailedWarmup = 0x409, - /// 1034 - VaultNcnTicketUnslashable + VaultNcnTicketFailedWarmup = 0x40A, + /// 1035 - VaultNcnTicketUnslashable #[error("VaultNcnTicketUnslashable")] - VaultNcnTicketUnslashable = 0x40A, - /// 1035 - OperatorVaultTicketUnslashable + VaultNcnTicketUnslashable = 0x40B, + /// 1036 - OperatorVaultTicketUnslashable #[error("OperatorVaultTicketUnslashable")] - OperatorVaultTicketUnslashable = 0x40B, - /// 1036 - NcnOperatorStateUnslashable + OperatorVaultTicketUnslashable = 0x40C, + /// 1037 - NcnOperatorStateUnslashable #[error("NcnOperatorStateUnslashable")] - NcnOperatorStateUnslashable = 0x40C, - /// 1037 - VaultNcnSlasherTicketUnslashable + NcnOperatorStateUnslashable = 0x40D, + /// 1038 - VaultNcnSlasherTicketUnslashable #[error("VaultNcnSlasherTicketUnslashable")] - VaultNcnSlasherTicketUnslashable = 0x40D, - /// 1038 - NcnVaultTicketUnslashable + VaultNcnSlasherTicketUnslashable = 0x40E, + /// 1039 - NcnVaultTicketUnslashable #[error("NcnVaultTicketUnslashable")] - NcnVaultTicketUnslashable = 0x40E, - /// 1039 - NcnVaultSlasherTicketUnslashable + NcnVaultTicketUnslashable = 0x40F, + /// 1040 - NcnVaultSlasherTicketUnslashable #[error("NcnVaultSlasherTicketUnslashable")] - NcnVaultSlasherTicketUnslashable = 0x40F, - /// 1040 - VaultMaxSlashedPerOperatorExceeded + NcnVaultSlasherTicketUnslashable = 0x410, + /// 1041 - VaultMaxSlashedPerOperatorExceeded #[error("VaultMaxSlashedPerOperatorExceeded")] - VaultMaxSlashedPerOperatorExceeded = 0x410, - /// 1041 - VaultStakerWithdrawalTicketInvalidStaker + VaultMaxSlashedPerOperatorExceeded = 0x411, + /// 1042 - VaultStakerWithdrawalTicketInvalidStaker #[error("VaultStakerWithdrawalTicketInvalidStaker")] - VaultStakerWithdrawalTicketInvalidStaker = 0x411, - /// 1042 - SlasherOverflow + VaultStakerWithdrawalTicketInvalidStaker = 0x412, + /// 1043 - SlasherOverflow #[error("SlasherOverflow")] - SlasherOverflow = 0x412, - /// 1043 - NcnOverflow + SlasherOverflow = 0x413, + /// 1044 - NcnOverflow #[error("NcnOverflow")] - NcnOverflow = 0x413, - /// 1044 - OperatorOverflow + NcnOverflow = 0x414, + /// 1045 - OperatorOverflow #[error("OperatorOverflow")] - OperatorOverflow = 0x414, - /// 1045 - VaultDelegationZero + OperatorOverflow = 0x415, + /// 1046 - VaultDelegationZero #[error("VaultDelegationZero")] - VaultDelegationZero = 0x415, - /// 1046 - VaultCooldownZero + VaultDelegationZero = 0x416, + /// 1047 - VaultCooldownZero #[error("VaultCooldownZero")] - VaultCooldownZero = 0x416, - /// 1047 - VaultBurnZero + VaultCooldownZero = 0x417, + /// 1048 - VaultBurnZero #[error("VaultBurnZero")] - VaultBurnZero = 0x417, - /// 1048 - VaultEnqueueWithdrawalAmountZero + VaultBurnZero = 0x418, + /// 1049 - VaultEnqueueWithdrawalAmountZero #[error("VaultEnqueueWithdrawalAmountZero")] - VaultEnqueueWithdrawalAmountZero = 0x418, - /// 1049 - VaultMintZero + VaultEnqueueWithdrawalAmountZero = 0x419, + /// 1050 - VaultMintZero #[error("VaultMintZero")] - VaultMintZero = 0x419, - /// 1050 - VaultIsPaused + VaultMintZero = 0x41A, + /// 1051 - VaultIsPaused #[error("VaultIsPaused")] - VaultIsPaused = 0x41A, - /// 1051 - InvalidDepositor + VaultIsPaused = 0x41B, + /// 1052 - InvalidDepositor #[error("InvalidDepositor")] - InvalidDepositor = 0x41B, - /// 1052 - InvalidDepositTokenAccount + InvalidDepositor = 0x41C, + /// 1053 - InvalidDepositTokenAccount #[error("InvalidDepositTokenAccount")] - InvalidDepositTokenAccount = 0x41C, - /// 1053 - NoSupportedMintBalanceChange + InvalidDepositTokenAccount = 0x41D, + /// 1054 - NoSupportedMintBalanceChange #[error("NoSupportedMintBalanceChange")] - NoSupportedMintBalanceChange = 0x41D, - /// 1054 - InvalidEpochLength + NoSupportedMintBalanceChange = 0x41E, + /// 1055 - InvalidEpochLength #[error("InvalidEpochLength")] - InvalidEpochLength = 0x41E, - /// 1055 - VaultRewardFeeDeltaTooLarge + InvalidEpochLength = 0x41F, + /// 1056 - VaultRewardFeeDeltaTooLarge #[error("VaultRewardFeeDeltaTooLarge")] - VaultRewardFeeDeltaTooLarge = 0x41F, - /// 1056 - VaultRewardFeeIsZero + VaultRewardFeeDeltaTooLarge = 0x420, + /// 1057 - VaultRewardFeeIsZero #[error("VaultRewardFeeIsZero")] - VaultRewardFeeIsZero = 0x420, - /// 1057 - VrtOutCannotBeZero + VaultRewardFeeIsZero = 0x421, + /// 1058 - VrtOutCannotBeZero #[error("VrtOutCannotBeZero")] - VrtOutCannotBeZero = 0x421, - /// 1058 - NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate + VrtOutCannotBeZero = 0x422, + /// 1059 - NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate #[error("NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate")] - NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate = 0x422, + NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate = 0x423, /// 3000 - ArithmeticOverflow #[error("ArithmeticOverflow")] ArithmeticOverflow = 0xBB8, diff --git a/clients/rust/vault_client/src/generated/instructions/initialize_vault.rs b/clients/rust/vault_client/src/generated/instructions/initialize_vault.rs index 54ee99e4..4466ab96 100644 --- a/clients/rust/vault_client/src/generated/instructions/initialize_vault.rs +++ b/clients/rust/vault_client/src/generated/instructions/initialize_vault.rs @@ -20,6 +20,10 @@ pub struct InitializeVault { pub vault_st_token_account: solana_program::pubkey::Pubkey, + pub burn_vault: solana_program::pubkey::Pubkey, + + pub burn_vault_vrt_token_account: solana_program::pubkey::Pubkey, + pub admin: solana_program::pubkey::Pubkey, pub base: solana_program::pubkey::Pubkey, @@ -27,6 +31,8 @@ pub struct InitializeVault { pub system_program: solana_program::pubkey::Pubkey, pub token_program: solana_program::pubkey::Pubkey, + + pub associated_token_program: solana_program::pubkey::Pubkey, } impl InitializeVault { @@ -42,7 +48,7 @@ impl InitializeVault { args: InitializeVaultInstructionArgs, remaining_accounts: &[solana_program::instruction::AccountMeta], ) -> solana_program::instruction::Instruction { - let mut accounts = Vec::with_capacity(10 + remaining_accounts.len()); + let mut accounts = Vec::with_capacity(13 + remaining_accounts.len()); accounts.push(solana_program::instruction::AccountMeta::new( self.config, false, @@ -66,6 +72,14 @@ impl InitializeVault { self.vault_st_token_account, false, )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.burn_vault, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.burn_vault_vrt_token_account, + false, + )); accounts.push(solana_program::instruction::AccountMeta::new( self.admin, true, )); @@ -80,6 +94,10 @@ impl InitializeVault { self.token_program, false, )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.associated_token_program, + false, + )); accounts.extend_from_slice(remaining_accounts); let mut data = InitializeVaultInstructionData::new().try_to_vec().unwrap(); let mut args = args.try_to_vec().unwrap(); @@ -117,6 +135,7 @@ pub struct InitializeVaultInstructionArgs { pub withdrawal_fee_bps: u16, pub reward_fee_bps: u16, pub decimals: u8, + pub initialize_token_amount: u64, } /// Instruction builder for `InitializeVault`. @@ -129,10 +148,13 @@ pub struct InitializeVaultInstructionArgs { /// 3. `[]` st_mint /// 4. `[writable]` admin_st_token_account /// 5. `[writable]` vault_st_token_account -/// 6. `[writable, signer]` admin -/// 7. `[signer]` base -/// 8. `[optional]` system_program (default to `11111111111111111111111111111111`) -/// 9. `[optional]` token_program (default to `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`) +/// 6. `[]` burn_vault +/// 7. `[writable]` burn_vault_vrt_token_account +/// 8. `[writable, signer]` admin +/// 9. `[signer]` base +/// 10. `[optional]` system_program (default to `11111111111111111111111111111111`) +/// 11. `[optional]` token_program (default to `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`) +/// 12. `[]` associated_token_program #[derive(Clone, Debug, Default)] pub struct InitializeVaultBuilder { config: Option, @@ -141,14 +163,18 @@ pub struct InitializeVaultBuilder { st_mint: Option, admin_st_token_account: Option, vault_st_token_account: Option, + burn_vault: Option, + burn_vault_vrt_token_account: Option, admin: Option, base: Option, system_program: Option, token_program: Option, + associated_token_program: Option, deposit_fee_bps: Option, withdrawal_fee_bps: Option, reward_fee_bps: Option, decimals: Option, + initialize_token_amount: Option, __remaining_accounts: Vec, } @@ -193,6 +219,19 @@ impl InitializeVaultBuilder { self } #[inline(always)] + pub fn burn_vault(&mut self, burn_vault: solana_program::pubkey::Pubkey) -> &mut Self { + self.burn_vault = Some(burn_vault); + self + } + #[inline(always)] + pub fn burn_vault_vrt_token_account( + &mut self, + burn_vault_vrt_token_account: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.burn_vault_vrt_token_account = Some(burn_vault_vrt_token_account); + self + } + #[inline(always)] pub fn admin(&mut self, admin: solana_program::pubkey::Pubkey) -> &mut Self { self.admin = Some(admin); self @@ -215,6 +254,14 @@ impl InitializeVaultBuilder { self } #[inline(always)] + pub fn associated_token_program( + &mut self, + associated_token_program: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.associated_token_program = Some(associated_token_program); + self + } + #[inline(always)] pub fn deposit_fee_bps(&mut self, deposit_fee_bps: u16) -> &mut Self { self.deposit_fee_bps = Some(deposit_fee_bps); self @@ -234,6 +281,11 @@ impl InitializeVaultBuilder { self.decimals = Some(decimals); self } + #[inline(always)] + pub fn initialize_token_amount(&mut self, initialize_token_amount: u64) -> &mut Self { + self.initialize_token_amount = Some(initialize_token_amount); + self + } /// Add an aditional account to the instruction. #[inline(always)] pub fn add_remaining_account( @@ -265,6 +317,10 @@ impl InitializeVaultBuilder { vault_st_token_account: self .vault_st_token_account .expect("vault_st_token_account is not set"), + burn_vault: self.burn_vault.expect("burn_vault is not set"), + burn_vault_vrt_token_account: self + .burn_vault_vrt_token_account + .expect("burn_vault_vrt_token_account is not set"), admin: self.admin.expect("admin is not set"), base: self.base.expect("base is not set"), system_program: self @@ -273,6 +329,9 @@ impl InitializeVaultBuilder { token_program: self.token_program.unwrap_or(solana_program::pubkey!( "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" )), + associated_token_program: self + .associated_token_program + .expect("associated_token_program is not set"), }; let args = InitializeVaultInstructionArgs { deposit_fee_bps: self @@ -288,6 +347,10 @@ impl InitializeVaultBuilder { .clone() .expect("reward_fee_bps is not set"), decimals: self.decimals.clone().expect("decimals is not set"), + initialize_token_amount: self + .initialize_token_amount + .clone() + .expect("initialize_token_amount is not set"), }; accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) @@ -308,6 +371,10 @@ pub struct InitializeVaultCpiAccounts<'a, 'b> { pub vault_st_token_account: &'b solana_program::account_info::AccountInfo<'a>, + pub burn_vault: &'b solana_program::account_info::AccountInfo<'a>, + + pub burn_vault_vrt_token_account: &'b solana_program::account_info::AccountInfo<'a>, + pub admin: &'b solana_program::account_info::AccountInfo<'a>, pub base: &'b solana_program::account_info::AccountInfo<'a>, @@ -315,6 +382,8 @@ pub struct InitializeVaultCpiAccounts<'a, 'b> { pub system_program: &'b solana_program::account_info::AccountInfo<'a>, pub token_program: &'b solana_program::account_info::AccountInfo<'a>, + + pub associated_token_program: &'b solana_program::account_info::AccountInfo<'a>, } /// `initialize_vault` CPI instruction. @@ -334,6 +403,10 @@ pub struct InitializeVaultCpi<'a, 'b> { pub vault_st_token_account: &'b solana_program::account_info::AccountInfo<'a>, + pub burn_vault: &'b solana_program::account_info::AccountInfo<'a>, + + pub burn_vault_vrt_token_account: &'b solana_program::account_info::AccountInfo<'a>, + pub admin: &'b solana_program::account_info::AccountInfo<'a>, pub base: &'b solana_program::account_info::AccountInfo<'a>, @@ -341,6 +414,8 @@ pub struct InitializeVaultCpi<'a, 'b> { pub system_program: &'b solana_program::account_info::AccountInfo<'a>, pub token_program: &'b solana_program::account_info::AccountInfo<'a>, + + pub associated_token_program: &'b solana_program::account_info::AccountInfo<'a>, /// The arguments for the instruction. pub __args: InitializeVaultInstructionArgs, } @@ -359,10 +434,13 @@ impl<'a, 'b> InitializeVaultCpi<'a, 'b> { st_mint: accounts.st_mint, admin_st_token_account: accounts.admin_st_token_account, vault_st_token_account: accounts.vault_st_token_account, + burn_vault: accounts.burn_vault, + burn_vault_vrt_token_account: accounts.burn_vault_vrt_token_account, admin: accounts.admin, base: accounts.base, system_program: accounts.system_program, token_program: accounts.token_program, + associated_token_program: accounts.associated_token_program, __args: args, } } @@ -399,7 +477,7 @@ impl<'a, 'b> InitializeVaultCpi<'a, 'b> { bool, )], ) -> solana_program::entrypoint::ProgramResult { - let mut accounts = Vec::with_capacity(10 + remaining_accounts.len()); + let mut accounts = Vec::with_capacity(13 + remaining_accounts.len()); accounts.push(solana_program::instruction::AccountMeta::new( *self.config.key, false, @@ -424,6 +502,14 @@ impl<'a, 'b> InitializeVaultCpi<'a, 'b> { *self.vault_st_token_account.key, false, )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.burn_vault.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.burn_vault_vrt_token_account.key, + false, + )); accounts.push(solana_program::instruction::AccountMeta::new( *self.admin.key, true, @@ -440,6 +526,10 @@ impl<'a, 'b> InitializeVaultCpi<'a, 'b> { *self.token_program.key, false, )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.associated_token_program.key, + false, + )); remaining_accounts.iter().for_each(|remaining_account| { accounts.push(solana_program::instruction::AccountMeta { pubkey: *remaining_account.0.key, @@ -456,7 +546,7 @@ impl<'a, 'b> InitializeVaultCpi<'a, 'b> { accounts, data, }; - let mut account_infos = Vec::with_capacity(10 + 1 + remaining_accounts.len()); + let mut account_infos = Vec::with_capacity(13 + 1 + remaining_accounts.len()); account_infos.push(self.__program.clone()); account_infos.push(self.config.clone()); account_infos.push(self.vault.clone()); @@ -464,10 +554,13 @@ impl<'a, 'b> InitializeVaultCpi<'a, 'b> { account_infos.push(self.st_mint.clone()); account_infos.push(self.admin_st_token_account.clone()); account_infos.push(self.vault_st_token_account.clone()); + account_infos.push(self.burn_vault.clone()); + account_infos.push(self.burn_vault_vrt_token_account.clone()); account_infos.push(self.admin.clone()); account_infos.push(self.base.clone()); account_infos.push(self.system_program.clone()); account_infos.push(self.token_program.clone()); + account_infos.push(self.associated_token_program.clone()); remaining_accounts .iter() .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); @@ -490,10 +583,13 @@ impl<'a, 'b> InitializeVaultCpi<'a, 'b> { /// 3. `[]` st_mint /// 4. `[writable]` admin_st_token_account /// 5. `[writable]` vault_st_token_account -/// 6. `[writable, signer]` admin -/// 7. `[signer]` base -/// 8. `[]` system_program -/// 9. `[]` token_program +/// 6. `[]` burn_vault +/// 7. `[writable]` burn_vault_vrt_token_account +/// 8. `[writable, signer]` admin +/// 9. `[signer]` base +/// 10. `[]` system_program +/// 11. `[]` token_program +/// 12. `[]` associated_token_program #[derive(Clone, Debug)] pub struct InitializeVaultCpiBuilder<'a, 'b> { instruction: Box>, @@ -509,14 +605,18 @@ impl<'a, 'b> InitializeVaultCpiBuilder<'a, 'b> { st_mint: None, admin_st_token_account: None, vault_st_token_account: None, + burn_vault: None, + burn_vault_vrt_token_account: None, admin: None, base: None, system_program: None, token_program: None, + associated_token_program: None, deposit_fee_bps: None, withdrawal_fee_bps: None, reward_fee_bps: None, decimals: None, + initialize_token_amount: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -567,6 +667,22 @@ impl<'a, 'b> InitializeVaultCpiBuilder<'a, 'b> { self } #[inline(always)] + pub fn burn_vault( + &mut self, + burn_vault: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.burn_vault = Some(burn_vault); + self + } + #[inline(always)] + pub fn burn_vault_vrt_token_account( + &mut self, + burn_vault_vrt_token_account: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.burn_vault_vrt_token_account = Some(burn_vault_vrt_token_account); + self + } + #[inline(always)] pub fn admin(&mut self, admin: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self { self.instruction.admin = Some(admin); self @@ -593,6 +709,14 @@ impl<'a, 'b> InitializeVaultCpiBuilder<'a, 'b> { self } #[inline(always)] + pub fn associated_token_program( + &mut self, + associated_token_program: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.associated_token_program = Some(associated_token_program); + self + } + #[inline(always)] pub fn deposit_fee_bps(&mut self, deposit_fee_bps: u16) -> &mut Self { self.instruction.deposit_fee_bps = Some(deposit_fee_bps); self @@ -612,6 +736,11 @@ impl<'a, 'b> InitializeVaultCpiBuilder<'a, 'b> { self.instruction.decimals = Some(decimals); self } + #[inline(always)] + pub fn initialize_token_amount(&mut self, initialize_token_amount: u64) -> &mut Self { + self.instruction.initialize_token_amount = Some(initialize_token_amount); + self + } /// Add an additional account to the instruction. #[inline(always)] pub fn add_remaining_account( @@ -674,6 +803,11 @@ impl<'a, 'b> InitializeVaultCpiBuilder<'a, 'b> { .decimals .clone() .expect("decimals is not set"), + initialize_token_amount: self + .instruction + .initialize_token_amount + .clone() + .expect("initialize_token_amount is not set"), }; let instruction = InitializeVaultCpi { __program: self.instruction.__program, @@ -696,6 +830,13 @@ impl<'a, 'b> InitializeVaultCpiBuilder<'a, 'b> { .vault_st_token_account .expect("vault_st_token_account is not set"), + burn_vault: self.instruction.burn_vault.expect("burn_vault is not set"), + + burn_vault_vrt_token_account: self + .instruction + .burn_vault_vrt_token_account + .expect("burn_vault_vrt_token_account is not set"), + admin: self.instruction.admin.expect("admin is not set"), base: self.instruction.base.expect("base is not set"), @@ -709,6 +850,11 @@ impl<'a, 'b> InitializeVaultCpiBuilder<'a, 'b> { .instruction .token_program .expect("token_program is not set"), + + associated_token_program: self + .instruction + .associated_token_program + .expect("associated_token_program is not set"), __args: args, }; instruction.invoke_signed_with_remaining_accounts( @@ -727,14 +873,18 @@ struct InitializeVaultCpiBuilderInstruction<'a, 'b> { st_mint: Option<&'b solana_program::account_info::AccountInfo<'a>>, admin_st_token_account: Option<&'b solana_program::account_info::AccountInfo<'a>>, vault_st_token_account: Option<&'b solana_program::account_info::AccountInfo<'a>>, + burn_vault: Option<&'b solana_program::account_info::AccountInfo<'a>>, + burn_vault_vrt_token_account: Option<&'b solana_program::account_info::AccountInfo<'a>>, admin: Option<&'b solana_program::account_info::AccountInfo<'a>>, base: Option<&'b solana_program::account_info::AccountInfo<'a>>, system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, token_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, + associated_token_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, deposit_fee_bps: Option, withdrawal_fee_bps: Option, reward_fee_bps: Option, decimals: Option, + initialize_token_amount: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>,