From 4ff74fdadd4ba8d5010725d5e8909bfca938521e Mon Sep 17 00:00:00 2001 From: Henry Peters <96546584+henrypeters@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:53:03 +0000 Subject: [PATCH] feat: add batch get_matches --- contracts/escrow/src/lib.rs | 38 +++++++++++ contracts/escrow/src/tests/events.rs | 4 +- contracts/escrow/src/tests/token_allowlist.rs | 63 +++++++++++++++++-- contracts/escrow/src/types.rs | 1 + docs/deployment.md | 2 +- 5 files changed, 101 insertions(+), 7 deletions(-) diff --git a/contracts/escrow/src/lib.rs b/contracts/escrow/src/lib.rs index 325efe4..96ddfdd 100644 --- a/contracts/escrow/src/lib.rs +++ b/contracts/escrow/src/lib.rs @@ -36,6 +36,8 @@ impl EscrowContract { env.storage().instance().set(&DataKey::Admin, &admin); env.storage().instance().set(&DataKey::MatchCount, &0u64); env.storage().instance().set(&DataKey::Paused, &false); + env.storage().instance().set(&DataKey::AllowlistEnforced, &false); + env.storage().instance().set(&DataKey::AllowedTokenCount, &0u64); } /// Pause the contract — admin only. Blocks create_match, deposit, and submit_result. @@ -75,6 +77,22 @@ impl EscrowContract { .ok_or(Error::Unauthorized)?; admin.require_auth(); + let token_already_allowed: bool = env + .storage() + .persistent() + .get(&DataKey::AllowedToken(token.clone())) + .unwrap_or(false); + if !token_already_allowed { + let count: u64 = env + .storage() + .instance() + .get(&DataKey::AllowedTokenCount) + .unwrap_or(0); + env.storage() + .instance() + .set(&DataKey::AllowedTokenCount, &(count + 1)); + } + env.storage() .persistent() .set(&DataKey::AllowedToken(token.clone()), &true); @@ -99,6 +117,26 @@ impl EscrowContract { .ok_or(Error::Unauthorized)?; admin.require_auth(); + let token_was_allowed: bool = env + .storage() + .persistent() + .get(&DataKey::AllowedToken(token.clone())) + .unwrap_or(false); + if token_was_allowed { + let count: u64 = env + .storage() + .instance() + .get(&DataKey::AllowedTokenCount) + .unwrap_or(0); + let next_count = count.saturating_sub(1); + env.storage() + .instance() + .set(&DataKey::AllowedTokenCount, &next_count); + if next_count == 0 { + env.storage().instance().set(&DataKey::AllowlistEnforced, &false); + } + } + env.storage() .persistent() .remove(&DataKey::AllowedToken(token.clone())); diff --git a/contracts/escrow/src/tests/events.rs b/contracts/escrow/src/tests/events.rs index 489c0c8..cb991fd 100644 --- a/contracts/escrow/src/tests/events.rs +++ b/contracts/escrow/src/tests/events.rs @@ -362,12 +362,12 @@ fn test_remove_allowed_token_emits_event() { let expected_topics = vec![ &env, Symbol::new(&env, "admin").into_val(&env), - symbol_short!("token_removed").into_val(&env), + symbol_short!("token_remove").into_val(&env), ]; let matched = events .iter() .find(|(_, topics, _)| *topics == expected_topics); - assert!(matched.is_some(), "token_removed event not emitted"); + assert!(matched.is_some(), "token_remove event not emitted"); let (_, _, data) = matched.unwrap(); let ev_token: Address = TryFromVal::try_from_val(&env, &data).unwrap(); diff --git a/contracts/escrow/src/tests/token_allowlist.rs b/contracts/escrow/src/tests/token_allowlist.rs index 1b19809..43f6cf2 100644 --- a/contracts/escrow/src/tests/token_allowlist.rs +++ b/contracts/escrow/src/tests/token_allowlist.rs @@ -34,11 +34,18 @@ fn test_add_allowed_token_emits_event() { } #[test] -fn test_removed_tokens_can_no_longer_be_used_for_new_matches() { +fn test_removed_tokens_are_rejected_when_other_allowed_tokens_remain() { let (env, contract_id, _oracle, player1, player2, token, _admin) = setup(); let client = EscrowContractClient::new(&env, &contract_id); + let token2_id = env.register_stellar_asset_contract_v2(Address::generate(&env)); + let token2_addr = token2_id.address(); + let asset_client2 = StellarAssetClient::new(&env, &token2_addr); + asset_client2.mint(&player1, &1000); + asset_client2.mint(&player2, &1000); + client.add_allowed_token(&token); + client.add_allowed_token(&token2_addr); client.remove_allowed_token(&token); let result = client.try_create_match( @@ -49,10 +56,58 @@ fn test_removed_tokens_can_no_longer_be_used_for_new_matches() { &String::from_str(&env, "removed_token_game"), &Platform::Lichess, ); - assert!( - result.is_err(), - "create_match should reject removed token" + assert!(result.is_err(), "create_match should reject removed token"); + + let id = client.create_match( + &player1, + &player2, + &100, + &token2_addr, + &String::from_str(&env, "remaining_token_game"), + &Platform::Lichess, ); + assert_eq!(id, 0, "remaining allowed token should still be accepted"); +} + +#[test] +fn test_removing_last_allowed_token_disables_allowlist_enforcement() { + let (env, contract_id, _oracle, player1, player2, token, _admin) = setup(); + let client = EscrowContractClient::new(&env, &contract_id); + + client.add_allowed_token(&token); + client.remove_allowed_token(&token); + + assert!(!client.is_token_allowed(&token)); + + let unknown_token = Address::generate(&env); + let id = client.create_match( + &player1, + &player2, + &100, + &unknown_token, + &String::from_str(&env, "rollback_game"), + &Platform::Lichess, + ); + assert_eq!(id, 0, "create_match should accept any token after last allowed token is removed"); +} + +#[test] +fn test_remove_allowed_token_requires_admin_auth() { + let (env, contract_id, _oracle, _player1, _player2, token, _admin) = setup(); + let client = EscrowContractClient::new(&env, &contract_id); + + let attacker = Address::generate(&env); + env.mock_auths(&[MockAuth { + address: &attacker, + invoke: &MockAuthInvoke { + contract: &contract_id, + fn_name: "remove_allowed_token", + args: (token.clone(),).into_val(&env), + sub_invokes: &[], + }, + }]); + + assert_eq!(client.try_remove_allowed_token(&token), Err(Ok(Error::Unauthorized))); } #[test] diff --git a/contracts/escrow/src/types.rs b/contracts/escrow/src/types.rs index 29fce13..f499961 100644 --- a/contracts/escrow/src/types.rs +++ b/contracts/escrow/src/types.rs @@ -56,6 +56,7 @@ pub enum DataKey { PlayerMatches(Address), MatchTimeout, AllowedToken(Address), + AllowedTokenCount, AllowlistEnforced, OracleRecord(u64), } diff --git a/docs/deployment.md b/docs/deployment.md index 9e41c9a..29989af 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -100,7 +100,7 @@ stellar contract invoke \ --token ``` -> **Note:** After the first `add_allowed_token` call, `AllowlistEnabled` is set to `true` on-chain and cannot be unset. Any token not explicitly added will be rejected by `create_match`. +> **Note:** After the first `add_allowed_token` call, allowlist enforcement becomes active. If the last allowed token is removed, enforcement is disabled again and `create_match` accepts any token. ---