Skip to content

Commit c2beade

Browse files
authored
Better zero handling (#92)
- Add failure for 0 asset handling for the following methods: cooldown, delegation, minting, burning - Added details on specification for mint/burn/etc.
1 parent 9051a45 commit c2beade

File tree

12 files changed

+272
-70
lines changed

12 files changed

+272
-70
lines changed

clients/js/vault_client/errors/jitoVault.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ export const JITO_VAULT_ERROR__SLASHER_OVERFLOW = 0x40d; // 1037
9494
export const JITO_VAULT_ERROR__NCN_OVERFLOW = 0x40e; // 1038
9595
/** OperatorOverflow: OperatorOverflow */
9696
export const JITO_VAULT_ERROR__OPERATOR_OVERFLOW = 0x40f; // 1039
97+
/** VaultDelegationZero: VaultDelegationZero */
98+
export const JITO_VAULT_ERROR__VAULT_DELEGATION_ZERO = 0x410; // 1040
99+
/** VaultCooldownZero: VaultCooldownZero */
100+
export const JITO_VAULT_ERROR__VAULT_COOLDOWN_ZERO = 0x411; // 1041
101+
/** VaultBurnZero: VaultBurnZero */
102+
export const JITO_VAULT_ERROR__VAULT_BURN_ZERO = 0x412; // 1042
103+
/** VaultEnqueueWithdrawalAmountZero: VaultEnqueueWithdrawalAmountZero */
104+
export const JITO_VAULT_ERROR__VAULT_ENQUEUE_WITHDRAWAL_AMOUNT_ZERO = 0x413; // 1043
105+
/** VaultMintZero: VaultMintZero */
106+
export const JITO_VAULT_ERROR__VAULT_MINT_ZERO = 0x414; // 1044
97107

98108
export type JitoVaultError =
99109
| typeof JITO_VAULT_ERROR__NCN_OPERATOR_STATE_UNSLASHABLE
@@ -105,9 +115,13 @@ export type JitoVaultError =
105115
| typeof JITO_VAULT_ERROR__SLASHER_OVERFLOW
106116
| typeof JITO_VAULT_ERROR__SLIPPAGE_ERROR
107117
| typeof JITO_VAULT_ERROR__VAULT_ADMIN_INVALID
118+
| typeof JITO_VAULT_ERROR__VAULT_BURN_ZERO
108119
| typeof JITO_VAULT_ERROR__VAULT_CAPACITY_ADMIN_INVALID
109120
| typeof JITO_VAULT_ERROR__VAULT_CAPACITY_EXCEEDED
121+
| typeof JITO_VAULT_ERROR__VAULT_COOLDOWN_ZERO
110122
| typeof JITO_VAULT_ERROR__VAULT_DELEGATION_ADMIN_INVALID
123+
| typeof JITO_VAULT_ERROR__VAULT_DELEGATION_ZERO
124+
| typeof JITO_VAULT_ERROR__VAULT_ENQUEUE_WITHDRAWAL_AMOUNT_ZERO
111125
| typeof JITO_VAULT_ERROR__VAULT_FEE_ADMIN_INVALID
112126
| typeof JITO_VAULT_ERROR__VAULT_FEE_BUMP_TOO_LARGE
113127
| typeof JITO_VAULT_ERROR__VAULT_FEE_CAP_EXCEEDED
@@ -116,6 +130,7 @@ export type JitoVaultError =
116130
| typeof JITO_VAULT_ERROR__VAULT_IS_UPDATED
117131
| typeof JITO_VAULT_ERROR__VAULT_MAX_SLASHED_PER_OPERATOR_EXCEEDED
118132
| typeof JITO_VAULT_ERROR__VAULT_MINT_BURN_ADMIN_INVALID
133+
| typeof JITO_VAULT_ERROR__VAULT_MINT_ZERO
119134
| typeof JITO_VAULT_ERROR__VAULT_NCN_ADMIN_INVALID
120135
| typeof JITO_VAULT_ERROR__VAULT_NCN_SLASHER_TICKET_FAILED_COOLDOWN
121136
| typeof JITO_VAULT_ERROR__VAULT_NCN_SLASHER_TICKET_FAILED_WARMUP
@@ -149,9 +164,13 @@ if (process.env.NODE_ENV !== 'production') {
149164
[JITO_VAULT_ERROR__SLASHER_OVERFLOW]: `SlasherOverflow`,
150165
[JITO_VAULT_ERROR__SLIPPAGE_ERROR]: `SlippageError`,
151166
[JITO_VAULT_ERROR__VAULT_ADMIN_INVALID]: `VaultAdminInvalid`,
167+
[JITO_VAULT_ERROR__VAULT_BURN_ZERO]: `VaultBurnZero`,
152168
[JITO_VAULT_ERROR__VAULT_CAPACITY_ADMIN_INVALID]: `VaultCapacityAdminInvalid`,
153169
[JITO_VAULT_ERROR__VAULT_CAPACITY_EXCEEDED]: `VaultCapacityExceeded`,
170+
[JITO_VAULT_ERROR__VAULT_COOLDOWN_ZERO]: `VaultCooldownZero`,
154171
[JITO_VAULT_ERROR__VAULT_DELEGATION_ADMIN_INVALID]: `VaultDelegationAdminInvalid`,
172+
[JITO_VAULT_ERROR__VAULT_DELEGATION_ZERO]: `VaultDelegationZero`,
173+
[JITO_VAULT_ERROR__VAULT_ENQUEUE_WITHDRAWAL_AMOUNT_ZERO]: `VaultEnqueueWithdrawalAmountZero`,
155174
[JITO_VAULT_ERROR__VAULT_FEE_ADMIN_INVALID]: `VaultFeeAdminInvalid`,
156175
[JITO_VAULT_ERROR__VAULT_FEE_BUMP_TOO_LARGE]: `VaultFeeBumpTooLarge`,
157176
[JITO_VAULT_ERROR__VAULT_FEE_CAP_EXCEEDED]: `VaultFeeCapExceeded`,
@@ -160,6 +179,7 @@ if (process.env.NODE_ENV !== 'production') {
160179
[JITO_VAULT_ERROR__VAULT_IS_UPDATED]: `VaultIsUpdated`,
161180
[JITO_VAULT_ERROR__VAULT_MAX_SLASHED_PER_OPERATOR_EXCEEDED]: `VaultMaxSlashedPerOperatorExceeded`,
162181
[JITO_VAULT_ERROR__VAULT_MINT_BURN_ADMIN_INVALID]: `VaultMintBurnAdminInvalid`,
182+
[JITO_VAULT_ERROR__VAULT_MINT_ZERO]: `VaultMintZero`,
163183
[JITO_VAULT_ERROR__VAULT_NCN_ADMIN_INVALID]: `VaultNcnAdminInvalid`,
164184
[JITO_VAULT_ERROR__VAULT_NCN_SLASHER_TICKET_FAILED_COOLDOWN]: `VaultNcnSlasherTicketFailedCooldown`,
165185
[JITO_VAULT_ERROR__VAULT_NCN_SLASHER_TICKET_FAILED_WARMUP]: `VaultNcnSlasherTicketFailedWarmup`,

clients/rust/vault_client/src/generated/errors/jito_vault.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,21 @@ pub enum JitoVaultError {
129129
/// 1039 - OperatorOverflow
130130
#[error("OperatorOverflow")]
131131
OperatorOverflow = 0x40F,
132+
/// 1040 - VaultDelegationZero
133+
#[error("VaultDelegationZero")]
134+
VaultDelegationZero = 0x410,
135+
/// 1041 - VaultCooldownZero
136+
#[error("VaultCooldownZero")]
137+
VaultCooldownZero = 0x411,
138+
/// 1042 - VaultBurnZero
139+
#[error("VaultBurnZero")]
140+
VaultBurnZero = 0x412,
141+
/// 1043 - VaultEnqueueWithdrawalAmountZero
142+
#[error("VaultEnqueueWithdrawalAmountZero")]
143+
VaultEnqueueWithdrawalAmountZero = 0x413,
144+
/// 1044 - VaultMintZero
145+
#[error("VaultMintZero")]
146+
VaultMintZero = 0x414,
132147
}
133148

134149
impl solana_program::program_error::PrintProgramError for JitoVaultError {

idl/jito_vault.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2316,6 +2316,31 @@
23162316
"code": 1039,
23172317
"name": "OperatorOverflow",
23182318
"msg": "OperatorOverflow"
2319+
},
2320+
{
2321+
"code": 1040,
2322+
"name": "VaultDelegationZero",
2323+
"msg": "VaultDelegationZero"
2324+
},
2325+
{
2326+
"code": 1041,
2327+
"name": "VaultCooldownZero",
2328+
"msg": "VaultCooldownZero"
2329+
},
2330+
{
2331+
"code": 1042,
2332+
"name": "VaultBurnZero",
2333+
"msg": "VaultBurnZero"
2334+
},
2335+
{
2336+
"code": 1043,
2337+
"name": "VaultEnqueueWithdrawalAmountZero",
2338+
"msg": "VaultEnqueueWithdrawalAmountZero"
2339+
},
2340+
{
2341+
"code": 1044,
2342+
"name": "VaultMintZero",
2343+
"msg": "VaultMintZero"
23192344
}
23202345
],
23212346
"metadata": {

integration_tests/tests/vault/enqueue_withdrawal.rs

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
#[cfg(test)]
22
mod tests {
33
use jito_vault_core::config::Config;
4-
use solana_sdk::signature::{Keypair, Signer};
4+
use jito_vault_sdk::error::VaultError;
5+
use solana_sdk::{
6+
instruction::InstructionError,
7+
signature::{Keypair, Signer},
8+
transaction::TransactionError,
9+
};
510
use spl_associated_token_account::get_associated_token_address;
611

712
use crate::fixtures::{
@@ -45,29 +50,12 @@ mod tests {
4550
.unwrap();
4651

4752
let depositor = Keypair::new();
48-
fixture.transfer(&depositor.pubkey(), 100.0).await.unwrap();
49-
fixture
50-
.mint_spl_to(&vault.supported_mint, &depositor.pubkey(), MINT_AMOUNT)
51-
.await
52-
.unwrap();
53-
fixture
54-
.create_ata(&vault.vrt_mint, &depositor.pubkey())
53+
vault_program_client
54+
.configure_depositor(&vault_root, &depositor.pubkey(), MINT_AMOUNT)
5555
.await
5656
.unwrap();
57-
5857
vault_program_client
59-
.mint_to(
60-
&vault_root.vault_pubkey,
61-
&vault.vrt_mint,
62-
&depositor,
63-
&get_associated_token_address(&depositor.pubkey(), &vault.supported_mint),
64-
&get_associated_token_address(&vault_root.vault_pubkey, &vault.supported_mint),
65-
&get_associated_token_address(&depositor.pubkey(), &vault.vrt_mint),
66-
&get_associated_token_address(&vault.fee_wallet, &vault.vrt_mint),
67-
None,
68-
MINT_AMOUNT,
69-
min_amount_out,
70-
)
58+
.do_mint_to(&vault_root, &depositor, MINT_AMOUNT, min_amount_out)
7159
.await
7260
.unwrap();
7361

@@ -156,4 +144,61 @@ mod tests {
156144
.unwrap();
157145
assert_eq!(vault.vrt_enqueued_for_cooldown_amount(), amount_to_dequeue);
158146
}
147+
148+
#[tokio::test]
149+
async fn test_enqueue_withdraw_zero_fails() {
150+
let mut fixture = TestBuilder::new().await;
151+
let ConfiguredVault {
152+
mut vault_program_client,
153+
vault_root,
154+
operator_roots,
155+
..
156+
} = fixture
157+
.setup_vault_with_ncn_and_operators(0, 0, 0, 1, &[])
158+
.await
159+
.unwrap();
160+
161+
let depositor = Keypair::new();
162+
vault_program_client
163+
.configure_depositor(&vault_root, &depositor.pubkey(), 100)
164+
.await
165+
.unwrap();
166+
vault_program_client
167+
.do_mint_to(&vault_root, &depositor, 100, 100)
168+
.await
169+
.unwrap();
170+
171+
// let vault operator ticket warmup
172+
let config = vault_program_client
173+
.get_config(&Config::find_program_address(&jito_vault_program::id()).0)
174+
.await
175+
.unwrap();
176+
fixture
177+
.warp_slot_incremental(2 * config.epoch_length())
178+
.await
179+
.unwrap();
180+
181+
let operator_root_pubkeys: Vec<_> = operator_roots
182+
.iter()
183+
.map(|root| root.operator_pubkey)
184+
.collect();
185+
vault_program_client
186+
.do_full_vault_update(&vault_root.vault_pubkey, &operator_root_pubkeys)
187+
.await
188+
.unwrap();
189+
190+
let err = vault_program_client
191+
.do_enqueue_withdraw(&vault_root, &depositor, 0)
192+
.await
193+
.unwrap_err()
194+
.to_transaction_error()
195+
.unwrap();
196+
assert_eq!(
197+
err,
198+
TransactionError::InstructionError(
199+
0,
200+
InstructionError::Custom(VaultError::VaultEnqueueWithdrawalAmountZero as u32)
201+
)
202+
);
203+
}
159204
}

vault_core/src/delegation_state.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ impl DelegationState {
188188
/// Cools down stake by subtracting it from the staked amount and adding it to the enqueued
189189
/// cooldown amount
190190
pub fn cooldown(&mut self, amount: u64) -> Result<(), VaultError> {
191+
if amount == 0 {
192+
msg!("Cooldown amount is zero");
193+
return Err(VaultError::VaultCooldownZero);
194+
}
195+
191196
let mut staked_amount: u64 = self.staked_amount.into();
192197
staked_amount = staked_amount
193198
.checked_sub(amount)
@@ -205,6 +210,11 @@ impl DelegationState {
205210

206211
/// Delegates assets to the operator
207212
pub fn delegate(&mut self, amount: u64) -> Result<(), VaultError> {
213+
if amount == 0 {
214+
msg!("Delegation amount is zero");
215+
return Err(VaultError::VaultDelegationZero);
216+
}
217+
208218
let mut staked_amount: u64 = self.staked_amount.into();
209219
staked_amount = staked_amount
210220
.checked_add(amount)
@@ -217,6 +227,7 @@ impl DelegationState {
217227
#[cfg(test)]
218228
mod tests {
219229
use jito_bytemuck::types::PodU64;
230+
use jito_vault_sdk::error::VaultError;
220231

221232
use crate::delegation_state::DelegationState;
222233

@@ -279,4 +290,22 @@ mod tests {
279290
assert_eq!(delegation_state.cooling_down_amount(), 0);
280291
assert_eq!(delegation_state.total_security().unwrap(), 50);
281292
}
293+
294+
#[test]
295+
fn test_delegate_zero() {
296+
let mut delegation_state = DelegationState::default();
297+
assert_eq!(
298+
delegation_state.delegate(0),
299+
Err(VaultError::VaultDelegationZero)
300+
);
301+
}
302+
303+
#[test]
304+
fn test_cooldown_zero() {
305+
let mut delegation_state = DelegationState::new(100, 0, 0);
306+
assert_eq!(
307+
delegation_state.cooldown(0),
308+
Err(VaultError::VaultCooldownZero)
309+
);
310+
}
282311
}

0 commit comments

Comments
 (0)