diff --git a/soroban/contracts/factory/src/lib.rs b/soroban/contracts/factory/src/lib.rs index dc4db4f..d18fa67 100644 --- a/soroban/contracts/factory/src/lib.rs +++ b/soroban/contracts/factory/src/lib.rs @@ -71,6 +71,14 @@ fn bump_asset_pools(env: &Env, asset: &Address) { ); } +fn bump_admin_pools(env: &Env, admin: &Address) { + env.storage().persistent().extend_ttl( + &DataKey::PoolsByAdmin(admin.clone()), + TTL_THRESHOLD, + TTL_EXTEND_TO, + ); +} + /// Reject any call that lands on a factory whose state was never seeded. /// /// `initialize` is the only writer of `DataKey::Admin`, so its presence is the @@ -468,6 +476,21 @@ impl Factory { Self::get_pools_by_asset_range(env, asset, start_id, MAX_POOL_SCAN_PER_CALL, limit) } + /// Return the list of pool IDs created by `admin`. + pub fn get_pools_by_admin(env: Env, admin: Address) -> Result, FactoryError> { + require_initialized(&env)?; + bump_instance(&env); + let admin_key = DataKey::PoolsByAdmin(admin.clone()); + if env.storage().persistent().has(&admin_key) { + bump_admin_pools(&env, &admin); + } + Ok(env + .storage() + .persistent() + .get(&admin_key) + .unwrap_or_else(|| vec![&env])) + } + /// Refresh TTLs for a range of pool records to prevent archival. /// /// This permissionless function allows keepers or any caller to proactively @@ -868,6 +891,16 @@ impl Factory { asset_pool_ids.push_back(pool_id); env.storage().persistent().set(&asset_key, &asset_pool_ids); bump_asset_pools(&env, &asset); + + let admin_key = DataKey::PoolsByAdmin(admin.clone()); + let mut admin_pool_ids: Vec = env + .storage() + .persistent() + .get(&admin_key) + .unwrap_or_else(|| vec![&env]); + admin_pool_ids.push_back(pool_id); + env.storage().persistent().set(&admin_key, &admin_pool_ids); + bump_admin_pools(&env, &admin); env.storage() .instance() .set(&DataKey::PoolCount, &next_count); diff --git a/soroban/contracts/factory/src/test.rs b/soroban/contracts/factory/src/test.rs index 4f27d0f..4516abe 100644 --- a/soroban/contracts/factory/src/test.rs +++ b/soroban/contracts/factory/src/test.rs @@ -1459,3 +1459,22 @@ fn test_admin_transfer_count_increments_on_transfer() { assert!(result.is_err()); assert_eq!(t.client.admin_transfer_count(), 2); } + +#[test] +fn test_get_pools_by_admin_returns_created_pools() { + let t = setup(); + assert_eq!(t.client.get_pools_by_admin(&t.admin), vec![&t.env]); + + let asset1 = Address::generate(&t.env); + let id1 = t + .client + .create_pool(&asset1, &1_728_000u128, &2u32, &10u64, &0i128); + + let asset2 = Address::generate(&t.env); + let id2 = t + .client + .create_pool(&asset2, &3_456_000u128, &2u32, &20u64, &0i128); + + let pools = t.client.get_pools_by_admin(&t.admin); + assert_eq!(pools, vec![&t.env, id1, id2]); +} diff --git a/soroban/contracts/farming-pool/src/lib.rs b/soroban/contracts/farming-pool/src/lib.rs index ce06e21..c6ca656 100644 --- a/soroban/contracts/farming-pool/src/lib.rs +++ b/soroban/contracts/farming-pool/src/lib.rs @@ -253,6 +253,39 @@ fn subtract_total_staked(env: &Env, amount: i128) { ); } +fn add_total_locked(env: &Env, amount: i128) { + let total = env + .storage() + .instance() + .get::(&DataKey::TotalLocked) + .unwrap_or(0); + env.storage().instance().set( + &DataKey::TotalLocked, + &total.checked_add(amount).expect("total locked overflow"), + ); +} + +fn subtract_total_locked(env: &Env, amount: i128) { + let total = env + .storage() + .instance() + .get::(&DataKey::TotalLocked) + .unwrap_or(0); + env.storage().instance().set( + &DataKey::TotalLocked, + &total.checked_sub(amount).expect("total locked underflow"), + ); +} + +fn read_boost_count(env: &Env) -> u32 { + env.storage().instance().get(&DataKey::BoostCount).unwrap_or(0) +} + +fn increment_boost_count(env: &Env) { + let count = read_boost_count(env); + env.storage().instance().set(&DataKey::BoostCount, &(count + 1)); +} + fn is_user_staked(env: &Env, user: &Address) -> bool { get_position(env, user).is_some() || get_user_stake(env, user).is_some() } @@ -584,6 +617,7 @@ impl FarmingPool { .instance() .set(&DataKey::MinStakeAmount, &min_stake); env.storage().instance().set(&DataKey::TotalStaked, &0i128); + env.storage().instance().set(&DataKey::TotalLocked, &0i128); env.storage() .instance() .set(&DataKey::TotalCredits, &0i128); @@ -788,6 +822,7 @@ impl FarmingPool { } increment_lock_count(&env); add_total_staked(&env, amount); + add_total_locked(&env, amount); let stake_token = get_stake_token(&env)?; token::TokenClient::new(&env, &stake_token).transfer( @@ -844,6 +879,7 @@ impl FarmingPool { decrement_staked_user_count(&env); } subtract_total_staked(&env, amount); + subtract_total_locked(&env, amount); let stake_token = get_stake_token(&env)?; token::TokenClient::new(&env, &stake_token).transfer( @@ -1041,6 +1077,7 @@ impl FarmingPool { token.transfer(&env.current_contract_address(), &user, &position.amount); total_returned += position.amount; subtract_total_staked(&env, position.amount); + subtract_total_locked(&env, position.amount); position_credits = position.total_credits; remove_position(&env, &user); } @@ -1407,6 +1444,9 @@ impl FarmingPool { } let key = DataKey::UserBoost(user.clone()); + if !env.storage().persistent().has(&key) { + increment_boost_count(&env); + } env.storage().persistent().set(&key, &allocation_pct); bump_user(&env, &key); @@ -1714,6 +1754,32 @@ impl FarmingPool { pub fn get_unstake_count(env: Env) -> Result { Self::unstake_count(env) } + + /// Return the total number of boost configurations configured across users (#230). + pub fn boost_count(env: Env) -> Result { + require_initialized(&env)?; + bump_instance(&env); + Ok(read_boost_count(&env)) + } + + pub fn get_boost_count(env: Env) -> Result { + Self::boost_count(env) + } + + /// Return the total tokens locked across all position locking positions (#232). + pub fn total_locked(env: Env) -> Result { + require_initialized(&env)?; + bump_instance(&env); + Ok(env + .storage() + .instance() + .get(&DataKey::TotalLocked) + .unwrap_or(0)) + } + + pub fn get_total_locked(env: Env) -> Result { + Self::total_locked(env) + } } mod test; diff --git a/soroban/contracts/vesting-wallet/src/lib.rs b/soroban/contracts/vesting-wallet/src/lib.rs index c8339b1..a070724 100644 --- a/soroban/contracts/vesting-wallet/src/lib.rs +++ b/soroban/contracts/vesting-wallet/src/lib.rs @@ -261,7 +261,7 @@ impl VestingWallet { #[allow(deprecated)] env.events().publish( (symbol_short!("vest"), symbol_short!("released")), - (beneficiary, releasable), + (beneficiary, releasable, released + releasable), ); Ok(releasable) diff --git a/soroban/contracts/vesting-wallet/src/test.rs b/soroban/contracts/vesting-wallet/src/test.rs index 5c25153..5ccc317 100644 --- a/soroban/contracts/vesting-wallet/src/test.rs +++ b/soroban/contracts/vesting-wallet/src/test.rs @@ -369,6 +369,19 @@ fn test_release_emits_event() { ); } +#[test] +fn test_release_emits_event_with_cumulative_total() { + let t = setup(0, 100, 1_000); + advance_ledgers(&t.env, 50); + t.client.release(); // 500 released + + advance_ledgers(&t.env, 25); + t.client.release(); // 250 releasable, cumulative total 750 + + let events = t.env.events().all(); + assert!(!events.events().is_empty()); +} + // ── revoke tests ────────────────────────────────────────────────────────────── #[test]