From b83adddde1439f1db853eb05f966a32392e83620 Mon Sep 17 00:00:00 2001 From: Samaro1 Date: Mon, 27 Jul 2026 19:30:20 +0100 Subject: [PATCH] Add settlement_status entrypoint for narrow settlement status reads Implements settlement_status(env, id) -> Result as described in issue #112. - Added settlement_status entrypoint in src/lib.rs (near settlement/settlement_exists) that returns just the SettlementStatus field via storage::get_settlement, or Error::SettlementNotFound for a missing id. - Added src/test.rs coverage: tests for Pending, Executed, Cancelled, Expired variants and for a missing id (SettlementNotFound). - Updated the README settlement table to document the new settlement_status(id) view. Relates to: #112 --- README.md | 1 + src/lib.rs | 16 ++++++++++ src/storage.rs | 20 ++++++------ src/test.rs | 84 +++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 103 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index f0cd845..663cda5 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,7 @@ cancel_expired_settlement(id) – Reclaim a timed-out pending settlement's liqui set_settlement_expiry_ledgers(ledgers) admin Set the ledger window after which a pending settlement may be reclaimed (0 disables) settlement_expiry_ledgers() – Read the settlement expiry window in ledgers settlement_exists(id) – Check whether a settlement exists +settlement_status(id) – Read only the status of a settlement, or SettlementNotFound if missing is_settlement_pending(id) – Check whether a settlement exists and its status is Pending is_settlement_expired(id) – Check whether a pending settlement has passed the expiry window, without reclaiming it settlement(id) – Read a settlement record diff --git a/src/lib.rs b/src/lib.rs index 3871adc..60afafd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1074,6 +1074,22 @@ impl AnchornetContract { storage::get_settlement(&env, id).is_some() } + /// Returns the [`SettlementStatus`] of the settlement with `id`, or + /// [`Error::SettlementNotFound`] if the settlement does not exist. + /// + /// Unlike [`settlement`](Self::settlement), which returns the full + /// [`Settlement`] struct, this entrypoint projects out only the + /// `status` field. For keeper polling loops (e.g. deciding whether to + /// call [`execute_settlement`](Self::execute_settlement) or + /// [`cancel_expired_settlement`](Self::cancel_expired_settlement)), + /// the narrower return type reduces cross-contract call payload size + /// and keeps settlement-state checks auditable in one place. + pub fn settlement_status(env: Env, id: u64) -> Result { + Ok(storage::get_settlement(&env, id) + .ok_or(Error::SettlementNotFound)? + .status) + } + /// Returns `true` if a settlement with `id` exists and its status is /// [`SettlementStatus::Pending`]. Returns `false` (not an error) for a /// missing id or any terminal-state settlement. Designed as a minimal-payload diff --git a/src/storage.rs b/src/storage.rs index 02252fb..b3aab92 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -471,17 +471,17 @@ pub fn set_max_settlement_amount(env: &Env, asset: &Symbol, amount: i128) { extend(env, &key); } - /// Removes the minimum liquidity floor for `asset`, reverting to unset state. - pub fn clear_min_liquidity(env: &Env, asset: &Symbol) { - let key = DataKey::MinLiquidity(asset.clone()); - env.storage().persistent().remove(&key); - } +/// Removes the minimum liquidity floor for `asset`, reverting to unset state. +pub fn clear_min_liquidity(env: &Env, asset: &Symbol) { + let key = DataKey::MinLiquidity(asset.clone()); + env.storage().persistent().remove(&key); +} - /// Removes the maximum settlement amount for `asset`, reverting to unset state. - pub fn clear_max_settlement_amount(env: &Env, asset: &Symbol) { - let key = DataKey::MaxSettlementAmount(asset.clone()); - env.storage().persistent().remove(&key); - } +/// Removes the maximum settlement amount for `asset`, reverting to unset state. +pub fn clear_max_settlement_amount(env: &Env, asset: &Symbol) { + let key = DataKey::MaxSettlementAmount(asset.clone()); + env.storage().persistent().remove(&key); +} /// Reads the per-asset fee override for `asset`, if one has been configured. /// diff --git a/src/test.rs b/src/test.rs index d02954e..c956d0a 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1649,6 +1649,58 @@ fn test_settlement_exists() { assert!(client.settlement_exists(&id)); } +#[test] +fn test_settlement_status_pending() { + let env = Env::default(); + let (client, _admin, anchor, asset) = funded(&env, 1_000); + let id = client.open_settlement(&anchor, &asset, &100); + + assert_eq!(client.settlement_status(&id), SettlementStatus::Pending); +} + +#[test] +fn test_settlement_status_executed() { + let env = Env::default(); + let (client, _admin, anchor, asset) = funded(&env, 1_000); + client.set_fee(&100); // 1% + let id = client.open_settlement(&anchor, &asset, &100); + client.execute_settlement(&id); + + assert_eq!(client.settlement_status(&id), SettlementStatus::Executed); +} + +#[test] +fn test_settlement_status_cancelled() { + let env = Env::default(); + let (client, _admin, anchor, asset) = funded(&env, 1_000); + let id = client.open_settlement(&anchor, &asset, &100); + client.cancel_settlement(&id); + + assert_eq!(client.settlement_status(&id), SettlementStatus::Cancelled); +} + +#[test] +fn test_settlement_status_expired() { + let env = Env::default(); + let (client, _admin, anchor, asset) = funded(&env, 1_000); + client.set_settlement_expiry_ledgers(&10); + let id = client.open_settlement(&anchor, &asset, &100); // opened_at == 0 + + env.ledger().set_sequence_number(10); + client.cancel_expired_settlement(&id); + + assert_eq!(client.settlement_status(&id), SettlementStatus::Expired); +} + +#[test] +fn test_settlement_status_not_found() { + let env = Env::default(); + let (client, _admin, _anchor, _asset) = funded(&env, 1_000); + + let err = client.try_settlement_status(&99).err().unwrap().unwrap(); + assert_eq!(err, Error::SettlementNotFound); +} + #[test] fn test_is_settlement_pending() { let env = Env::default(); @@ -6394,11 +6446,17 @@ fn test_pool_exists_true_after_provide_liquidity() { client.initialize(&admin); client.register_anchor(&anchor); - assert!(!client.pool_exists(&asset), "must be false before any liquidity"); + assert!( + !client.pool_exists(&asset), + "must be false before any liquidity" + ); client.provide_liquidity(&anchor, &asset, &1_000); - assert!(client.pool_exists(&asset), "must be true after provide_liquidity"); + assert!( + client.pool_exists(&asset), + "must be true after provide_liquidity" + ); } /// `pool_exists` returns `true` once `provide_liquidity_multi` has touched the @@ -6418,7 +6476,10 @@ fn test_pool_exists_true_after_provide_liquidity_multi() { assert!(!client.pool_exists(&usdc)); assert!(!client.pool_exists(&eurc)); - client.provide_liquidity_multi(&anchor, &vec![&env, (usdc.clone(), 100), (eurc.clone(), 200)]); + client.provide_liquidity_multi( + &anchor, + &vec![&env, (usdc.clone(), 100), (eurc.clone(), 200)], + ); assert!(client.pool_exists(&usdc)); assert!(client.pool_exists(&eurc)); @@ -6431,7 +6492,10 @@ fn test_pool_exists_true_after_full_withdrawal() { let env = Env::default(); let (client, _admin, anchor, asset) = funded(&env, 1_000); - assert!(client.pool_exists(&asset), "precondition: pool exists after funding"); + assert!( + client.pool_exists(&asset), + "precondition: pool exists after funding" + ); client.withdraw_all_liquidity(&anchor, &asset); @@ -6924,8 +6988,10 @@ fn test_has_asset_fee_override_true_after_set_zero_bps() { // Explicit 0 bps override — distinct from no override. client.set_asset_fee(&asset, &0); - assert!(client.has_asset_fee_override(&asset), - "an explicit 0 bps override must be distinguishable from no override"); + assert!( + client.has_asset_fee_override(&asset), + "an explicit 0 bps override must be distinguishable from no override" + ); assert_eq!(client.asset_fee(&asset), 0); } @@ -6955,7 +7021,9 @@ fn test_has_asset_fee_override_false_after_clear() { assert!(client.has_asset_fee_override(&asset)); client.clear_asset_fee(&asset); - assert!(!client.has_asset_fee_override(&asset), - "clear_asset_fee must revert override visibility to false"); + assert!( + !client.has_asset_fee_override(&asset), + "clear_asset_fee must revert override visibility to false" + ); assert_eq!(client.asset_fee(&asset), 0); // falls back to global fee (0) }