Skip to content

Commit 9a686e6

Browse files
Emmanuelluxuryblackghost
andauthored
Initial commit (#378)
* Initial commit * fix: remove invalid PoolStatus reference, use MarketState instead * Testing * Tessting --------- Co-authored-by: blackghost <blackghost@example.com>
1 parent 201bac1 commit 9a686e6

File tree

2 files changed

+108
-2
lines changed

2 files changed

+108
-2
lines changed

contract/contracts/predifi-contract/src/lib.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,14 @@ pub struct TreasuryWithdrawnEvent {
451451
pub recipient: Address,
452452
pub timestamp: u64,
453453
}
454+
#[contractevent(topics = ["refund_claimed"])]
455+
#[derive(Clone, Debug, Eq, PartialEq)]
456+
pub struct RefundClaimedEvent {
457+
pub pool_id: u64,
458+
pub user: Address,
459+
pub amount: i128,
460+
}
461+
454462
#[contractevent(topics = ["upgrade"])]
455463
#[derive(Clone, Debug, Eq, PartialEq)]
456464
pub struct UpgradeEvent {
@@ -1600,6 +1608,104 @@ impl PredifiContract {
16001608
Ok(winnings)
16011609
}
16021610

1611+
/// Claim a refund from a canceled pool. Returns the refunded amount.
1612+
/// Only available for canceled pools. User receives their full original stake.
1613+
///
1614+
/// PRE: pool.state = Canceled, user has a prediction on the pool
1615+
/// POST: HasClaimed(user, pool) = true (INV-3), user receives full stake amount
1616+
///
1617+
/// # Arguments
1618+
/// * `user` - Address claiming the refund (must provide auth)
1619+
/// * `pool_id` - ID of the canceled pool
1620+
///
1621+
/// # Returns
1622+
/// Ok(amount) - Refund successfully claimed, returns refunded amount
1623+
/// Err(PredifiError) - Operation failed with specific error code
1624+
///
1625+
/// # Errors
1626+
/// - `InvalidPoolState` if pool doesn't exist or is not canceled
1627+
/// - `InsufficientBalance` if user has no prediction or zero stake
1628+
/// - `AlreadyClaimed` if user already claimed refund for this pool
1629+
/// - `PoolNotResolved` if pool is resolved (not canceled)
1630+
#[allow(clippy::needless_borrows_for_generic_args)]
1631+
pub fn claim_refund(env: Env, user: Address, pool_id: u64) -> Result<i128, PredifiError> {
1632+
Self::require_not_paused(&env);
1633+
user.require_auth();
1634+
1635+
Self::enter_reentrancy_guard(&env);
1636+
1637+
// --- CHECKS ---
1638+
1639+
let pool_key = DataKey::Pool(pool_id);
1640+
let pool: Pool = match env.storage().persistent().get(&pool_key) {
1641+
Some(p) => p,
1642+
None => {
1643+
Self::exit_reentrancy_guard(&env);
1644+
return Err(PredifiError::InvalidPoolState);
1645+
}
1646+
};
1647+
Self::extend_persistent(&env, &pool_key);
1648+
1649+
// Verify pool is canceled
1650+
if pool.state != MarketState::Canceled {
1651+
Self::exit_reentrancy_guard(&env);
1652+
return Err(PredifiError::InvalidPoolState);
1653+
}
1654+
1655+
// Check if user already claimed refund
1656+
let claimed_key = DataKey::Claimed(user.clone(), pool_id);
1657+
if env.storage().persistent().has(&claimed_key) {
1658+
Self::exit_reentrancy_guard(&env);
1659+
return Err(PredifiError::AlreadyClaimed);
1660+
}
1661+
1662+
// Get user's prediction
1663+
let pred_key = DataKey::Pred(user.clone(), pool_id);
1664+
let prediction: Option<Prediction> = env.storage().persistent().get(&pred_key);
1665+
1666+
if env.storage().persistent().has(&pred_key) {
1667+
Self::extend_persistent(&env, &pred_key);
1668+
}
1669+
1670+
let prediction = match prediction {
1671+
Some(p) => p,
1672+
None => {
1673+
Self::exit_reentrancy_guard(&env);
1674+
return Err(PredifiError::InsufficientBalance);
1675+
}
1676+
};
1677+
1678+
// Verify user has a non-zero stake
1679+
if prediction.amount <= 0 {
1680+
Self::exit_reentrancy_guard(&env);
1681+
return Err(PredifiError::InsufficientBalance);
1682+
}
1683+
1684+
// --- EFFECTS ---
1685+
1686+
// Mark as claimed immediately to prevent re-entrancy (INV-3)
1687+
env.storage().persistent().set(&claimed_key, &true);
1688+
Self::extend_persistent(&env, &claimed_key);
1689+
1690+
let refund_amount = prediction.amount;
1691+
1692+
// --- INTERACTIONS ---
1693+
1694+
let token_client = token::Client::new(&env, &pool.token);
1695+
token_client.transfer(&env.current_contract_address(), &user, &refund_amount);
1696+
1697+
Self::exit_reentrancy_guard(&env);
1698+
1699+
RefundClaimedEvent {
1700+
pool_id,
1701+
user: user.clone(),
1702+
amount: refund_amount,
1703+
}
1704+
.publish(&env);
1705+
1706+
Ok(refund_amount)
1707+
}
1708+
16031709
/// Update the stake limits for an active pool. Caller must have Operator role (1).
16041710
/// PRE: pool.state = Active, operator has role 1
16051711
/// POST: pool.min_stake and pool.max_stake updated

contract/contracts/predifi-contract/src/test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ mod dummy_access_control {
2828
}
2929
}
3030

31-
const ROLE_ADMIN: u32 = 0;
32-
const ROLE_OPERATOR: u32 = 1;
31+
const ROLE_ADMIN: u32 = 0; // i am testing this
32+
const ROLE_OPERATOR: u32 = 1; // i am testing this the second one
3333
const ROLE_ORACLE: u32 = 3;
3434

3535
fn setup(

0 commit comments

Comments
 (0)