Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions contracts/escrow/src/test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![cfg(test)]

use super::*;
use crate::contract::{EscrowContract, EscrowContractClient};
use soroban_sdk::{
testutils::{storage::Persistent as _, Address as _, Ledger},
token, vec, Address, Env,
Expand Down Expand Up @@ -369,8 +370,8 @@ fn test_adversarial_ordering_resistance() {
let admin = Address::generate(&env);
let oracle = Address::generate(&env);
let treasury = Address::generate(&env);
let contract_id = env.register(crate::EscrowContract, ());
let client = crate::EscrowContractClient::new(&env, &contract_id);
let contract_id = env.register(crate::contract::EscrowContract, ());
let client = crate::contract::EscrowContractClient::new(&env, &contract_id);

// Initialize with 0% fee to simplify fraction/dust calculations
client.initialize(&admin, &oracle, &treasury, &0u32, &None);
Expand Down Expand Up @@ -435,8 +436,8 @@ fn test_large_split_distributes_dust_by_largest_remainder() {
let admin = Address::generate(&env);
let oracle = Address::generate(&env);
let treasury = Address::generate(&env);
let contract_id = env.register(crate::EscrowContract, ());
let client = crate::EscrowContractClient::new(&env, &contract_id);
let contract_id = env.register(crate::contract::EscrowContract, ());
let client = crate::contract::EscrowContractClient::new(&env, &contract_id);
// 0% fee so the whole total is distributable.
client.initialize(&admin, &oracle, &treasury, &0u32, &None);

Expand Down Expand Up @@ -2152,6 +2153,7 @@ fn test_set_treasury_updates_treasury_address() {
#[test]
fn test_set_treasury_requires_admin_auth() {
let env = Env::default();
env.mock_all_auths();
let (_, admin, _treasury, client) = setup(&env);

let new_treasury = Address::generate(&env);
Expand Down
14 changes: 8 additions & 6 deletions contracts/milestones/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,13 +493,15 @@ mod contract {
/// Rejects if the issue is already Released (funds have left the
/// contract) or not currently Allocated.
///
/// Blocked while the contract is paused, like `allocate`, since it
/// mutates the same allocation state.
/// **Allowed while paused.** Deallocate is a sponsor-protective reclaim
/// path — it only moves an allocated amount back toward
/// `remaining_budget` (recoverable via `cancel_milestone`), so it must
/// stay live during a pause per the pause design's first principle
/// ("a paused contract must still allow sponsor-protective
/// refund/reclaim paths"). Blocking it during an incident would trap
/// allocated-but-unreleased sponsor funds. Only spend-direction
/// mutations (allocate, contribute, release) are pause-gated.
pub fn deallocate(env: Env, milestone_id: u64, issue_id: u64) -> Result<(), Error> {
if is_paused(&env) {
return Err(Error::ContractPaused);
}

require_admin(&env)?.require_auth();

let mkey = DataKey::Milestone(milestone_id);
Expand Down
26 changes: 16 additions & 10 deletions contracts/milestones/src/test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![cfg(test)]

use super::*;
use crate::contract::{MilestonesContract, MilestonesContractClient};
use mergefi_common::MAX_SPONSORS;
use soroban_sdk::{
testutils::{Address as _, Ledger as _},
Expand Down Expand Up @@ -200,8 +201,8 @@ fn test_large_split_distributes_dust_by_largest_remainder() {
let admin = Address::generate(&env);
let oracle = Address::generate(&env);
let treasury = Address::generate(&env);
let contract_id = env.register(crate::MilestonesContract, ());
let client = crate::MilestonesContractClient::new(&env, &contract_id);
let contract_id = env.register(crate::contract::MilestonesContract, ());
let client = crate::contract::MilestonesContractClient::new(&env, &contract_id);
// 0% fee so the whole total is distributable.
client.initialize(&admin, &oracle, &treasury, &0u32, &None, &None);

Expand Down Expand Up @@ -424,7 +425,11 @@ fn test_deallocate_requires_admin_auth() {
}

#[test]
fn test_deallocate_blocked_while_paused() {
fn test_deallocate_allowed_while_paused() {
// Deallocate is a sponsor-protective reclaim path — it must stay live
// during a pause so allocated-but-unreleased sponsor funds aren't
// trapped by an incident pause. Only spend-direction mutations are
// pause-gated.
let env = Env::default();
env.mock_all_auths();
let (_admin, _treasury, client) = setup(&env);
Expand All @@ -436,12 +441,14 @@ fn test_deallocate_blocked_while_paused() {
client.create_milestone(&95u64, &sponsor, &token_addr, &10_000_000_000i128, &1_000u64);
client.allocate(&95u64, &950u64, &1_000_000_000i128);

// Paused — deallocate still succeeds: the allocation returns to
// remaining_budget and the issue-status record clears.
client.pause();
let result = client.try_deallocate(&95u64, &950u64);
assert_eq!(result, Err(Ok(Error::ContractPaused)));

client.unpause();
client.deallocate(&95u64, &950u64);

let milestone = client.get_milestone(&95u64);
assert_eq!(milestone.remaining_budget, 10_000_000_000i128);
assert!(milestone.allocations.get(950u64).is_none());
}

#[test]
Expand Down Expand Up @@ -1027,15 +1034,14 @@ fn test_pause_blocks_commitment_paths_but_allows_cancel() {
let allocate_err = client.try_allocate(&90u64, &901u64, &1_000_000_000i128);
assert_eq!(allocate_err, Err(Ok(Error::ContractPaused)));

let deallocate_err = client.try_deallocate(&90u64, &900u64);
assert_eq!(deallocate_err, Err(Ok(Error::ContractPaused)));
client.deallocate(&90u64, &900u64);

let recipients = vec![&env, (recipient, 10_000u32)];
let release_err = client.try_release_issue(&90u64, &900u64, &recipients);
assert_eq!(release_err, Err(Ok(Error::ContractPaused)));

client.cancel_milestone(&90u64);
assert_eq!(token_client.balance(&sponsor), 9_000_000_000i128);
assert_eq!(token_client.balance(&sponsor), 10_000_000_000i128);
assert_eq!(token_client.balance(&treasury), 0);
}

Expand Down