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/cli/src/vault_handler.rs b/cli/src/vault_handler.rs index 015d0ebf..331e9468 100644 --- a/cli/src/vault_handler.rs +++ b/cli/src/vault_handler.rs @@ -246,7 +246,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/errors/jitoVault.ts b/clients/js/vault_client/errors/jitoVault.ts index 0eb12ee9..5ecee29f 100644 --- a/clients/js/vault_client/errors/jitoVault.ts +++ b/clients/js/vault_client/errors/jitoVault.ts @@ -16,120 +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 = 0x422; // 1058 /** 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 = 0x423; // 1059 /** ArithmeticOverflow: ArithmeticOverflow */ export const JITO_VAULT_ERROR__ARITHMETIC_OVERFLOW = 0xbb8; // 3000 /** ArithmeticUnderflow: ArithmeticUnderflow */ @@ -169,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 @@ -198,7 +203,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') { @@ -234,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`, @@ -264,6 +271,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/js/vault_client/instructions/initializeVault.ts b/clients/js/vault_client/instructions/initializeVault.ts index 9e389cf9..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, @@ -44,7 +46,13 @@ 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, + TAccountBurnVault extends string | IAccountMeta = string, + TAccountBurnVaultVrtTokenAccount extends + | string + | IAccountMeta = string, TAccountAdmin extends string | IAccountMeta = string, TAccountBase extends string | IAccountMeta = string, TAccountSystemProgram extends @@ -53,6 +61,7 @@ export type InitializeVaultInstruction< TAccountTokenProgram extends | string | IAccountMeta = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', + TAccountAssociatedTokenProgram extends string | IAccountMeta = string, TRemainingAccounts extends readonly IAccountMeta[] = [], > = IInstruction & IInstructionWithData & @@ -68,9 +77,21 @@ 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, + TAccountBurnVault extends string + ? ReadonlyAccount + : TAccountBurnVault, + TAccountBurnVaultVrtTokenAccount extends string + ? WritableAccount + : TAccountBurnVaultVrtTokenAccount, TAccountAdmin extends string ? WritableSignerAccount & IAccountSignerMeta @@ -84,6 +105,9 @@ export type InitializeVaultInstruction< TAccountTokenProgram extends string ? ReadonlyAccount : TAccountTokenProgram, + TAccountAssociatedTokenProgram extends string + ? ReadonlyAccount + : TAccountAssociatedTokenProgram, ...TRemainingAccounts, ] >; @@ -94,6 +118,7 @@ export type InitializeVaultInstructionData = { withdrawalFeeBps: number; rewardFeeBps: number; decimals: number; + initializeTokenAmount: bigint; }; export type InitializeVaultInstructionDataArgs = { @@ -101,6 +126,7 @@ export type InitializeVaultInstructionDataArgs = { withdrawalFeeBps: number; rewardFeeBps: number; decimals: number; + initializeTokenAmount: number | bigint; }; export function getInitializeVaultInstructionDataEncoder(): Encoder { @@ -111,6 +137,7 @@ export function getInitializeVaultInstructionDataEncoder(): Encoder ({ ...value, discriminator: INITIALIZE_VAULT_DISCRIMINATOR }) ); @@ -123,6 +150,7 @@ export function getInitializeVaultInstructionDataDecoder(): Decoder = { config: Address; vault: Address; vrtMint: TransactionSigner; - tokenMint: Address; + 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< TAccountConfig extends string, TAccountVault extends string, TAccountVrtMint extends string, - TAccountTokenMint extends string, + 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, TAccountVault, TAccountVrtMint, - TAccountTokenMint, + TAccountStMint, + TAccountAdminStTokenAccount, + TAccountVaultStTokenAccount, + TAccountBurnVault, + TAccountBurnVaultVrtTokenAccount, TAccountAdmin, TAccountBase, TAccountSystemProgram, - TAccountTokenProgram + TAccountTokenProgram, + TAccountAssociatedTokenProgram > ): InitializeVaultInstruction< typeof JITO_VAULT_PROGRAM_ADDRESS, TAccountConfig, TAccountVault, TAccountVrtMint, - TAccountTokenMint, + TAccountStMint, + TAccountAdminStTokenAccount, + TAccountVaultStTokenAccount, + TAccountBurnVault, + TAccountBurnVaultVrtTokenAccount, TAccountAdmin, TAccountBase, TAccountSystemProgram, - TAccountTokenProgram + TAccountTokenProgram, + TAccountAssociatedTokenProgram > { // Program address. const programAddress = JITO_VAULT_PROGRAM_ADDRESS; @@ -199,11 +253,28 @@ 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, + }, + 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, @@ -229,11 +300,16 @@ 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.burnVault), + getAccountMeta(accounts.burnVaultVrtTokenAccount), getAccountMeta(accounts.admin), getAccountMeta(accounts.base), getAccountMeta(accounts.systemProgram), getAccountMeta(accounts.tokenProgram), + getAccountMeta(accounts.associatedTokenProgram), ], programAddress, data: getInitializeVaultInstructionDataEncoder().encode( @@ -244,11 +320,16 @@ export function getInitializeVaultInstruction< TAccountConfig, TAccountVault, TAccountVrtMint, - TAccountTokenMint, + TAccountStMint, + TAccountAdminStTokenAccount, + TAccountVaultStTokenAccount, + TAccountBurnVault, + TAccountBurnVaultVrtTokenAccount, TAccountAdmin, TAccountBase, TAccountSystemProgram, - TAccountTokenProgram + TAccountTokenProgram, + TAccountAssociatedTokenProgram >; return instruction; @@ -263,11 +344,16 @@ 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]; + burnVault: TAccountMetas[6]; + burnVaultVrtTokenAccount: TAccountMetas[7]; + admin: TAccountMetas[8]; + base: TAccountMetas[9]; + systemProgram: TAccountMetas[10]; + tokenProgram: TAccountMetas[11]; + associatedTokenProgram: TAccountMetas[12]; }; data: InitializeVaultInstructionData; }; @@ -280,7 +366,7 @@ export function parseInitializeVaultInstruction< IInstructionWithAccounts & IInstructionWithData ): ParsedInitializeVaultInstruction { - if (instruction.accounts.length < 8) { + if (instruction.accounts.length < 13) { // TODO: Coded error. throw new Error('Not enough accounts'); } @@ -296,11 +382,16 @@ export function parseInitializeVaultInstruction< config: getNextAccount(), vault: getNextAccount(), vrtMint: getNextAccount(), - tokenMint: getNextAccount(), + 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 50fd5817..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,177 +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 - NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate + VaultRewardFeeIsZero = 0x421, + /// 1058 - VrtOutCannotBeZero + #[error("VrtOutCannotBeZero")] + VrtOutCannotBeZero = 0x422, + /// 1059 - NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate #[error("NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate")] - NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate = 0x421, + 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 70231f2a..4466ab96 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,15 @@ 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 burn_vault: solana_program::pubkey::Pubkey, + + pub burn_vault_vrt_token_account: solana_program::pubkey::Pubkey, pub admin: solana_program::pubkey::Pubkey, @@ -23,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 { @@ -38,7 +48,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(13 + remaining_accounts.len()); accounts.push(solana_program::instruction::AccountMeta::new( self.config, false, @@ -51,7 +61,23 @@ 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_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( @@ -68,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(); @@ -105,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`. @@ -114,25 +145,36 @@ 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. `[]` 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, vault: Option, vrt_mint: Option, - token_mint: Option, + 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, } @@ -156,8 +198,37 @@ 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)] + 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)] @@ -183,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 @@ -202,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( @@ -226,7 +310,17 @@ 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"), + 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 @@ -235,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 @@ -250,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) @@ -264,7 +365,15 @@ 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 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>, @@ -273,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. @@ -286,7 +397,15 @@ 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 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>, @@ -295,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, } @@ -310,11 +431,16 @@ 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, + 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, } } @@ -351,7 +477,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(13 + remaining_accounts.len()); accounts.push(solana_program::instruction::AccountMeta::new( *self.config.key, false, @@ -365,7 +491,23 @@ 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_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( @@ -384,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, @@ -400,16 +546,21 @@ 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(13 + 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.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())); @@ -429,11 +580,16 @@ 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. `[]` 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>, @@ -446,15 +602,21 @@ 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, + 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 } @@ -481,11 +643,43 @@ impl<'a, 'b> InitializeVaultCpiBuilder<'a, 'b> { self } #[inline(always)] - pub fn token_mint( + pub fn st_mint( + &mut self, + st_mint: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.st_mint = Some(st_mint); + self + } + #[inline(always)] + pub fn admin_st_token_account( &mut self, - token_mint: &'b solana_program::account_info::AccountInfo<'a>, + admin_st_token_account: &'b solana_program::account_info::AccountInfo<'a>, ) -> &mut Self { - self.instruction.token_mint = Some(token_mint); + 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)] + 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)] @@ -515,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 @@ -534,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( @@ -596,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, @@ -606,7 +818,24 @@ 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"), + + 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"), @@ -621,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( @@ -636,15 +870,21 @@ 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>>, + 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>, 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 882b0ffe..8411ae85 100644 --- a/idl/jito_vault.json +++ b/idl/jito_vault.json @@ -61,10 +61,30 @@ "isSigner": true }, { - "name": "tokenMint", + "name": "stMint", + "isMut": false, + "isSigner": false + }, + { + "name": "adminStTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "vaultStTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "burnVault", "isMut": false, "isSigner": false }, + { + "name": "burnVaultVrtTokenAccount", + "isMut": true, + "isSigner": false + }, { "name": "admin", "isMut": true, @@ -84,6 +104,11 @@ "name": "tokenProgram", "isMut": false, "isSigner": false + }, + { + "name": "associatedTokenProgram", + "isMut": false, + "isSigner": false } ], "args": [ @@ -102,6 +127,10 @@ { "name": "decimals", "type": "u8" + }, + { + "name": "initializeTokenAmount", + "type": "u64" } ], "discriminant": { @@ -2135,286 +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": 1059, "name": "NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate", "msg": "NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate" }, diff --git a/integration_tests/tests/fixtures/vault_client.rs b/integration_tests/tests/fixtures/vault_client.rs index 71d72a71..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,33 +295,59 @@ 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; 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?; + + 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(), + initialize_token_amount, + ) + .await?; + 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, + &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?; - // 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?; @@ -688,13 +715,18 @@ 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, + 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?; @@ -704,13 +736,18 @@ impl VaultProgramClient { config, vault, &vrt_mint.pubkey(), - &token_mint.pubkey(), + &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 8659dcae..8b94dfd2 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::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + AMOUNT_IN + ); + assert_eq!( + vault.vrt_supply() - Vault::DEFAULT_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::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 ba003776..fddab05e 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; @@ -293,8 +293,14 @@ 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::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + 0 + ); + assert_eq!( + vault.vrt_supply() - Vault::DEFAULT_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); @@ -458,8 +464,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::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + expected_fee + ); + assert_eq!( + vault.vrt_supply() - Vault::DEFAULT_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); @@ -607,8 +619,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::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + expected_fee + ); + assert_eq!( + vault.vrt_supply() - Vault::DEFAULT_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); @@ -724,8 +742,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::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + MINT_AMOUNT - AMOUNT_TO_WITHDRAWAL + ); + assert_eq!( + vault.vrt_supply() - Vault::DEFAULT_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); @@ -936,8 +960,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::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + expected_remaining_supply + ); + assert_eq!( + vault.vrt_supply() - Vault::DEFAULT_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 442d9be0..bffe9d85 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; @@ -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::DEFAULT_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::DEFAULT_INITIALIZATION_TOKEN_AMOUNT + ); assert_eq!(vault.delegation_state.enqueued_for_cooldown_amount(), 0); assert_eq!(vault.vrt_ready_to_claim_amount(), 75_000); } @@ -995,8 +1001,14 @@ 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::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + 0 + ); + assert_eq!( + vault.vrt_supply() - Vault::DEFAULT_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); @@ -1111,8 +1123,14 @@ 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::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + 0 + ); + assert_eq!( + vault.vrt_supply() - Vault::DEFAULT_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); @@ -1227,8 +1245,14 @@ 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::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + 0 + ); + assert_eq!( + vault.vrt_supply() - Vault::DEFAULT_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 c433454b..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; + 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, @@ -39,8 +40,16 @@ 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(), + 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); @@ -51,6 +60,81 @@ 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] + 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] 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..ddff6b65 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::DEFAULT_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::DEFAULT_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::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 a5654b32..2c760015 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}; @@ -11,7 +11,7 @@ mod tests { const MINT_AMOUNT: u64 = 1000; // Match's unit test in vault.rs: test_calculate_reward_fee - const EXPECTED_FEE: u64 = 52; + const EXPECTED_FEE: u64 = 92; let deposit_fee_bps = 0; let withdrawal_fee_bps = 0; @@ -78,9 +78,15 @@ mod tests { .await .unwrap(); - assert_eq!(MINT_AMOUNT * 2, vault.tokens_deposited()); + assert_eq!( + MINT_AMOUNT * 2, + 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()); + assert_eq!( + MINT_AMOUNT + EXPECTED_FEE, + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + ); } #[tokio::test] @@ -154,9 +160,15 @@ mod tests { .await .unwrap(); - assert_eq!(MINT_AMOUNT * 2, vault.tokens_deposited()); + assert_eq!( + MINT_AMOUNT * 2, + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + ); assert_eq!(MINT_AMOUNT, reward_fee_account.amount); - assert_eq!(MINT_AMOUNT * 2, vault.vrt_supply()); + assert_eq!( + MINT_AMOUNT * 2, + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + ); } #[tokio::test] @@ -224,8 +236,111 @@ mod tests { .await .unwrap(); - assert_eq!(MINT_AMOUNT, vault.tokens_deposited()); + assert_eq!( + MINT_AMOUNT, + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT + ); assert_eq!(0, reward_fee_account.amount); - assert_eq!(0, vault.vrt_supply()); + assert_eq!( + 0, + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT + ); + } + + #[tokio::test] + async fn test_reward_with_non_zero_balance() { + let mut fixture = TestBuilder::new().await; + + const MINT_AMOUNT: u64 = 100_000; + const EXPECTED_FEE: u64 = 5500; + + let deposit_fee_bps = 0; + let withdrawal_fee_bps = 0; + let reward_fee_bps = 1000; // 10% + let num_operators = 1; + let slasher_amounts = vec![]; + + let ConfiguredVault { + mut vault_program_client, + vault_root, + operator_roots, + .. + } = fixture + .setup_vault_with_ncn_and_operators( + deposit_fee_bps, + withdrawal_fee_bps, + reward_fee_bps, + num_operators, + &slasher_amounts, + ) + .await + .unwrap(); + + let depositor = Keypair::new(); + vault_program_client + .configure_depositor(&vault_root, &depositor.pubkey(), MINT_AMOUNT * 2) + .await + .unwrap(); + + vault_program_client + .do_mint_to(&vault_root, &depositor, MINT_AMOUNT, MINT_AMOUNT) + .await + .unwrap(); + + let config = vault_program_client + .get_config(&Config::find_program_address(&jito_vault_program::id()).0) + .await + .unwrap(); + + // go to next epoch to force update + fixture + .warp_slot_incremental(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(); + + vault_program_client + .create_and_fund_reward_vault(&vault_root.vault_pubkey, &depositor, MINT_AMOUNT) + .await + .unwrap(); + + // go to next epoch to force update + fixture + .warp_slot_incremental(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(); + + let reward_fee_account = vault_program_client + .get_reward_fee_token_account(&vault_root.vault_pubkey) + .await + .unwrap(); + + assert_eq!( + MINT_AMOUNT * 2, + 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::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 019f553a..125c4bd3 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; @@ -110,7 +110,8 @@ mod tests { async fn test_update_vault_balance_ok() { const MINT_AMOUNT: u64 = 1000; // Match's unit test in vault.rs: test_calculate_reward_fee - const EXPECTED_FEE: u64 = 52; + // We have to account for the INITIALIZATION_TOKEN_AMOUNT + const EXPECTED_FEE: u64 = 92; let (fixture, vault_root) = setup_with_reward( MINT_AMOUNT, @@ -147,24 +148,30 @@ mod tests { .await .unwrap(); - assert_eq!(vault.tokens_deposited(), MINT_AMOUNT * 2); + assert_eq!( + vault.tokens_deposited() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + MINT_AMOUNT * 2 + ); assert_eq!(reward_fee_account.amount, EXPECTED_FEE); - assert_eq!(vault.vrt_supply(), MINT_AMOUNT + EXPECTED_FEE); + assert_eq!( + vault.vrt_supply() - Vault::DEFAULT_INITIALIZATION_TOKEN_AMOUNT, + MINT_AMOUNT + EXPECTED_FEE + ); } #[tokio::test] async fn test_update_vault_balance_no_initial_supply() { + // There will always be an initial supply of 10_000 let (fixture, vault_root) = setup_with_reward( 1000, 0, 0, 1000, //10% ) .await; let mut vault_program_client = fixture.vault_program_client(); - let test_error = vault_program_client + vault_program_client .update_vault_balance(&vault_root.vault_pubkey) - .await; - - assert_vault_error(test_error, VaultError::VaultRewardFeeIsZero); + .await + .unwrap(); } #[tokio::test] diff --git a/vault_core/src/burn_vault.rs b/vault_core/src/burn_vault.rs new file mode 100644 index 00000000..9e6b97ba --- /dev/null +++ b/vault_core/src/burn_vault.rs @@ -0,0 +1,199 @@ +use solana_program::{account_info::AccountInfo, msg, program_error::ProgramError, pubkey::Pubkey}; + +// Empty PDA to send tokens to "burn" +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(()) + } +} + +#[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/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 7055719e..307e0102 100644 --- a/vault_core/src/vault.rs +++ b/vault_core/src/vault.rs @@ -154,6 +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 DEFAULT_INITIALIZATION_TOKEN_AMOUNT: u64 = 10_000; #[allow(clippy::too_many_arguments)] pub fn new( @@ -442,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 @@ -2507,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 14cba5e3..ad711c57 100644 --- a/vault_program/src/initialize_vault.rs +++ b/vault_program/src/initialize_vault.rs @@ -4,17 +4,30 @@ 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_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::{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::state::Mint; /// Processes the create instruction: [`crate::VaultInstruction::InitializeVault`] pub fn process_initialize_vault( @@ -24,8 +37,10 @@ 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, mint, admin, base, system_program, token_program] = accounts + 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); }; @@ -36,13 +51,31 @@ pub fn process_initialize_vault( 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_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, + )?; + 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)?; - - // Only the original spl token program is allowed 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(VaultError::VaultInitialAmountFailed.into()); + } // The vault account shall be at the canonical PDA let (vault_pubkey, vault_bump, mut vault_seeds) = @@ -53,6 +86,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 @@ -111,11 +148,11 @@ 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, - *mint.key, + *st_mint.key, *admin.key, config.num_vaults(), *base.key, @@ -126,6 +163,87 @@ pub fn process_initialize_vault( vault_bump, slot, )?; + + { + // "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, + )?, + &[ + admin_st_token_account.clone(), + vault_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_program/src/mint_to.rs b/vault_program/src/mint_to.rs index bfc1294d..9afe8860 100644 --- a/vault_program/src/mint_to.rs +++ b/vault_program/src/mint_to.rs @@ -94,6 +94,11 @@ pub fn process_mint( vrt_to_fee_wallet, } = vault.mint_with_fee(amount_in, min_amount_out)?; + if vrt_to_depositor == 0 { + msg!("Some VRT must be minted to the depositor. If you wish to donate to the vault, please send ST directly to the vault token account"); + return Err(VaultError::VrtOutCannotBeZero.into()); + } + // transfer tokens from depositor to vault { invoke( 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 b4452b8a..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")] @@ -117,9 +119,10 @@ pub enum VaultError { VaultRewardFeeDeltaTooLarge, #[error("VaultRewardFeeIsZero")] VaultRewardFeeIsZero, + #[error("VrtOutCannotBeZero")] + VrtOutCannotBeZero, #[error("NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate")] NonZeroAdditionalAssetsNeededForWithdrawalAtEndOfUpdate, - #[error("ArithmeticOverflow")] ArithmeticOverflow = 3000, #[error("ArithmeticUnderflow")] diff --git a/vault_sdk/src/instruction.rs b/vault_sdk/src/instruction.rs index 5f73b8d6..e29892d6 100644 --- a/vault_sdk/src/instruction.rs +++ b/vault_sdk/src/instruction.rs @@ -19,16 +19,22 @@ 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, 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 f4b08a8d..c4faaea6 100644 --- a/vault_sdk/src/sdk.rs +++ b/vault_sdk/src/sdk.rs @@ -40,23 +40,33 @@ 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, + 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), 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, false), + AccountMeta::new(*vault_st_token_account, false), + AccountMeta::new_readonly(*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, @@ -66,6 +76,7 @@ pub fn initialize_vault( withdrawal_fee_bps, reward_fee_bps, decimals, + initialize_token_amount, } .try_to_vec() .unwrap(),