diff --git a/soroban/contracts/farming-pool/src/lib.rs b/soroban/contracts/farming-pool/src/lib.rs
index 5baf153..1a76e1b 100644
--- a/soroban/contracts/farming-pool/src/lib.rs
+++ b/soroban/contracts/farming-pool/src/lib.rs
@@ -2,6 +2,8 @@
mod types;
+use soroban_sdk::{contract, contractimpl, symbol_short, token, Address, Env};
+use types::{BoostConfig, DataKey, PoolError, Position, UserStake};
use soroban_sdk::{
contract, contractimpl, symbol_short, token, Address, Env,
};
@@ -26,8 +28,18 @@ fn bump_user(env: &Env, key: &DataKey) {
.extend_ttl(key, USER_TTL_THRESHOLD, USER_TTL_EXTEND_TO);
}
-fn get_admin(env: &Env) -> Address {
- env.storage().instance().get(&DataKey::Admin).unwrap()
+fn require_initialized(env: &Env) -> Result<(), PoolError> {
+ if !env.storage().instance().has(&DataKey::Admin) {
+ return Err(PoolError::NotInitialized);
+ }
+ Ok(())
+}
+
+fn get_admin(env: &Env) -> Result
{
+ env.storage()
+ .instance()
+ .get(&DataKey::Admin)
+ .ok_or(PoolError::NotInitialized)
}
fn get_global_multiplier(env: &Env) -> u32 {
@@ -44,8 +56,11 @@ fn get_credit_rate(env: &Env) -> i128 {
.unwrap_or(1)
}
-fn get_stake_token(env: &Env) -> Address {
- env.storage().instance().get(&DataKey::StakeToken).unwrap()
+fn get_stake_token(env: &Env) -> Result {
+ env.storage()
+ .instance()
+ .get(&DataKey::StakeToken)
+ .ok_or(PoolError::NotInitialized)
}
fn get_min_lock_period(env: &Env) -> u32 {
@@ -156,7 +171,8 @@ fn checkpoint(env: &Env, user: &Address, stake: &mut UserStake) {
let rate = get_credit_rate(env);
let current = env.ledger().sequence();
let elapsed = current.saturating_sub(stake.start_ledger);
- stake.credits_banked += compute_credits(stake.amount, allocation_pct, multiplier, rate, elapsed);
+ stake.credits_banked +=
+ compute_credits(stake.amount, allocation_pct, multiplier, rate, elapsed);
stake.start_ledger = current;
}
@@ -188,19 +204,28 @@ impl FarmingPool {
global_multiplier: u32,
credit_rate: i128,
min_lock_period: u32,
- ) {
+ ) -> Result<(), PoolError> {
if env.storage().instance().has(&DataKey::Admin) {
- panic!("already initialized");
+ return Err(PoolError::AlreadyInitialized);
}
assert!(global_multiplier >= 1, "multiplier must be >= 1");
assert!(credit_rate > 0, "credit_rate must be positive");
env.storage().instance().set(&DataKey::Admin, &admin);
- env.storage().instance().set(&DataKey::StakeToken, &stake_token);
- env.storage().instance().set(&DataKey::GlobalMultiplier, &global_multiplier);
- env.storage().instance().set(&DataKey::CreditRate, &credit_rate);
- env.storage().instance().set(&DataKey::MinLockPeriod, &min_lock_period);
+ env.storage()
+ .instance()
+ .set(&DataKey::StakeToken, &stake_token);
+ env.storage()
+ .instance()
+ .set(&DataKey::GlobalMultiplier, &global_multiplier);
+ env.storage()
+ .instance()
+ .set(&DataKey::CreditRate, &credit_rate);
+ env.storage()
+ .instance()
+ .set(&DataKey::MinLockPeriod, &min_lock_period);
bump_instance(&env);
+ Ok(())
}
// ── Lock / Unlock system ─────────────────────────────────────────────────
@@ -209,8 +234,9 @@ impl FarmingPool {
/// checkpointed first and the new amount is added to the existing position.
///
/// Emits a `("pool", "locked")` event with `(user, amount)`.
- pub fn lock_assets(env: Env, user: Address, amount: i128) {
+ pub fn lock_assets(env: Env, user: Address, amount: i128) -> Result<(), PoolError> {
user.require_auth();
+ require_initialized(&env)?;
assert!(!pool_is_paused(&env), "pool is paused");
assert!(amount > 0, "amount must be positive");
bump_instance(&env);
@@ -229,8 +255,12 @@ impl FarmingPool {
}
};
- token::TokenClient::new(&env, &get_stake_token(&env))
- .transfer(&user, &env.current_contract_address(), &amount);
+ let stake_token = get_stake_token(&env)?;
+ token::TokenClient::new(&env, &stake_token).transfer(
+ &user,
+ &env.current_contract_address(),
+ &amount,
+ );
set_position(&env, &user, &pos);
@@ -238,6 +268,7 @@ impl FarmingPool {
(symbol_short!("pool"), symbol_short!("locked")),
(user, amount),
);
+ Ok(())
}
/// Unlock `amount` tokens for the caller. The minimum lock period (in ledgers) must
@@ -245,8 +276,9 @@ impl FarmingPool {
/// remaining balance stays locked.
///
/// Emits a `("pool", "unlocked")` event with `(user, amount, total_credits)`.
- pub fn unlock_assets(env: Env, user: Address, amount: i128) {
+ pub fn unlock_assets(env: Env, user: Address, amount: i128) -> Result<(), PoolError> {
user.require_auth();
+ require_initialized(&env)?;
assert!(!pool_is_paused(&env), "pool is paused");
assert!(amount > 0, "amount must be positive");
bump_instance(&env);
@@ -265,8 +297,12 @@ impl FarmingPool {
let total_credits = pos.total_credits;
pos.amount -= amount;
- token::TokenClient::new(&env, &get_stake_token(&env))
- .transfer(&env.current_contract_address(), &user, &amount);
+ let stake_token = get_stake_token(&env)?;
+ token::TokenClient::new(&env, &stake_token).transfer(
+ &env.current_contract_address(),
+ &user,
+ &amount,
+ );
if pos.amount == 0 {
remove_position(&env, &user);
@@ -278,23 +314,29 @@ impl FarmingPool {
(symbol_short!("pool"), symbol_short!("unlocked")),
(user, amount, total_credits),
);
+ Ok(())
}
/// Return total credits for `user` (banked + currently accruing). Returns 0 if no position.
- pub fn calculate_credits(env: Env, user: Address) -> i128 {
+ pub fn calculate_credits(env: Env, user: Address) -> Result {
+ require_initialized(&env)?;
bump_instance(&env);
let Some(pos) = get_position(&env, &user) else {
- return 0;
+ return Ok(0);
};
let rate = get_credit_rate(&env);
- let elapsed = env.ledger().sequence().saturating_sub(pos.checkpoint_ledger);
- pos.total_credits + pos.amount * rate * elapsed as i128
+ let elapsed = env
+ .ledger()
+ .sequence()
+ .saturating_sub(pos.checkpoint_ledger);
+ Ok(pos.total_credits + pos.amount * rate * elapsed as i128)
}
/// Return the current position for `user`, or `None` if no position exists.
- pub fn get_user_position(env: Env, user: Address) -> Option {
+ pub fn get_user_position(env: Env, user: Address) -> Result