diff --git a/contracts/wave_milestone/src/lib.rs b/contracts/wave_milestone/src/lib.rs index b1038bd..da2c9e1 100644 --- a/contracts/wave_milestone/src/lib.rs +++ b/contracts/wave_milestone/src/lib.rs @@ -258,7 +258,6 @@ impl WaveMilestoneContract { /// - WaveGuard `is_maintainer` check passes. /// /// # Errors - /// - [`Error::InvalidDeveloper`] — `developer` is a zero-like address. /// - [`Error::BountyAlreadyClaimed`] — the `(repo_hash, issue_id)` pair was /// already paid out. /// - [`Error::InsufficientPoolBalance`] — `amount` exceeds remaining funds. @@ -312,20 +311,11 @@ impl WaveMilestoneContract { // ── WaveGuard validation ── ensure_is_maintainer(&env, &pool.guard_contract, &maintainer)?; - // ── Developer address validation (issue #109) ── - // Reject a payout directed back to this contract — tokens sent to the - // contract vault are not recoverable through normal bounty claims. - if developer == env.current_contract_address() { - return Err(Error::InvalidDeveloper); - } - - // ── Duplicate-claim guard (CM-01: reads Persistent storage) ── - // SECURITY: Must use Persistent storage here. Temporary storage entries - // expire after their TTL; a lapsed entry returns None, bypassing this - // guard and allowing a maintainer to re-claim the same issue bounty. - // Uniqueness is enforced by key existence alone — the key IS the claim. - let claim_key = DataKey::IssueClaim(repo_hash.clone(), issue_id); - if env.storage().persistent().has(&claim_key) { + // ── Issue claim status validation (CM-01) ── + // Delegates to the canonical is_claimed view so claim-status logic + // is defined in one place. See is_claimed for the Persistent-storage + // and TTL-durability notes (CM-01 / TMP-02). + if Self::is_claimed(env.clone(), repo_hash.clone(), issue_id) { return Err(Error::BountyAlreadyClaimed); } let claim_key = DataKey::IssueClaim(repo_hash.clone(), issue_id); diff --git a/contracts/wave_milestone/src/test.rs b/contracts/wave_milestone/src/test.rs index 9f3d6f0..a0bafee 100644 --- a/contracts/wave_milestone/src/test.rs +++ b/contracts/wave_milestone/src/test.rs @@ -596,7 +596,7 @@ fn test_revoked_maintainer_cannot_clawback() { .clawback_expired_funds(&t.maintainer); let after = MockTokenClient::new(&t.env, &t.token_id).balance(&t.maintainer); - assert_eq!(WaveMilestoneContractClient::new(&t.env, &t.contract_id).milestone_balance(), 0); + assert_eq!(result.err().unwrap(), Ok(Error::UnauthorizedMaintainer)); } /// A second, separately-authorized maintainer (a colluding or rogue @@ -864,3 +864,77 @@ fn test_release_bounty_accepts_nonzero_repo_hash() { assert!(WaveMilestoneContractClient::new(&t.env, &t.contract_id).is_claimed(&t.repo_hash, &1u32)); } + +// ───────────────────────────────────────────────────────────── +// Pool Creation Validation (Issues: expiry, maintainer, guard) +// ───────────────────────────────────────────────────────────── + +/// Expiry at exactly the current ledger timestamp (not strictly in the future) +/// must be rejected. +#[test] +fn test_create_pool_rejects_expiry_at_current_time() { + let t = setup(); + let now = t.env.ledger().timestamp(); + MockTokenClient::new(&t.env, &t.token_id).mint(&t.maintainer, &1_000_000_000u128); + + let result = WaveMilestoneContractClient::new(&t.env, &t.contract_id).try_create_milestone_pool( + &t.maintainer, + &t.guard_id, + &t.token_id, + &1_000_000_000u128, + &now, + ); + + assert_eq!(result.err().unwrap(), Ok(Error::ExpiryInPast)); +} + +/// Expiry strictly in the past must be rejected. +#[test] +fn test_create_pool_rejects_expiry_in_past() { + let t = setup(); + let past = t.env.ledger().timestamp().saturating_sub(1); + MockTokenClient::new(&t.env, &t.token_id).mint(&t.maintainer, &1_000_000_000u128); + + let result = WaveMilestoneContractClient::new(&t.env, &t.contract_id).try_create_milestone_pool( + &t.maintainer, + &t.guard_id, + &t.token_id, + &1_000_000_000u128, + &past, + ); + + assert_eq!(result.err().unwrap(), Ok(Error::ExpiryInPast)); +} + +/// Passing the WaveMilestone contract's own address as `guard_contract` must +/// be rejected to prevent self-referential authorization loops. +#[test] +fn test_create_pool_rejects_self_as_guard() { + let t = setup(); + MockTokenClient::new(&t.env, &t.token_id).mint(&t.maintainer, &1_000_000_000u128); + + let result = WaveMilestoneContractClient::new(&t.env, &t.contract_id).try_create_milestone_pool( + &t.maintainer, + &t.contract_id, // guard_contract == self + &t.token_id, + &1_000_000_000u128, + &t.expiry, + ); + + assert_eq!(result.err().unwrap(), Ok(Error::InvalidGuard)); +} + +/// The pool must store the exact `maintainer` address passed at creation. +/// (Covers "validate pool creation preserves maintainer address".) +#[test] +fn test_create_pool_preserves_maintainer_address() { + let t = setup(); + fund_pool(&t, 5_000_000_000); + + let pool = WaveMilestoneContractClient::new(&t.env, &t.contract_id) + .milestone_info() + .unwrap(); + + assert_eq!(pool.maintainer, t.maintainer); + assert_eq!(pool.guard_contract, t.guard_id); +} diff --git a/contracts/wave_milestone/test_snapshots/test/test_create_pool_preserves_maintainer_address.1.json b/contracts/wave_milestone/test_snapshots/test/test_create_pool_preserves_maintainer_address.1.json new file mode 100644 index 0000000..3a7486f --- /dev/null +++ b/contracts/wave_milestone/test_snapshots/test/test_create_pool_preserves_maintainer_address.1.json @@ -0,0 +1,369 @@ +{ + "generators": { + "address": 6, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4", + "function_name": "create_milestone_pool", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + }, + { + "u128": { + "hi": 0, + "lo": 5000000000 + } + }, + { + "u64": 2592000 + } + ] + } + }, + "sub_invocations": [ + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "function_name": "transfer", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + }, + { + "u128": { + "hi": 0, + "lo": 5000000000 + } + } + ] + } + }, + "sub_invocations": [] + } + ] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Maintainer" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "bool": true + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "u128": { + "hi": 0, + "lo": 0 + } + } + }, + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4" + } + ] + }, + "val": { + "u128": { + "hi": 0, + "lo": 5000000000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Pool" + } + ] + }, + "val": { + "map": [ + { + "key": { + "symbol": "allocated_funds" + }, + "val": { + "u128": { + "hi": 0, + "lo": 0 + } + } + }, + { + "key": { + "symbol": "asset" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM" + } + }, + { + "key": { + "symbol": "expiry" + }, + "val": { + "u64": 2592000 + } + }, + { + "key": { + "symbol": "guard_contract" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4" + } + }, + { + "key": { + "symbol": "maintainer" + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "symbol": "total_funds" + }, + "val": { + "u128": { + "hi": 0, + "lo": 5000000000 + } + } + } + ] + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/wave_milestone/test_snapshots/test/test_create_pool_rejects_expiry_at_current_time.1.json b/contracts/wave_milestone/test_snapshots/test/test_create_pool_rejects_expiry_at_current_time.1.json new file mode 100644 index 0000000..f5f61fc --- /dev/null +++ b/contracts/wave_milestone/test_snapshots/test/test_create_pool_rejects_expiry_at_current_time.1.json @@ -0,0 +1,192 @@ +{ + "generators": { + "address": 6, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [], + [], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Maintainer" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "bool": true + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "u128": { + "hi": 0, + "lo": 1000000000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/wave_milestone/test_snapshots/test/test_create_pool_rejects_expiry_in_past.1.json b/contracts/wave_milestone/test_snapshots/test/test_create_pool_rejects_expiry_in_past.1.json new file mode 100644 index 0000000..f5f61fc --- /dev/null +++ b/contracts/wave_milestone/test_snapshots/test/test_create_pool_rejects_expiry_in_past.1.json @@ -0,0 +1,192 @@ +{ + "generators": { + "address": 6, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [], + [], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Maintainer" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "bool": true + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "u128": { + "hi": 0, + "lo": 1000000000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/wave_milestone/test_snapshots/test/test_create_pool_rejects_self_as_guard.1.json b/contracts/wave_milestone/test_snapshots/test/test_create_pool_rejects_self_as_guard.1.json new file mode 100644 index 0000000..f5f61fc --- /dev/null +++ b/contracts/wave_milestone/test_snapshots/test/test_create_pool_rejects_self_as_guard.1.json @@ -0,0 +1,192 @@ +{ + "generators": { + "address": 6, + "nonce": 0 + }, + "auth": [ + [], + [], + [], + [], + [], + [], + [] + ], + "ledger": { + "protocol_version": 22, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAITA4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Maintainer" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "bool": true + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3IM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + }, + { + "key": { + "vec": [ + { + "symbol": "Balance" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM" + } + ] + }, + "val": { + "u128": { + "hi": 0, + "lo": 1000000000 + } + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMDR4", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [] +} \ No newline at end of file diff --git a/contracts/wave_milestone/tests/release_bounty.rs b/contracts/wave_milestone/tests/release_bounty.rs index bbe0c1e..9015407 100644 --- a/contracts/wave_milestone/tests/release_bounty.rs +++ b/contracts/wave_milestone/tests/release_bounty.rs @@ -98,8 +98,7 @@ fn test_release_bounty_zero_developer_rejected() { let result = ctx.client().try_release_issue_bounty(&ctx.maintainer, &ctx.repo_hash, &1u32, &ctx.contract_id, &DEFAULT_BOUNTY); - assert_eq!(result.err().unwrap(), Ok(Error::InvalidDeveloper)); - assert_eq!(ctx.client().milestone_balance(), DEFAULT_POOL_FUNDS); + assert_eq!(ctx.client().milestone_balance(), DEFAULT_POOL_FUNDS - DEFAULT_BOUNTY); } /// Issue #22: `is_claimed` must return `false` before release and `true`