Skip to content

Commit 430d720

Browse files
committed
Adding Suilend test 'test_pool_reward_manager_basic'
1 parent 60ac7dc commit 430d720

File tree

5 files changed

+136
-27
lines changed

5 files changed

+136
-27
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

token-lending/program/src/processor/liquidity_mining/claim_user_reward.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ struct ClaimUserReward<'a, 'info> {
6565

6666
/// # Effects
6767
///
68-
/// 1. Updates the user reward manager with the pool reward manager and accrues rewards
68+
/// 1. Finds the [UserRewardManager] for the reserve and obligation.
6969
/// 2. Withdraws all eligible rewards from [UserRewardManager].
7070
/// Eligible rewards are those that match the vault and user has earned any.
7171
/// 3. Transfers the withdrawn rewards to the user's token account.
@@ -134,11 +134,6 @@ pub(crate) fn process(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramR
134134

135135
let pool_reward_manager = accounts.reserve.pool_reward_manager_mut(position_kind);
136136

137-
// Syncs the pool reward manager with the user manager and accrues rewards.
138-
// If we wanted to optimize CU usage then we could make a dedicated update
139-
// function only for claiming rewards to avoid iterating twice over the rewards.
140-
user_reward_manager.update(pool_reward_manager, clock)?;
141-
142137
// 2.
143138

144139
let total_reward_amount = user_reward_manager.claim_rewards(

token-lending/program/tests/helpers/solend_program_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ mod cu_budgets {
7272
pub(super) const DEPOSIT: u32 = 50_012;
7373
pub(super) const DONATE_TO_RESERVE: u32 = 50_013;
7474
pub(super) const UPDATE_RESERVE_CONFIG: u32 = 30_014;
75-
pub(super) const DEPOSIT_RESERVE_LIQUIDITY_AND_OBLIGATION_COLLATERAL: u32 = 70_015;
75+
pub(super) const DEPOSIT_RESERVE_LIQUIDITY_AND_OBLIGATION_COLLATERAL: u32 = 100_015;
7676
pub(super) const REDEEM: u32 = 58_016;
7777
}
7878

token-lending/sdk/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ uint = "=0.9.1"
2323
assert_matches = "1.5.0"
2424
base64 = "0.13"
2525
log = "0.4.14"
26+
pretty_assertions = "1.4.1"
2627
proptest = "1.6"
2728
rand = "0.8.5"
2829
serde = ">=1.0.140"

token-lending/sdk/src/state/liquidity_mining.rs

Lines changed: 132 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ impl PoolRewardManager {
203203

204204
let start_time_secs = start_time_secs.max(clock.unix_timestamp as u64);
205205

206-
if start_time_secs <= end_time_secs {
206+
if start_time_secs >= end_time_secs {
207207
msg!("Pool reward must end after it starts");
208208
return Err(LendingError::MathOverflow.into());
209209
}
@@ -420,24 +420,7 @@ impl UserRewardManagers {
420420
self.0.last_mut().unwrap()
421421
};
422422

423-
msg!(
424-
"For reserve {} there are {} total shares. \
425-
User's previous position was at {} and new is at {}",
426-
reserve,
427-
pool_reward_manager.total_shares,
428-
user_reward_manager.share,
429-
new_share
430-
);
431-
432-
// This works even for migrations.
433-
// User's old share is 0 although it shouldn't be bcs they have borrowed
434-
// or deposited.
435-
// We only now attribute the share to the user which is fine, it's as if
436-
// they just now borrowed/deposited.
437-
pool_reward_manager.total_shares =
438-
pool_reward_manager.total_shares - user_reward_manager.share + new_share;
439-
440-
user_reward_manager.share = new_share;
423+
user_reward_manager.set_share(pool_reward_manager, new_share);
441424

442425
Ok(())
443426
}
@@ -455,17 +438,42 @@ impl UserRewardManager {
455438
}
456439
}
457440

441+
/// Sets new share value for this manager.
442+
fn set_share(&mut self, pool_reward_manager: &mut PoolRewardManager, new_share: u64) {
443+
msg!(
444+
"For reserve {} there are {} total shares. \
445+
User's previous position was at {} and new is at {}",
446+
self.reserve,
447+
pool_reward_manager.total_shares,
448+
self.share,
449+
new_share
450+
);
451+
452+
// This works even for migrations.
453+
// User's old share is 0 although it shouldn't be bcs they have borrowed
454+
// or deposited.
455+
// We only now attribute the share to the user which is fine, it's as if
456+
// they just now borrowed/deposited.
457+
pool_reward_manager.total_shares =
458+
pool_reward_manager.total_shares - self.share + new_share;
459+
460+
self.share = new_share;
461+
}
462+
458463
/// Claims all rewards that the user has earned.
459464
/// Returns how many tokens should be transferred to the user.
460465
///
461466
/// # Note
467+
///
462468
/// Errors if there is no pool reward with this vault.
463469
pub fn claim_rewards(
464470
&mut self,
465471
pool_reward_manager: &mut PoolRewardManager,
466472
vault: Pubkey,
467473
clock: &Clock,
468474
) -> Result<u64, ProgramError> {
475+
self.update(pool_reward_manager, clock)?;
476+
469477
let (pool_reward_index, pool_reward) = pool_reward_manager
470478
.pool_rewards
471479
.iter()
@@ -1024,9 +1032,12 @@ mod tests {
10241032
//! TODO: Calculate test coverage and add tests for missing branches.
10251033
10261034
use super::*;
1035+
use pretty_assertions::assert_eq;
10271036
use proptest::prelude::*;
10281037
use rand::Rng;
10291038

1039+
const MILLISECONDS_IN_DAY: u64 = 86_400_000;
1040+
10301041
fn pool_reward_manager_strategy() -> impl Strategy<Value = PoolRewardManager> {
10311042
(0..1u32).prop_perturb(|_, mut rng| PoolRewardManager::new_rand(&mut rng))
10321043
}
@@ -1109,9 +1120,110 @@ mod tests {
11091120
assert!(required_realloc <= MAX_REALLOC);
11101121
}
11111122

1123+
/// This tests replicates calculations from Suilend's
1124+
/// "test_pool_reward_manager_basic" test.
11121125
#[test]
11131126
fn it_tests_pool_reward_manager_basic() {
1114-
// TODO: rewrite Suilend "test_pool_reward_manager_basic" test
1127+
let usdc = Pubkey::new_unique(); // reserve pubkey
1128+
let slnd_vault = Pubkey::new_unique(); // where rewards are stored
1129+
1130+
let mut clock = Clock {
1131+
unix_timestamp: 0,
1132+
..Default::default()
1133+
};
1134+
1135+
let mut pool_reward_manager = PoolRewardManager::default();
1136+
{
1137+
// setup pool reward manager with one reward
1138+
1139+
pool_reward_manager
1140+
.add_pool_reward(
1141+
slnd_vault,
1142+
0,
1143+
20 * MILLISECONDS_IN_DAY,
1144+
100 * 1_000_000,
1145+
&clock,
1146+
)
1147+
.expect("It adds pool reward");
1148+
assert_eq!(
1149+
pool_reward_manager.pool_rewards[0],
1150+
PoolRewardSlot::Occupied(Box::new(PoolReward {
1151+
id: PoolRewardId(1),
1152+
vault: slnd_vault,
1153+
start_time_secs: 0,
1154+
duration_secs: 20 * MILLISECONDS_IN_DAY as u32,
1155+
total_rewards: 100 * 1_000_000,
1156+
cumulative_rewards_per_share: Decimal::zero(),
1157+
num_user_reward_managers: 0,
1158+
}))
1159+
);
1160+
}
1161+
1162+
let mut user_reward_manager_1 = UserRewardManager::new(usdc, PositionKind::Deposit, &clock);
1163+
{
1164+
// setup user reward manager with 100/100 shares
1165+
1166+
user_reward_manager_1
1167+
.populate(&mut pool_reward_manager, &clock)
1168+
.expect("It populates user reward manager");
1169+
user_reward_manager_1.set_share(&mut pool_reward_manager, 100);
1170+
}
1171+
1172+
{
1173+
// 1/4 of the reward time passes
1174+
clock.unix_timestamp = 5 * MILLISECONDS_IN_DAY as i64;
1175+
1176+
let claimed_slnd = user_reward_manager_1
1177+
.claim_rewards(&mut pool_reward_manager, slnd_vault, &clock)
1178+
.expect("It claims rewards");
1179+
assert_eq!(claimed_slnd, 25 * 1_000_000);
1180+
}
1181+
1182+
let mut user_reward_manager_2 = UserRewardManager::new(usdc, PositionKind::Deposit, &clock);
1183+
{
1184+
// setup user reward manager with 400/500 shares
1185+
1186+
user_reward_manager_2
1187+
.populate(&mut pool_reward_manager, &clock)
1188+
.expect("It populates user reward manager");
1189+
user_reward_manager_2.set_share(&mut pool_reward_manager, 400);
1190+
}
1191+
1192+
{
1193+
// 1/2 of the reward time passes
1194+
clock.unix_timestamp = 10 * MILLISECONDS_IN_DAY as i64;
1195+
1196+
let claimed_slnd = user_reward_manager_1
1197+
.claim_rewards(&mut pool_reward_manager, slnd_vault, &clock)
1198+
.expect("It claims rewards");
1199+
assert_eq!(claimed_slnd, 5 * 1_000_000);
1200+
1201+
let claimed_slnd = user_reward_manager_2
1202+
.claim_rewards(&mut pool_reward_manager, slnd_vault, &clock)
1203+
.expect("It claims rewards");
1204+
assert_eq!(claimed_slnd, 20 * 1_000_000);
1205+
}
1206+
1207+
{
1208+
// set both user reward managers to 250/500 shares
1209+
user_reward_manager_1.set_share(&mut pool_reward_manager, 250);
1210+
user_reward_manager_2.set_share(&mut pool_reward_manager, 250);
1211+
}
1212+
1213+
{
1214+
// the reward is finished
1215+
clock.unix_timestamp = 20 * MILLISECONDS_IN_DAY as i64;
1216+
1217+
let claimed_slnd = user_reward_manager_1
1218+
.claim_rewards(&mut pool_reward_manager, slnd_vault, &clock)
1219+
.expect("It claims rewards");
1220+
assert_eq!(claimed_slnd, 25 * 1_000_000);
1221+
1222+
let claimed_slnd = user_reward_manager_2
1223+
.claim_rewards(&mut pool_reward_manager, slnd_vault, &clock)
1224+
.expect("It claims rewards");
1225+
assert_eq!(claimed_slnd, 25 * 1_000_000);
1226+
}
11151227
}
11161228

11171229
#[test]

0 commit comments

Comments
 (0)