diff --git a/soroban/contracts/factory/src/lib.rs b/soroban/contracts/factory/src/lib.rs index d18fa67..94ed66e 100644 --- a/soroban/contracts/factory/src/lib.rs +++ b/soroban/contracts/factory/src/lib.rs @@ -666,6 +666,31 @@ impl Factory { record.wasm_hash = new_wasm_hash.clone(); env.storage().persistent().set(&key, &record); + let old_wasm_key = DataKey::PoolsByWasmHash(old_hash.clone()); + if let Some(mut old_pool_ids) = env + .storage() + .persistent() + .get::>(&old_wasm_key) + { + let mut new_old_ids: Vec = vec![&env]; + for id in old_pool_ids.iter() { + if id != pool_id { + new_old_ids.push_back(id); + } + } + env.storage().persistent().set(&old_wasm_key, &new_old_ids); + } + + let new_wasm_key = DataKey::PoolsByWasmHash(new_wasm_hash.clone()); + let mut new_pool_ids: Vec = env + .storage() + .persistent() + .get(&new_wasm_key) + .unwrap_or_else(|| vec![&env]); + new_pool_ids.push_back(pool_id); + env.storage().persistent().set(&new_wasm_key, &new_pool_ids); + bump_wasm_pools(&env, &new_wasm_hash); + env.storage() .instance() .set(&DataKey::UpgradeCount, &read_upgrade_count(&env).saturating_add(1)); diff --git a/soroban/contracts/factory/src/types.rs b/soroban/contracts/factory/src/types.rs index 43d3311..a8ce040 100644 --- a/soroban/contracts/factory/src/types.rs +++ b/soroban/contracts/factory/src/types.rs @@ -1,4 +1,4 @@ -use soroban_sdk::{contracterror, contracttype, Address, Vec}; +use soroban_sdk::{contracterror, contracttype, Address, BytesN, Vec}; /// Storage keys used by the factory contract. #[contracttype] diff --git a/soroban/contracts/farming-pool/src/lib.rs b/soroban/contracts/farming-pool/src/lib.rs index c6ca656..4eb3a83 100644 --- a/soroban/contracts/farming-pool/src/lib.rs +++ b/soroban/contracts/farming-pool/src/lib.rs @@ -373,6 +373,102 @@ fn add_total_distributed_credits(env: &Env, amount: i128) { ); } +fn add_total_credits(env: &Env, amount: i128) { + let total = env + .storage() + .instance() + .get::(&DataKey::TotalCredits) + .unwrap_or(0); + env.storage().instance().set( + &DataKey::TotalCredits, + &total.checked_add(amount).expect("total credits overflow"), + ); +} + +fn read_total_deposits(env: &Env) -> i128 { + env.storage() + .instance() + .get(&DataKey::TotalDeposits) + .unwrap_or(0) +} + +fn add_total_deposits(env: &Env, amount: i128) { + let total = env + .storage() + .instance() + .get::(&DataKey::TotalDeposits) + .unwrap_or(0); + env.storage().instance().set( + &DataKey::TotalDeposits, + &total.checked_add(amount).expect("total deposits overflow"), + ); +} + +fn read_total_withdrawals(env: &Env) -> i128 { + env.storage() + .instance() + .get(&DataKey::TotalWithdrawals) + .unwrap_or(0) +} + +fn add_total_withdrawals(env: &Env, amount: i128) { + let total = env + .storage() + .instance() + .get::(&DataKey::TotalWithdrawals) + .unwrap_or(0); + env.storage().instance().set( + &DataKey::TotalWithdrawals, + &total.checked_add(amount).expect("total withdrawals overflow"), + ); +} + +fn read_total_boost_allocations(env: &Env) -> u64 { + env.storage() + .instance() + .get(&DataKey::TotalBoostAlloc) + .unwrap_or(0) +} + +fn add_total_boost_allocation(env: &Env, delta: i64) { + let total = read_total_boost_allocations(env); + if delta >= 0 { + env.storage().instance().set( + &DataKey::TotalBoostAlloc, + &total.checked_add(delta as u64).expect("total boost alloc overflow"), + ); + } else { + let sub = (-delta) as u64; + env.storage().instance().set( + &DataKey::TotalBoostAlloc, + &total.checked_sub(sub).expect("total boost alloc underflow"), + ); + } +} + +fn read_boost_user_count(env: &Env) -> u32 { + env.storage() + .instance() + .get(&DataKey::BoostUserCount) + .unwrap_or(0) +} + +fn increment_boost_user_count(env: &Env) { + let count = read_boost_user_count(env); + env.storage() + .instance() + .set(&DataKey::BoostUserCount, &(count + 1)); +} + +fn decrement_boost_user_count(env: &Env) { + let count = read_boost_user_count(env); + if count > 0 { + env.storage() + .instance() + .set(&DataKey::BoostUserCount, &(count - 1)); + } +} + fn get_position(env: &Env, user: &Address) -> Option { let key = DataKey::UserPosition(user.clone()); let value: Option = env.storage().persistent().get(&key); @@ -621,6 +717,12 @@ impl FarmingPool { env.storage() .instance() .set(&DataKey::TotalCredits, &0i128); + env.storage() + .instance() + .set(&DataKey::TotalDeposits, &0i128); + env.storage() + .instance() + .set(&DataKey::TotalWithdrawals, &0i128); env.storage() .instance() .set(&DataKey::SchemaVersion, &SCHEMA_VERSION); @@ -1098,6 +1200,8 @@ impl FarmingPool { return Err(PoolError::NoActiveStake); } + add_total_withdrawals(&env, total_returned); + // Bank the position and stake credits as separate totals so each staking // system's accrual history survives even when a user held both (#145). if position_credits > 0 || stake_credits > 0 { @@ -1372,6 +1476,7 @@ impl FarmingPool { increment_staked_user_count(&env); } add_total_staked(&env, amount); + add_total_deposits(&env, amount); // Pull tokens from caller into the contract. let stake_token = get_stake_token(&env)?; @@ -1425,6 +1530,7 @@ impl FarmingPool { } increment_unstake_count(&env); subtract_total_staked(&env, stake.amount); + add_total_withdrawals(&env, stake.amount); Ok(total_credits) } @@ -1443,6 +1549,17 @@ impl FarmingPool { set_user_stake(&env, &user, &stake); } + let old_alloc: u32 = get_user_boost(&env, &user).unwrap_or(0); + if old_alloc == 0 { + increment_boost_user_count(&env); + add_total_boost_allocation(&env, allocation_pct as i64); + } else { + let delta = allocation_pct as i64 - old_alloc as i64; + if delta != 0 { + add_total_boost_allocation(&env, delta); + } + } + let key = DataKey::UserBoost(user.clone()); if !env.storage().persistent().has(&key) { increment_boost_count(&env); @@ -1718,6 +1835,30 @@ impl FarmingPool { .unwrap_or(0)) } + /// Return the running total of all tokens deposited into the pool. + /// + /// Incremented by `stake` and `lock_assets` with the amount transferred in. + /// Tracks cumulative inflow for protocol flow analytics; compare with + /// `total_withdrawals` to derive net flow and with `total_staked` to + /// reconcile current TVL against historical turnover. + pub fn total_deposits(env: Env) -> Result { + require_initialized(&env)?; + bump_instance(&env); + Ok(read_total_deposits(&env)) + } + + /// Return the running total of all tokens withdrawn from the pool. + /// + /// Incremented by `unstake`, `unlock_assets`, and `emergency_withdraw` + /// with the amount transferred out. Tracks cumulative outflow for + /// protocol flow analytics; compare with `total_deposits` to derive + /// net flow. + pub fn total_withdrawals(env: Env) -> Result { + require_initialized(&env)?; + bump_instance(&env); + Ok(read_total_withdrawals(&env)) + } + /// Return the count of currently staked unique users in the pool. pub fn staked_user_count(env: Env) -> Result { require_initialized(&env)?; diff --git a/soroban/contracts/vesting-wallet/src/lib.rs b/soroban/contracts/vesting-wallet/src/lib.rs index a070724..f3b3754 100644 --- a/soroban/contracts/vesting-wallet/src/lib.rs +++ b/soroban/contracts/vesting-wallet/src/lib.rs @@ -337,6 +337,13 @@ impl VestingWallet { Ok(compute_vested(&env)? - get_released(&env)) } + /// Return whether the vesting schedule is revocable by admin. + pub fn revocable(env: Env) -> Result { + require_initialized(&env)?; + bump_instance(&env); + Ok(is_revocable(&env)) + } + /// Return the full vesting schedule parameters in a single call. /// /// Frontends need `beneficiary`, `token`, `total_amount`, `start_ledger`,