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
17 changes: 16 additions & 1 deletion contracts/milestones/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ mod contract {
deadline,
closed: false,
allocations: Map::new(&env),
released_total: 0,
contributor_count: 1,
};
env.storage().persistent().set(&key, &milestone);
Expand Down Expand Up @@ -382,7 +383,7 @@ mod contract {
require_oracle(&env)?.require_auth();

let mkey = DataKey::Milestone(milestone_id);
let milestone: Milestone = env
let mut milestone: Milestone = env
.storage()
.persistent()
.get(&mkey)
Expand Down Expand Up @@ -434,6 +435,20 @@ mod contract {
}
}

// #608 — remove the released allocation: the funds left the
// contract, so allocations must stop listing the issue as
// outstanding. (remaining_budget was already reduced at
// allocate-time; it correctly excludes this amount.) Otherwise the
// map drifts into a phantom ledger.
let released_amount = milestone
.allocations
.get(issue_id)
.unwrap_or(0);
milestone.allocations.remove(issue_id);
milestone.released_total += released_amount;
env.storage().persistent().set(&mkey, &milestone);
extend_ttl(&env, &mkey);

// Refresh the parent milestone's Contribution sub-records so they
// stay alive as individual issues are released over the milestone's
// lifetime — release_issue doesn't touch contributions directly but
Expand Down
9 changes: 5 additions & 4 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 @@ -1208,8 +1209,8 @@ fn assert_milestone_invariants(client: &MilestonesContractClient, milestone_id:
// total_budget == remaining_budget + sum(allocations)
assert_eq!(
milestone.total_budget,
milestone.remaining_budget + sum_allocations,
"Invariant 1 failed: total_budget != remaining_budget + sum_allocations for open milestone {}",
milestone.remaining_budget + sum_allocations + milestone.released_total,
"Invariant 1 failed: total_budget != remaining_budget + sum_allocations + released_total for open milestone {}",
milestone_id
);
}
Expand Down
8 changes: 6 additions & 2 deletions contracts/milestones/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@ pub struct Milestone {
pub created_at: u64,
pub deadline: u64,
pub closed: bool,
/// issue_id -> allocated amount (0 once released and removed from the
/// "open" set is not necessary; we track release via `IssueStatus`).
/// issue_id -> allocated amount for issues still outstanding; released
/// issues are removed so the map only ever lists open obligations (#608).
pub allocations: Map<u64, i128>,
/// Sum of every amount paid out via `release_issue`. Together with
/// `remaining_budget` and `allocations` this preserves the accounting
/// identity: total_budget == remaining + outstanding + released.
pub released_total: i128,
/// Number of distinct contributions recorded (`1` for a single-sponsor
/// milestone); enumerate the ledger via
/// `get_contribution(0..contributor_count)`.
Expand Down