Skip to content

Commit

Permalink
backstop: cap q4w length and extend emission allowance TTL
Browse files Browse the repository at this point in the history
  • Loading branch information
mootz12 committed Mar 28, 2024
1 parent 1b045bd commit bfd3ae9
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 12 deletions.
1 change: 0 additions & 1 deletion backstop/src/backstop/deposit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ mod tests {
e.as_contract(&backstop_address, || {
execute_deposit(&e, &samwise, &pool_0_id, 100_0000001);

// TODO: Handle token errors gracefully
assert!(false);
});
}
Expand Down
81 changes: 77 additions & 4 deletions backstop/src/backstop/user.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use soroban_sdk::{contracttype, panic_with_error, vec, Env, Vec};

use crate::errors::BackstopError;
use crate::{
constants::{MAX_Q4W_SIZE, Q4W_LOCK_TIME},
errors::BackstopError,
};

/// A deposit that is queued for withdrawal
#[derive(Clone)]
Expand Down Expand Up @@ -51,14 +54,15 @@ impl UserBalance {
if self.shares < to_q {
panic_with_error!(e, BackstopError::BalanceError);
}
if self.q4w.len() >= MAX_Q4W_SIZE {
panic_with_error!(e, BackstopError::TooManyQ4WEntries);
}
self.shares = self.shares - to_q;

// user has enough tokens to withdrawal, add Q4W
// TODO: Consider capping how many active Q4Ws a user can have
let twentyone_days_in_sec = 21 * 24 * 60 * 60;
let new_q4w = Q4W {
amount: to_q,
exp: e.ledger().timestamp() + twentyone_days_in_sec,
exp: e.ledger().timestamp() + Q4W_LOCK_TIME,
};
self.q4w.push_back(new_q4w.clone());
}
Expand Down Expand Up @@ -210,6 +214,75 @@ mod tests {
assert_eq_vec_q4w(&user.q4w, &cur_q4w);
}

#[test]
fn test_q4w_new_to_max_works() {
let e = Env::default();
let exp = 12592000;
let mut cur_q4w = vec![&e];
for i in 0..20 {
cur_q4w.push_back(Q4W {
amount: 200,
exp: exp + i,
});
}
let mut user = UserBalance {
shares: 1000,
q4w: cur_q4w.clone(),
};

e.ledger().set(LedgerInfo {
protocol_version: 20,
sequence_number: 1,
timestamp: 11000000,
network_id: Default::default(),
base_reserve: 10,
min_temp_entry_ttl: 10,
min_persistent_entry_ttl: 10,
max_entry_ttl: 3110400,
});

let to_queue = 500;
user.queue_shares_for_withdrawal(&e, to_queue);
cur_q4w.push_back(Q4W {
amount: to_queue,
exp: 11000000 + 21 * 24 * 60 * 60,
});
assert_eq_vec_q4w(&user.q4w, &cur_q4w);
}

#[test]
#[should_panic(expected = "Error(Contract, #1007)")]
fn test_q4w_new_over_max_panics() {
let e = Env::default();

let exp = 12592000;
let mut cur_q4w = vec![&e];
for i in 0..21 {
cur_q4w.push_back(Q4W {
amount: 200,
exp: exp + i,
});
}
let mut user = UserBalance {
shares: 1000,
q4w: cur_q4w.clone(),
};

e.ledger().set(LedgerInfo {
protocol_version: 20,
sequence_number: 1,
timestamp: 11000000,
network_id: Default::default(),
base_reserve: 10,
min_temp_entry_ttl: 10,
min_persistent_entry_ttl: 10,
max_entry_ttl: 3110400,
});

let to_queue = 500;
user.queue_shares_for_withdrawal(&e, to_queue);
}

#[test]
#[should_panic(expected = "Error(Contract, #10)")]
fn test_q4w_over_shares_panics() {
Expand Down
7 changes: 7 additions & 0 deletions backstop/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,10 @@ pub const SCALAR_7: i128 = 1_0000000;
/// actual deployment time and should not be considered accruate. It is only used to determine reward
/// zone size on ~90 day intervals, starting at 10 on or before April 15th, 2024 00:00:00 UTC.
pub const BACKSTOP_EPOCH: u64 = 1713139200;

/// The maximum amount of active Q4W entries that a user can have against a single backstop.
/// Set such that a user can create a maximum of 1 entry per day over the 21 day lock period.
pub const MAX_Q4W_SIZE: u32 = 21;

/// The time in seconds that a Q4W entry is locked for (21 days).
pub const Q4W_LOCK_TIME: u64 = 21 * 24 * 60 * 60;
6 changes: 2 additions & 4 deletions backstop/src/emissions/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,6 @@ pub fn gulp_emissions(e: &Env) -> i128 {

let mut rz_balance: Vec<PoolBalance> = vec![e];

// TODO: Potential to assume optimization of backstop token balances ~= RZ tokens
// However, linear iteration over the RZ will still occur
// fetch total tokens of BLND in the reward zone
let mut total_non_queued_tokens: i128 = 0;
for rz_pool_index in 0..rz_len {
Expand Down Expand Up @@ -140,12 +138,12 @@ pub fn gulp_pool_emissions(e: &Env, pool_id: &Address) -> i128 {
let blnd_token_client = TokenClient::new(e, &storage::get_blnd_token(e));
let current_allowance = blnd_token_client.allowance(&e.current_contract_address(), pool_id);
let new_tokens = current_allowance + pool_emissions;
let new_seq = e.ledger().sequence() + 17_280 * 30; // ~30 days: TODO: check phase 1 limits
let new_seq = e.ledger().sequence() + storage::LEDGER_BUMP_USER; // ~120 days
blnd_token_client.approve(
&e.current_contract_address(),
pool_id,
&new_tokens,
&new_seq, // ~30 days: TODO: check phase 1 limits
&new_seq,
);
storage::set_pool_emissions(e, pool_id, 0);
pool_emissions
Expand Down
1 change: 1 addition & 0 deletions backstop/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ pub enum BackstopError {
NotPool = 1004,
InvalidShareMintAmount = 1005,
InvalidTokenWithdrawAmount = 1006,
TooManyQ4WEntries = 1007,
}
4 changes: 1 addition & 3 deletions backstop/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const LEDGER_THRESHOLD_SHARED: u32 = ONE_DAY_LEDGERS * 45; // ~ 45 days
const LEDGER_BUMP_SHARED: u32 = LEDGER_THRESHOLD_SHARED + ONE_DAY_LEDGERS; // ~ 46 days

const LEDGER_THRESHOLD_USER: u32 = ONE_DAY_LEDGERS * 100; // ~ 100 days
const LEDGER_BUMP_USER: u32 = LEDGER_THRESHOLD_USER + 20 * ONE_DAY_LEDGERS; // ~ 120 days
pub(crate) const LEDGER_BUMP_USER: u32 = LEDGER_THRESHOLD_USER + 20 * ONE_DAY_LEDGERS; // ~ 120 days

/********** Storage Types **********/

Expand Down Expand Up @@ -314,8 +314,6 @@ pub fn set_last_distribution_time(e: &Env, timestamp: &u64) {
}

/// Get the current pool addresses that are in the reward zone
///
// @dev - TODO: Once data access costs are available, find the breakeven point for splitting this up
pub fn get_reward_zone(e: &Env) -> Vec<Address> {
get_persistent_default(
e,
Expand Down

0 comments on commit bfd3ae9

Please sign in to comment.