From aed3a9c8a08aaafc2f10a39a1e1bcebffcbe9977 Mon Sep 17 00:00:00 2001 From: Evan Batsell Date: Wed, 13 Nov 2024 11:06:34 -0500 Subject: [PATCH] lock mints --- .../jito_tip_router/errors/jitoTipRouter.ts | 6 +- .../instructions/initializeWeightTable.ts | 28 ++++---- .../instructions/registerMint.ts | 26 ++++++-- .../src/generated/errors/jito_tip_router.rs | 7 +- .../instructions/initialize_weight_table.rs | 41 ++++++------ .../generated/instructions/register_mint.rs | 66 +++++++++++++++---- core/src/error.rs | 2 + core/src/instruction.rs | 13 ++-- idl/jito_tip_router.json | 12 +++- .../tests/fixtures/tip_router_client.rs | 20 ++++-- .../tip_router/admin_update_weight_table.rs | 5 +- .../tip_router/initialize_weight_table.rs | 5 +- .../tests/tip_router/register_mint.rs | 14 ++++ .../tests/tip_router/set_new_admin.rs | 2 + program/src/register_mint.rs | 24 ++++++- 15 files changed, 195 insertions(+), 76 deletions(-) diff --git a/clients/js/jito_tip_router/errors/jitoTipRouter.ts b/clients/js/jito_tip_router/errors/jitoTipRouter.ts index a4469937..1053652a 100644 --- a/clients/js/jito_tip_router/errors/jitoTipRouter.ts +++ b/clients/js/jito_tip_router/errors/jitoTipRouter.ts @@ -48,8 +48,10 @@ export const JITO_TIP_ROUTER_ERROR__CONFIG_MINTS_NOT_UPDATED = 0x2209; // 8713 export const JITO_TIP_ROUTER_ERROR__CONFIG_MINT_LIST_FULL = 0x220a; // 8714 /** TrackedMintListFull: Tracked mints are at capacity */ export const JITO_TIP_ROUTER_ERROR__TRACKED_MINT_LIST_FULL = 0x220b; // 8715 +/** TrackedMintsLocked: Tracked mints are locked for the epoch */ +export const JITO_TIP_ROUTER_ERROR__TRACKED_MINTS_LOCKED = 0x220c; // 8716 /** VaultIndexAlreadyInUse: Vault index already in use by a different mint */ -export const JITO_TIP_ROUTER_ERROR__VAULT_INDEX_ALREADY_IN_USE = 0x220c; // 8716 +export const JITO_TIP_ROUTER_ERROR__VAULT_INDEX_ALREADY_IN_USE = 0x220d; // 8717 /** FeeCapExceeded: Fee cap exceeded */ export const JITO_TIP_ROUTER_ERROR__FEE_CAP_EXCEEDED = 0x2300; // 8960 /** IncorrectNcnAdmin: Incorrect NCN Admin */ @@ -78,6 +80,7 @@ export type JitoTipRouterError = | typeof JITO_TIP_ROUTER_ERROR__NO_MINTS_IN_TABLE | typeof JITO_TIP_ROUTER_ERROR__TOO_MANY_MINTS_FOR_TABLE | typeof JITO_TIP_ROUTER_ERROR__TRACKED_MINT_LIST_FULL + | typeof JITO_TIP_ROUTER_ERROR__TRACKED_MINTS_LOCKED | typeof JITO_TIP_ROUTER_ERROR__VAULT_INDEX_ALREADY_IN_USE | typeof JITO_TIP_ROUTER_ERROR__WEIGHT_MINTS_DO_NOT_MATCH_LENGTH | typeof JITO_TIP_ROUTER_ERROR__WEIGHT_MINTS_DO_NOT_MATCH_MINT_HASH @@ -104,6 +107,7 @@ if (process.env.NODE_ENV !== 'production') { [JITO_TIP_ROUTER_ERROR__NO_MINTS_IN_TABLE]: `There are no mints in the table`, [JITO_TIP_ROUTER_ERROR__TOO_MANY_MINTS_FOR_TABLE]: `Too many mints for table`, [JITO_TIP_ROUTER_ERROR__TRACKED_MINT_LIST_FULL]: `Tracked mints are at capacity`, + [JITO_TIP_ROUTER_ERROR__TRACKED_MINTS_LOCKED]: `Tracked mints are locked for the epoch`, [JITO_TIP_ROUTER_ERROR__VAULT_INDEX_ALREADY_IN_USE]: `Vault index already in use by a different mint`, [JITO_TIP_ROUTER_ERROR__WEIGHT_MINTS_DO_NOT_MATCH_LENGTH]: `Weight mints do not match - length`, [JITO_TIP_ROUTER_ERROR__WEIGHT_MINTS_DO_NOT_MATCH_MINT_HASH]: `Weight mints do not match - mint hash`, diff --git a/clients/js/jito_tip_router/instructions/initializeWeightTable.ts b/clients/js/jito_tip_router/instructions/initializeWeightTable.ts index fb1b9041..2b1f90a2 100644 --- a/clients/js/jito_tip_router/instructions/initializeWeightTable.ts +++ b/clients/js/jito_tip_router/instructions/initializeWeightTable.ts @@ -45,7 +45,7 @@ export function getInitializeWeightTableDiscriminatorBytes() { export type InitializeWeightTableInstruction< TProgram extends string = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, TAccountRestakingConfig extends string | IAccountMeta = string, - TAccountNcnConfig extends string | IAccountMeta = string, + TAccountTrackedMints extends string | IAccountMeta = string, TAccountNcn extends string | IAccountMeta = string, TAccountWeightTable extends string | IAccountMeta = string, TAccountPayer extends string | IAccountMeta = string, @@ -61,9 +61,9 @@ export type InitializeWeightTableInstruction< TAccountRestakingConfig extends string ? ReadonlyAccount : TAccountRestakingConfig, - TAccountNcnConfig extends string - ? ReadonlyAccount - : TAccountNcnConfig, + TAccountTrackedMints extends string + ? ReadonlyAccount + : TAccountTrackedMints, TAccountNcn extends string ? ReadonlyAccount : TAccountNcn, TAccountWeightTable extends string ? WritableAccount @@ -123,7 +123,7 @@ export function getInitializeWeightTableInstructionDataCodec(): Codec< export type InitializeWeightTableInput< TAccountRestakingConfig extends string = string, - TAccountNcnConfig extends string = string, + TAccountTrackedMints extends string = string, TAccountNcn extends string = string, TAccountWeightTable extends string = string, TAccountPayer extends string = string, @@ -131,7 +131,7 @@ export type InitializeWeightTableInput< TAccountSystemProgram extends string = string, > = { restakingConfig: Address; - ncnConfig: Address; + trackedMints: Address; ncn: Address; weightTable: Address; payer: TransactionSigner; @@ -142,7 +142,7 @@ export type InitializeWeightTableInput< export function getInitializeWeightTableInstruction< TAccountRestakingConfig extends string, - TAccountNcnConfig extends string, + TAccountTrackedMints extends string, TAccountNcn extends string, TAccountWeightTable extends string, TAccountPayer extends string, @@ -152,7 +152,7 @@ export function getInitializeWeightTableInstruction< >( input: InitializeWeightTableInput< TAccountRestakingConfig, - TAccountNcnConfig, + TAccountTrackedMints, TAccountNcn, TAccountWeightTable, TAccountPayer, @@ -163,7 +163,7 @@ export function getInitializeWeightTableInstruction< ): InitializeWeightTableInstruction< TProgramAddress, TAccountRestakingConfig, - TAccountNcnConfig, + TAccountTrackedMints, TAccountNcn, TAccountWeightTable, TAccountPayer, @@ -180,7 +180,7 @@ export function getInitializeWeightTableInstruction< value: input.restakingConfig ?? null, isWritable: false, }, - ncnConfig: { value: input.ncnConfig ?? null, isWritable: false }, + trackedMints: { value: input.trackedMints ?? null, isWritable: false }, ncn: { value: input.ncn ?? null, isWritable: false }, weightTable: { value: input.weightTable ?? null, isWritable: true }, payer: { value: input.payer ?? null, isWritable: true }, @@ -208,7 +208,7 @@ export function getInitializeWeightTableInstruction< const instruction = { accounts: [ getAccountMeta(accounts.restakingConfig), - getAccountMeta(accounts.ncnConfig), + getAccountMeta(accounts.trackedMints), getAccountMeta(accounts.ncn), getAccountMeta(accounts.weightTable), getAccountMeta(accounts.payer), @@ -222,7 +222,7 @@ export function getInitializeWeightTableInstruction< } as InitializeWeightTableInstruction< TProgramAddress, TAccountRestakingConfig, - TAccountNcnConfig, + TAccountTrackedMints, TAccountNcn, TAccountWeightTable, TAccountPayer, @@ -240,7 +240,7 @@ export type ParsedInitializeWeightTableInstruction< programAddress: Address; accounts: { restakingConfig: TAccountMetas[0]; - ncnConfig: TAccountMetas[1]; + trackedMints: TAccountMetas[1]; ncn: TAccountMetas[2]; weightTable: TAccountMetas[3]; payer: TAccountMetas[4]; @@ -272,7 +272,7 @@ export function parseInitializeWeightTableInstruction< programAddress: instruction.programAddress, accounts: { restakingConfig: getNextAccount(), - ncnConfig: getNextAccount(), + trackedMints: getNextAccount(), ncn: getNextAccount(), weightTable: getNextAccount(), payer: getNextAccount(), diff --git a/clients/js/jito_tip_router/instructions/registerMint.ts b/clients/js/jito_tip_router/instructions/registerMint.ts index 91641547..c4bd83c2 100644 --- a/clients/js/jito_tip_router/instructions/registerMint.ts +++ b/clients/js/jito_tip_router/instructions/registerMint.ts @@ -38,6 +38,7 @@ export type RegisterMintInstruction< TAccountRestakingConfig extends string | IAccountMeta = string, TAccountTrackedMints extends string | IAccountMeta = string, TAccountNcn extends string | IAccountMeta = string, + TAccountWeightTable extends string | IAccountMeta = string, TAccountVault extends string | IAccountMeta = string, TAccountVaultNcnTicket extends string | IAccountMeta = string, TAccountNcnVaultTicket extends string | IAccountMeta = string, @@ -55,6 +56,9 @@ export type RegisterMintInstruction< ? WritableAccount : TAccountTrackedMints, TAccountNcn extends string ? ReadonlyAccount : TAccountNcn, + TAccountWeightTable extends string + ? ReadonlyAccount + : TAccountWeightTable, TAccountVault extends string ? ReadonlyAccount : TAccountVault, @@ -103,6 +107,7 @@ export type RegisterMintInput< TAccountRestakingConfig extends string = string, TAccountTrackedMints extends string = string, TAccountNcn extends string = string, + TAccountWeightTable extends string = string, TAccountVault extends string = string, TAccountVaultNcnTicket extends string = string, TAccountNcnVaultTicket extends string = string, @@ -112,6 +117,7 @@ export type RegisterMintInput< restakingConfig: Address; trackedMints: Address; ncn: Address; + weightTable: Address; vault: Address; vaultNcnTicket: Address; ncnVaultTicket: Address; @@ -123,6 +129,7 @@ export function getRegisterMintInstruction< TAccountRestakingConfig extends string, TAccountTrackedMints extends string, TAccountNcn extends string, + TAccountWeightTable extends string, TAccountVault extends string, TAccountVaultNcnTicket extends string, TAccountNcnVaultTicket extends string, @@ -134,6 +141,7 @@ export function getRegisterMintInstruction< TAccountRestakingConfig, TAccountTrackedMints, TAccountNcn, + TAccountWeightTable, TAccountVault, TAccountVaultNcnTicket, TAccountNcnVaultTicket, @@ -146,6 +154,7 @@ export function getRegisterMintInstruction< TAccountRestakingConfig, TAccountTrackedMints, TAccountNcn, + TAccountWeightTable, TAccountVault, TAccountVaultNcnTicket, TAccountNcnVaultTicket, @@ -164,6 +173,7 @@ export function getRegisterMintInstruction< }, trackedMints: { value: input.trackedMints ?? null, isWritable: true }, ncn: { value: input.ncn ?? null, isWritable: false }, + weightTable: { value: input.weightTable ?? null, isWritable: false }, vault: { value: input.vault ?? null, isWritable: false }, vaultNcnTicket: { value: input.vaultNcnTicket ?? null, isWritable: false }, ncnVaultTicket: { value: input.ncnVaultTicket ?? null, isWritable: false }, @@ -184,6 +194,7 @@ export function getRegisterMintInstruction< getAccountMeta(accounts.restakingConfig), getAccountMeta(accounts.trackedMints), getAccountMeta(accounts.ncn), + getAccountMeta(accounts.weightTable), getAccountMeta(accounts.vault), getAccountMeta(accounts.vaultNcnTicket), getAccountMeta(accounts.ncnVaultTicket), @@ -197,6 +208,7 @@ export function getRegisterMintInstruction< TAccountRestakingConfig, TAccountTrackedMints, TAccountNcn, + TAccountWeightTable, TAccountVault, TAccountVaultNcnTicket, TAccountNcnVaultTicket, @@ -216,11 +228,12 @@ export type ParsedRegisterMintInstruction< restakingConfig: TAccountMetas[0]; trackedMints: TAccountMetas[1]; ncn: TAccountMetas[2]; - vault: TAccountMetas[3]; - vaultNcnTicket: TAccountMetas[4]; - ncnVaultTicket: TAccountMetas[5]; - restakingProgramId: TAccountMetas[6]; - vaultProgramId: TAccountMetas[7]; + weightTable: TAccountMetas[3]; + vault: TAccountMetas[4]; + vaultNcnTicket: TAccountMetas[5]; + ncnVaultTicket: TAccountMetas[6]; + restakingProgramId: TAccountMetas[7]; + vaultProgramId: TAccountMetas[8]; }; data: RegisterMintInstructionData; }; @@ -233,7 +246,7 @@ export function parseRegisterMintInstruction< IInstructionWithAccounts & IInstructionWithData ): ParsedRegisterMintInstruction { - if (instruction.accounts.length < 8) { + if (instruction.accounts.length < 9) { // TODO: Coded error. throw new Error('Not enough accounts'); } @@ -249,6 +262,7 @@ export function parseRegisterMintInstruction< restakingConfig: getNextAccount(), trackedMints: getNextAccount(), ncn: getNextAccount(), + weightTable: getNextAccount(), vault: getNextAccount(), vaultNcnTicket: getNextAccount(), ncnVaultTicket: getNextAccount(), diff --git a/clients/rust/jito_tip_router/src/generated/errors/jito_tip_router.rs b/clients/rust/jito_tip_router/src/generated/errors/jito_tip_router.rs index 3a3792c2..ddc3395d 100644 --- a/clients/rust/jito_tip_router/src/generated/errors/jito_tip_router.rs +++ b/clients/rust/jito_tip_router/src/generated/errors/jito_tip_router.rs @@ -60,9 +60,12 @@ pub enum JitoTipRouterError { /// 8715 - Tracked mints are at capacity #[error("Tracked mints are at capacity")] TrackedMintListFull = 0x220B, - /// 8716 - Vault index already in use by a different mint + /// 8716 - Tracked mints are locked for the epoch + #[error("Tracked mints are locked for the epoch")] + TrackedMintsLocked = 0x220C, + /// 8717 - Vault index already in use by a different mint #[error("Vault index already in use by a different mint")] - VaultIndexAlreadyInUse = 0x220C, + VaultIndexAlreadyInUse = 0x220D, /// 8960 - Fee cap exceeded #[error("Fee cap exceeded")] FeeCapExceeded = 0x2300, diff --git a/clients/rust/jito_tip_router/src/generated/instructions/initialize_weight_table.rs b/clients/rust/jito_tip_router/src/generated/instructions/initialize_weight_table.rs index 1c703fcc..b46b10c3 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/initialize_weight_table.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/initialize_weight_table.rs @@ -10,7 +10,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; pub struct InitializeWeightTable { pub restaking_config: solana_program::pubkey::Pubkey, - pub ncn_config: solana_program::pubkey::Pubkey, + pub tracked_mints: solana_program::pubkey::Pubkey, pub ncn: solana_program::pubkey::Pubkey, @@ -42,7 +42,7 @@ impl InitializeWeightTable { false, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.ncn_config, + self.tracked_mints, false, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( @@ -106,7 +106,7 @@ pub struct InitializeWeightTableInstructionArgs { /// ### Accounts: /// /// 0. `[]` restaking_config -/// 1. `[]` ncn_config +/// 1. `[]` tracked_mints /// 2. `[]` ncn /// 3. `[writable]` weight_table /// 4. `[writable, signer]` payer @@ -115,7 +115,7 @@ pub struct InitializeWeightTableInstructionArgs { #[derive(Clone, Debug, Default)] pub struct InitializeWeightTableBuilder { restaking_config: Option, - ncn_config: Option, + tracked_mints: Option, ncn: Option, weight_table: Option, payer: Option, @@ -138,8 +138,8 @@ impl InitializeWeightTableBuilder { self } #[inline(always)] - pub fn ncn_config(&mut self, ncn_config: solana_program::pubkey::Pubkey) -> &mut Self { - self.ncn_config = Some(ncn_config); + pub fn tracked_mints(&mut self, tracked_mints: solana_program::pubkey::Pubkey) -> &mut Self { + self.tracked_mints = Some(tracked_mints); self } #[inline(always)] @@ -199,7 +199,7 @@ impl InitializeWeightTableBuilder { pub fn instruction(&self) -> solana_program::instruction::Instruction { let accounts = InitializeWeightTable { restaking_config: self.restaking_config.expect("restaking_config is not set"), - ncn_config: self.ncn_config.expect("ncn_config is not set"), + tracked_mints: self.tracked_mints.expect("tracked_mints is not set"), ncn: self.ncn.expect("ncn is not set"), weight_table: self.weight_table.expect("weight_table is not set"), payer: self.payer.expect("payer is not set"), @@ -222,7 +222,7 @@ impl InitializeWeightTableBuilder { pub struct InitializeWeightTableCpiAccounts<'a, 'b> { pub restaking_config: &'b solana_program::account_info::AccountInfo<'a>, - pub ncn_config: &'b solana_program::account_info::AccountInfo<'a>, + pub tracked_mints: &'b solana_program::account_info::AccountInfo<'a>, pub ncn: &'b solana_program::account_info::AccountInfo<'a>, @@ -242,7 +242,7 @@ pub struct InitializeWeightTableCpi<'a, 'b> { pub restaking_config: &'b solana_program::account_info::AccountInfo<'a>, - pub ncn_config: &'b solana_program::account_info::AccountInfo<'a>, + pub tracked_mints: &'b solana_program::account_info::AccountInfo<'a>, pub ncn: &'b solana_program::account_info::AccountInfo<'a>, @@ -266,7 +266,7 @@ impl<'a, 'b> InitializeWeightTableCpi<'a, 'b> { Self { __program: program, restaking_config: accounts.restaking_config, - ncn_config: accounts.ncn_config, + tracked_mints: accounts.tracked_mints, ncn: accounts.ncn, weight_table: accounts.weight_table, payer: accounts.payer, @@ -314,7 +314,7 @@ impl<'a, 'b> InitializeWeightTableCpi<'a, 'b> { false, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.ncn_config.key, + *self.tracked_mints.key, false, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( @@ -358,7 +358,7 @@ impl<'a, 'b> InitializeWeightTableCpi<'a, 'b> { let mut account_infos = Vec::with_capacity(7 + 1 + remaining_accounts.len()); account_infos.push(self.__program.clone()); account_infos.push(self.restaking_config.clone()); - account_infos.push(self.ncn_config.clone()); + account_infos.push(self.tracked_mints.clone()); account_infos.push(self.ncn.clone()); account_infos.push(self.weight_table.clone()); account_infos.push(self.payer.clone()); @@ -381,7 +381,7 @@ impl<'a, 'b> InitializeWeightTableCpi<'a, 'b> { /// ### Accounts: /// /// 0. `[]` restaking_config -/// 1. `[]` ncn_config +/// 1. `[]` tracked_mints /// 2. `[]` ncn /// 3. `[writable]` weight_table /// 4. `[writable, signer]` payer @@ -397,7 +397,7 @@ impl<'a, 'b> InitializeWeightTableCpiBuilder<'a, 'b> { let instruction = Box::new(InitializeWeightTableCpiBuilderInstruction { __program: program, restaking_config: None, - ncn_config: None, + tracked_mints: None, ncn: None, weight_table: None, payer: None, @@ -417,11 +417,11 @@ impl<'a, 'b> InitializeWeightTableCpiBuilder<'a, 'b> { self } #[inline(always)] - pub fn ncn_config( + pub fn tracked_mints( &mut self, - ncn_config: &'b solana_program::account_info::AccountInfo<'a>, + tracked_mints: &'b solana_program::account_info::AccountInfo<'a>, ) -> &mut Self { - self.instruction.ncn_config = Some(ncn_config); + self.instruction.tracked_mints = Some(tracked_mints); self } #[inline(always)] @@ -516,7 +516,10 @@ impl<'a, 'b> InitializeWeightTableCpiBuilder<'a, 'b> { .restaking_config .expect("restaking_config is not set"), - ncn_config: self.instruction.ncn_config.expect("ncn_config is not set"), + tracked_mints: self + .instruction + .tracked_mints + .expect("tracked_mints is not set"), ncn: self.instruction.ncn.expect("ncn is not set"), @@ -549,7 +552,7 @@ impl<'a, 'b> InitializeWeightTableCpiBuilder<'a, 'b> { struct InitializeWeightTableCpiBuilderInstruction<'a, 'b> { __program: &'b solana_program::account_info::AccountInfo<'a>, restaking_config: Option<&'b solana_program::account_info::AccountInfo<'a>>, - ncn_config: Option<&'b solana_program::account_info::AccountInfo<'a>>, + tracked_mints: Option<&'b solana_program::account_info::AccountInfo<'a>>, ncn: Option<&'b solana_program::account_info::AccountInfo<'a>>, weight_table: Option<&'b solana_program::account_info::AccountInfo<'a>>, payer: Option<&'b solana_program::account_info::AccountInfo<'a>>, diff --git a/clients/rust/jito_tip_router/src/generated/instructions/register_mint.rs b/clients/rust/jito_tip_router/src/generated/instructions/register_mint.rs index 667a722e..ed3496ce 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/register_mint.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/register_mint.rs @@ -14,6 +14,8 @@ pub struct RegisterMint { pub ncn: solana_program::pubkey::Pubkey, + pub weight_table: solana_program::pubkey::Pubkey, + pub vault: solana_program::pubkey::Pubkey, pub vault_ncn_ticket: solana_program::pubkey::Pubkey, @@ -34,7 +36,7 @@ impl RegisterMint { &self, 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(9 + remaining_accounts.len()); accounts.push(solana_program::instruction::AccountMeta::new_readonly( self.restaking_config, false, @@ -46,6 +48,10 @@ impl RegisterMint { accounts.push(solana_program::instruction::AccountMeta::new_readonly( self.ncn, false, )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.weight_table, + false, + )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( self.vault, false, )); @@ -100,16 +106,18 @@ impl Default for RegisterMintInstructionData { /// 0. `[]` restaking_config /// 1. `[writable]` tracked_mints /// 2. `[]` ncn -/// 3. `[]` vault -/// 4. `[]` vault_ncn_ticket -/// 5. `[]` ncn_vault_ticket -/// 6. `[]` restaking_program_id -/// 7. `[]` vault_program_id +/// 3. `[]` weight_table +/// 4. `[]` vault +/// 5. `[]` vault_ncn_ticket +/// 6. `[]` ncn_vault_ticket +/// 7. `[]` restaking_program_id +/// 8. `[]` vault_program_id #[derive(Clone, Debug, Default)] pub struct RegisterMintBuilder { restaking_config: Option, tracked_mints: Option, ncn: Option, + weight_table: Option, vault: Option, vault_ncn_ticket: Option, ncn_vault_ticket: Option, @@ -141,6 +149,11 @@ impl RegisterMintBuilder { self } #[inline(always)] + pub fn weight_table(&mut self, weight_table: solana_program::pubkey::Pubkey) -> &mut Self { + self.weight_table = Some(weight_table); + self + } + #[inline(always)] pub fn vault(&mut self, vault: solana_program::pubkey::Pubkey) -> &mut Self { self.vault = Some(vault); self @@ -201,6 +214,7 @@ impl RegisterMintBuilder { restaking_config: self.restaking_config.expect("restaking_config is not set"), tracked_mints: self.tracked_mints.expect("tracked_mints is not set"), ncn: self.ncn.expect("ncn is not set"), + weight_table: self.weight_table.expect("weight_table is not set"), vault: self.vault.expect("vault is not set"), vault_ncn_ticket: self.vault_ncn_ticket.expect("vault_ncn_ticket is not set"), ncn_vault_ticket: self.ncn_vault_ticket.expect("ncn_vault_ticket is not set"), @@ -222,6 +236,8 @@ pub struct RegisterMintCpiAccounts<'a, 'b> { pub ncn: &'b solana_program::account_info::AccountInfo<'a>, + pub weight_table: &'b solana_program::account_info::AccountInfo<'a>, + pub vault: &'b solana_program::account_info::AccountInfo<'a>, pub vault_ncn_ticket: &'b solana_program::account_info::AccountInfo<'a>, @@ -244,6 +260,8 @@ pub struct RegisterMintCpi<'a, 'b> { pub ncn: &'b solana_program::account_info::AccountInfo<'a>, + pub weight_table: &'b solana_program::account_info::AccountInfo<'a>, + pub vault: &'b solana_program::account_info::AccountInfo<'a>, pub vault_ncn_ticket: &'b solana_program::account_info::AccountInfo<'a>, @@ -265,6 +283,7 @@ impl<'a, 'b> RegisterMintCpi<'a, 'b> { restaking_config: accounts.restaking_config, tracked_mints: accounts.tracked_mints, ncn: accounts.ncn, + weight_table: accounts.weight_table, vault: accounts.vault, vault_ncn_ticket: accounts.vault_ncn_ticket, ncn_vault_ticket: accounts.ncn_vault_ticket, @@ -305,7 +324,7 @@ impl<'a, 'b> RegisterMintCpi<'a, 'b> { bool, )], ) -> solana_program::entrypoint::ProgramResult { - let mut accounts = Vec::with_capacity(8 + remaining_accounts.len()); + let mut accounts = Vec::with_capacity(9 + remaining_accounts.len()); accounts.push(solana_program::instruction::AccountMeta::new_readonly( *self.restaking_config.key, false, @@ -318,6 +337,10 @@ impl<'a, 'b> RegisterMintCpi<'a, 'b> { *self.ncn.key, false, )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.weight_table.key, + false, + )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( *self.vault.key, false, @@ -352,11 +375,12 @@ impl<'a, 'b> RegisterMintCpi<'a, 'b> { accounts, data, }; - let mut account_infos = Vec::with_capacity(8 + 1 + remaining_accounts.len()); + let mut account_infos = Vec::with_capacity(9 + 1 + remaining_accounts.len()); account_infos.push(self.__program.clone()); account_infos.push(self.restaking_config.clone()); account_infos.push(self.tracked_mints.clone()); account_infos.push(self.ncn.clone()); + account_infos.push(self.weight_table.clone()); account_infos.push(self.vault.clone()); account_infos.push(self.vault_ncn_ticket.clone()); account_infos.push(self.ncn_vault_ticket.clone()); @@ -381,11 +405,12 @@ impl<'a, 'b> RegisterMintCpi<'a, 'b> { /// 0. `[]` restaking_config /// 1. `[writable]` tracked_mints /// 2. `[]` ncn -/// 3. `[]` vault -/// 4. `[]` vault_ncn_ticket -/// 5. `[]` ncn_vault_ticket -/// 6. `[]` restaking_program_id -/// 7. `[]` vault_program_id +/// 3. `[]` weight_table +/// 4. `[]` vault +/// 5. `[]` vault_ncn_ticket +/// 6. `[]` ncn_vault_ticket +/// 7. `[]` restaking_program_id +/// 8. `[]` vault_program_id #[derive(Clone, Debug)] pub struct RegisterMintCpiBuilder<'a, 'b> { instruction: Box>, @@ -398,6 +423,7 @@ impl<'a, 'b> RegisterMintCpiBuilder<'a, 'b> { restaking_config: None, tracked_mints: None, ncn: None, + weight_table: None, vault: None, vault_ncn_ticket: None, ncn_vault_ticket: None, @@ -429,6 +455,14 @@ impl<'a, 'b> RegisterMintCpiBuilder<'a, 'b> { self } #[inline(always)] + pub fn weight_table( + &mut self, + weight_table: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.weight_table = Some(weight_table); + self + } + #[inline(always)] pub fn vault(&mut self, vault: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self { self.instruction.vault = Some(vault); self @@ -521,6 +555,11 @@ impl<'a, 'b> RegisterMintCpiBuilder<'a, 'b> { ncn: self.instruction.ncn.expect("ncn is not set"), + weight_table: self + .instruction + .weight_table + .expect("weight_table is not set"), + vault: self.instruction.vault.expect("vault is not set"), vault_ncn_ticket: self @@ -556,6 +595,7 @@ struct RegisterMintCpiBuilderInstruction<'a, 'b> { restaking_config: Option<&'b solana_program::account_info::AccountInfo<'a>>, tracked_mints: Option<&'b solana_program::account_info::AccountInfo<'a>>, ncn: Option<&'b solana_program::account_info::AccountInfo<'a>>, + weight_table: Option<&'b solana_program::account_info::AccountInfo<'a>>, vault: Option<&'b solana_program::account_info::AccountInfo<'a>>, vault_ncn_ticket: Option<&'b solana_program::account_info::AccountInfo<'a>>, ncn_vault_ticket: Option<&'b solana_program::account_info::AccountInfo<'a>>, diff --git a/core/src/error.rs b/core/src/error.rs index 39958c24..49ab208b 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -38,6 +38,8 @@ pub enum TipRouterError { ConfigMintListFull, #[error("Tracked mints are at capacity")] TrackedMintListFull, + #[error("Tracked mints are locked for the epoch")] + TrackedMintsLocked, #[error("Vault index already in use by a different mint")] VaultIndexAlreadyInUse, #[error("Fee cap exceeded")] diff --git a/core/src/instruction.rs b/core/src/instruction.rs index 76d2f65c..01215a46 100644 --- a/core/src/instruction.rs +++ b/core/src/instruction.rs @@ -53,7 +53,7 @@ pub enum WeightTableInstruction { /// Initializes the weight table for a given NCN epoch #[account(0, name = "restaking_config")] - #[account(1, name = "ncn_config")] + #[account(1, name = "tracked_mints")] #[account(2, name = "ncn")] #[account(3, writable, name = "weight_table")] #[account(4, writable, signer, name = "payer")] @@ -78,11 +78,12 @@ pub enum WeightTableInstruction { #[account(0, name = "restaking_config")] #[account(1, writable, name = "tracked_mints")] #[account(2, name = "ncn")] - #[account(3, name = "vault")] - #[account(4, name = "vault_ncn_ticket")] - #[account(5, name = "ncn_vault_ticket")] - #[account(6, name = "restaking_program_id")] - #[account(7, name = "vault_program_id")] + #[account(3, name = "weight_table")] + #[account(4, name = "vault")] + #[account(5, name = "vault_ncn_ticket")] + #[account(6, name = "ncn_vault_ticket")] + #[account(7, name = "restaking_program_id")] + #[account(8, name = "vault_program_id")] RegisterMint, /// Initializes the tracked mints account for an NCN diff --git a/idl/jito_tip_router.json b/idl/jito_tip_router.json index 0a0d60ce..77bbc073 100644 --- a/idl/jito_tip_router.json +++ b/idl/jito_tip_router.json @@ -176,7 +176,7 @@ "isSigner": false }, { - "name": "ncnConfig", + "name": "trackedMints", "isMut": false, "isSigner": false }, @@ -281,6 +281,11 @@ "isMut": false, "isSigner": false }, + { + "name": "weightTable", + "isMut": false, + "isSigner": false + }, { "name": "vault", "isMut": false, @@ -691,6 +696,11 @@ }, { "code": 8716, + "name": "TrackedMintsLocked", + "msg": "Tracked mints are locked for the epoch" + }, + { + "code": 8717, "name": "VaultIndexAlreadyInUse", "msg": "Vault index already in use by a different mint" }, diff --git a/integration_tests/tests/fixtures/tip_router_client.rs b/integration_tests/tests/fixtures/tip_router_client.rs index 31531ec8..b6ffb944 100644 --- a/integration_tests/tests/fixtures/tip_router_client.rs +++ b/integration_tests/tests/fixtures/tip_router_client.rs @@ -18,6 +18,7 @@ use solana_program::{ }; use solana_program_test::BanksClient; use solana_sdk::{ + clock::Clock, commitment_config::CommitmentLevel, signature::{Keypair, Signer}, system_program, @@ -262,13 +263,14 @@ impl TipRouterClient { let restaking_config_account = self.get_restaking_config().await?; let ncn_epoch = current_slot / restaking_config_account.epoch_length(); - let config_pda = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn).0; + let tracked_mints_pda = + TrackedMints::find_program_address(&jito_tip_router_program::id(), &ncn).0; let weight_table = WeightTable::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch).0; let ix = InitializeWeightTableBuilder::new() .restaking_config(restaking_config) - .ncn_config(config_pda) + .tracked_mints(tracked_mints_pda) .ncn(ncn) .weight_table(weight_table) .payer(self.payer.pubkey()) @@ -369,14 +371,22 @@ impl TipRouterClient { vault_ncn_ticket: Pubkey, ncn_vault_ticket: Pubkey, ) -> TestResult<()> { - let restaking_config = Config::find_program_address(&jito_restaking_program::id()).0; + let restaking_config_address = + Config::find_program_address(&jito_restaking_program::id()).0; let tracked_mints = TrackedMints::find_program_address(&jito_tip_router_program::id(), &ncn).0; + let restaking_config = self.get_restaking_config().await?; + let current_slot = self.banks_client.get_sysvar::().await?.slot; + let ncn_epoch = current_slot / restaking_config.epoch_length(); + let weight_table = + WeightTable::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch).0; + self.register_mint( - restaking_config, + restaking_config_address, tracked_mints, ncn, + weight_table, vault, vault_ncn_ticket, ncn_vault_ticket, @@ -389,6 +399,7 @@ impl TipRouterClient { restaking_config: Pubkey, tracked_mints: Pubkey, ncn: Pubkey, + weight_table: Pubkey, vault: Pubkey, vault_ncn_ticket: Pubkey, ncn_vault_ticket: Pubkey, @@ -397,6 +408,7 @@ impl TipRouterClient { .restaking_config(restaking_config) .tracked_mints(tracked_mints) .ncn(ncn) + .weight_table(weight_table) .vault(vault) .vault_ncn_ticket(vault_ncn_ticket) .ncn_vault_ticket(ncn_vault_ticket) diff --git a/integration_tests/tests/tip_router/admin_update_weight_table.rs b/integration_tests/tests/tip_router/admin_update_weight_table.rs index dc912133..adc7d323 100644 --- a/integration_tests/tests/tip_router/admin_update_weight_table.rs +++ b/integration_tests/tests/tip_router/admin_update_weight_table.rs @@ -15,10 +15,7 @@ mod tests { let slot = fixture.clock().await.slot; - //TODO fix when config has mints - tip_router_client - .do_initialize_config(ncn_root.ncn_pubkey, &ncn_root.ncn_admin) - .await?; + tip_router_client.setup_tip_router(&ncn_root).await?; tip_router_client .do_initialize_weight_table(ncn_root.ncn_pubkey, slot) diff --git a/integration_tests/tests/tip_router/initialize_weight_table.rs b/integration_tests/tests/tip_router/initialize_weight_table.rs index 86c747f0..3d70c7eb 100644 --- a/integration_tests/tests/tip_router/initialize_weight_table.rs +++ b/integration_tests/tests/tip_router/initialize_weight_table.rs @@ -13,10 +13,7 @@ mod tests { let slot = fixture.clock().await.slot; - //TODO fix when config has mints - tip_router_client - .do_initialize_config(ncn_root.ncn_pubkey, &ncn_root.ncn_admin) - .await?; + tip_router_client.setup_tip_router(&ncn_root).await?; tip_router_client .do_initialize_weight_table(ncn_root.ncn_pubkey, slot) diff --git a/integration_tests/tests/tip_router/register_mint.rs b/integration_tests/tests/tip_router/register_mint.rs index 04b1e274..27c6c208 100644 --- a/integration_tests/tests/tip_router/register_mint.rs +++ b/integration_tests/tests/tip_router/register_mint.rs @@ -104,4 +104,18 @@ mod tests { Ok(()) } + + #[tokio::test] + async fn test_register_mint_fails_with_weight_table() -> TestResult<()> { + let mut fixture = TestBuilder::new().await; + let mut tip_router_client = fixture.tip_router_client(); + let ncn_root = fixture.setup_ncn().await?; + + tip_router_client.setup_tip_router(&ncn_root).await?; + + // TODO create ncn and vault with 1 mint, register mint, initialize weight table + // TODO verify weight table locks register_mint + + Ok(()) + } } diff --git a/integration_tests/tests/tip_router/set_new_admin.rs b/integration_tests/tests/tip_router/set_new_admin.rs index 8f56adcd..af6801c0 100644 --- a/integration_tests/tests/tip_router/set_new_admin.rs +++ b/integration_tests/tests/tip_router/set_new_admin.rs @@ -29,6 +29,8 @@ mod tests { .await?; assert_eq!(config.fee_admin, new_fee_admin); + fixture.warp_slot_incremental(1).await?; + let new_tie_breaker = Pubkey::new_unique(); tip_router_client .do_set_new_admin(ConfigAdminRole::TieBreakerAdmin, new_tie_breaker, &ncn_root) diff --git a/program/src/register_mint.rs b/program/src/register_mint.rs index 1818f5c0..8fe2b0ed 100644 --- a/program/src/register_mint.rs +++ b/program/src/register_mint.rs @@ -1,6 +1,9 @@ use jito_bytemuck::AccountDeserialize; +use jito_jsm_core::loader::load_system_account; use jito_restaking_core::{config::Config, ncn::Ncn, ncn_vault_ticket::NcnVaultTicket}; -use jito_tip_router_core::tracked_mints::TrackedMints; +use jito_tip_router_core::{ + error::TipRouterError, tracked_mints::TrackedMints, weight_table::WeightTable, +}; use jito_vault_core::{vault::Vault, vault_ncn_ticket::VaultNcnTicket}; use solana_program::{ account_info::AccountInfo, @@ -8,11 +11,12 @@ use solana_program::{ msg, program_error::ProgramError, pubkey::Pubkey, + system_program, sysvar::{clock::Clock, Sysvar}, }; pub fn process_register_mint(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { - let [restaking_config, tracked_mints, ncn, vault, vault_ncn_ticket, ncn_vault_ticket, restaking_program_id, vault_program_id] = + let [restaking_config, tracked_mints, ncn, weight_table, vault, vault_ncn_ticket, ncn_vault_ticket, restaking_program_id, vault_program_id] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); @@ -37,6 +41,22 @@ pub fn process_register_mint(program_id: &Pubkey, accounts: &[AccountInfo]) -> P let slot = Clock::get()?.slot; + let ncn_epoch = slot + .checked_div(epoch_length) + .ok_or(TipRouterError::DenominatorIsZero)?; + + // Once tracked_mints.mint_count() == ncn.vault_count, the weight table can be initialized + // Once the weight table is initialized, you can't add any more mints + if weight_table.owner.eq(&system_program::ID) { + load_system_account(weight_table, false)?; + } else if weight_table.owner.eq(program_id) { + WeightTable::load(program_id, weight_table, ncn, ncn_epoch, false)?; + return Err(TipRouterError::TrackedMintsLocked.into()); + } else { + msg!("Weight table account is not owned by this program or the system program"); + return Err(ProgramError::InvalidAccountOwner); + } + // Verify tickets are active let vault_ncn_ticket_data = vault_ncn_ticket.data.borrow(); let vault_ncn_ticket = VaultNcnTicket::try_from_slice_unchecked(&vault_ncn_ticket_data)?;