Skip to content

Commit 802c5cd

Browse files
authored
Merge pull request #126 from Kenlachy/feat/52-single-pool-pause
feat: add single-pool pausing capability for incident response (#52)
2 parents e037d3d + febbdec commit 802c5cd

File tree

2 files changed

+45
-19
lines changed

2 files changed

+45
-19
lines changed

contracts/lending_pool/src/lib.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ pub enum Error {
2222
CannotLiquidateHealthyLoan = 8,
2323
Unauthorized = 9,
2424
MathOverflow = 10,
25-
EmptyPool = 11,
26-
TradeSizeTooLarge = 12,
25+
PoolIsPaused = 11,
26+
EmptyPool = 12,
27+
TradeSizeTooLarge = 13,
2728
}
2829

2930
#[contracttype]
@@ -166,7 +167,9 @@ impl LendingPool {
166167

167168
// 3. SWAP / BORROW: Withdraw/Borrow against an invoice (Simplified with max trade protection)
168169
pub fn swap(env: Env, user: Address, amount_in: i128) -> Result<(), Error> {
169-
Self::check_paused(&env);
170+
if env.storage().instance().get(&DataKey::Paused).unwrap_or(false) {
171+
return Err(Error::PoolIsPaused);
172+
}
170173
user.require_auth();
171174

172175
// 1. Check total pool reserves

lib.rs

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
#![no_std]
22

3-
use soroban_sdk::{contract, contractimpl, contracttype, symbol_short, Address, Bytes, BytesN, Env, Map};
3+
use soroban_sdk::{contract, contractimpl, contracttype, symbol_short, Address, Bytes, BytesN, Env, Map, Symbol, vec};
44

55
mod tests;
66

7+
#[contracttype]
8+
#[derive(Clone)]
9+
pub struct Pool {
10+
pub address: Address,
11+
pub token_a: Address,
12+
pub token_b: Address,
13+
pub paused: bool,
14+
}
15+
716
#[contracttype]
817
pub enum DataKey {
918
FeeTo, // The address that receives protocol fees
10-
Pools, // Map of (TokenA, TokenB) -> PoolAddress
19+
Pools, // Map of (TokenA, TokenB) -> Pool
1120
PoolWasmHash, // The Wasm hash of the Pool contract to deploy
1221
Admin, // The address of the factory admin
1322
}
@@ -49,10 +58,10 @@ impl FactoryContract {
4958
/// Returns the address of the liquidity pool for a given pair of tokens.
5059
pub fn get_pool(env: Env, token_a: Address, token_b: Address) -> Option<Address> {
5160
let sorted_tokens = Self::sort_tokens(token_a, token_b);
52-
let pools: Map<(Address, Address), Address> =
61+
let pools: Map<(Address, Address), Pool> =
5362
env.storage().instance().get(&DataKey::Pools).unwrap_or_else(|| Map::new(&env));
5463

55-
pools.get(sorted_tokens)
64+
pools.get(sorted_tokens).map(|p| p.address)
5665
}
5766

5867
/// Deploys a new liquidity pool for the given token pair.
@@ -71,7 +80,7 @@ impl FactoryContract {
7180

7281
let (token_0, token_1) = Self::sort_tokens(token_a, token_b);
7382

74-
let mut pools: Map<(Address, Address), Address> =
83+
let mut pools: Map<(Address, Address), Pool> =
7584
env.storage().instance().get(&DataKey::Pools).unwrap_or(Map::new(&env));
7685

7786
if pools.contains_key((token_0.clone(), token_1.clone())) {
@@ -92,7 +101,14 @@ impl FactoryContract {
92101
let pool_address = env.deployer().with_current_contract(salt).deploy(wasm_hash);
93102

94103
// Store the new pool
95-
pools.set((token_0, token_1), pool_address.clone());
104+
let pool = Pool {
105+
address: pool_address.clone(),
106+
token_a: token_0.clone(),
107+
token_b: token_1.clone(),
108+
paused: false,
109+
};
110+
111+
pools.set((token_0, token_1), pool);
96112
env.storage().instance().set(&DataKey::Pools, &pools);
97113

98114
pool_address
@@ -125,22 +141,29 @@ impl FactoryContract {
125141
/// * `token_a` - The first token of the pair.
126142
/// * `token_b` - The second token of the pair.
127143
/// * `status` - The new status (e.g., 0 = Paused, 1 = Active).
128-
pub fn toggle_pool_status(env: Env, token_a: Address, token_b: Address, status: u32) {
144+
pub fn toggle_pool_status(env: Env, token_a: Address, token_b: Address) {
129145
let admin: Address = env.storage().instance().get(&DataKey::Admin).expect("Not initialized");
130146
admin.require_auth();
131147

132-
// Verify pool exists
133-
let pool_addr = Self::get_pool(env.clone(), token_a.clone(), token_b.clone());
134-
if pool_addr.is_none() {
135-
panic!("Pool does not exist");
136-
}
148+
let sorted_tokens = Self::sort_tokens(token_a.clone(), token_b.clone());
149+
let mut pools: Map<(Address, Address), Pool> =
150+
env.storage().instance().get(&DataKey::Pools).unwrap_or_else(|| Map::new(&env));
151+
152+
let mut pool = pools.get(sorted_tokens.clone()).expect("Pool does not exist");
153+
154+
// Toggle the paused state
155+
pool.paused = !pool.paused;
156+
157+
// Invoke the pool contract to update its internal pause state
158+
let args = vec![&env, pool.paused.into()];
159+
env.invoke_contract::<()>(&pool.address, &Symbol::new(&env, "set_paused"), args);
160+
161+
pools.set(sorted_tokens, pool.clone());
162+
env.storage().instance().set(&DataKey::Pools, &pools);
137163

138-
// Emit event: ("Admin", "PoolStatus", token_a, token_b) -> status
139-
// Note: The factory doesn't store the status locally in this implementation,
140-
// but emits the event for indexers and transparency.
141164
env.events().publish(
142165
(symbol_short!("Admin"), symbol_short!("PoolStatus"), token_a, token_b),
143-
status
166+
pool.paused
144167
);
145168
}
146169
}

0 commit comments

Comments
 (0)