Skip to content
Merged
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
7 changes: 7 additions & 0 deletions contracts/escrow/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,11 @@ pub enum Error {

/// (16) The token address is not on the allowlist.
InvalidToken = 16,

/// (17) `set_match_timeout` was called with a value of zero.
InvalidTimeout = 17,

/// (18) `set_match_timeout` was called with a value exceeding the maximum
/// allowed timeout (~30 days / `MATCH_TTL_LEDGERS`).
TimeoutTooLarge = 18,
}
11 changes: 11 additions & 0 deletions contracts/escrow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,13 +661,24 @@ impl EscrowContract {
}

/// Set the match expiry timeout in ledgers. Requires admin auth.
///
/// # Errors
/// - [`Error::Unauthorized`] — caller is not the admin.
/// - [`Error::InvalidTimeout`] — `ledgers` is zero.
/// - [`Error::TimeoutTooLarge`] — `ledgers` exceeds `MATCH_TTL_LEDGERS`.
pub fn set_match_timeout(env: Env, ledgers: u32) -> Result<(), Error> {
let admin: Address = env
.storage()
.instance()
.get(&DataKey::Admin)
.ok_or(Error::Unauthorized)?;
admin.require_auth();
if ledgers == 0 {
return Err(Error::InvalidTimeout);
}
if ledgers > MATCH_TTL_LEDGERS {
return Err(Error::TimeoutTooLarge);
}
env.storage()
.instance()
.set(&DataKey::MatchTimeout, &ledgers);
Expand Down
74 changes: 74 additions & 0 deletions contracts/escrow/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2683,6 +2683,79 @@ fn test_expire_match_refunds_both_players_when_both_deposited_but_still_pending(
assert_eq!(token_client.balance(&player2) - p2_balance_before, 100);
}


// ── Task #1: expire_match emits ("match", "expired") with match_id payload ──
#[test]
fn test_expire_match_emits_expired_event() {
let (env, contract_id, _oracle, player1, player2, token, _admin) = setup();
let client = EscrowContractClient::new(&env, &contract_id);

env.ledger().set_sequence_number(100);

let id = client.create_match(
&player1,
&player2,
&100,
&token,
&String::from_str(&env, "expire_event_game"),
&Platform::Lichess,
);

// Extend TTLs so storage survives the ledger jump
for addr in [&contract_id, &token] {
env.deployer()
.extend_ttl_for_contract_instance(addr.clone(), MATCH_TTL_LEDGERS, MATCH_TTL_LEDGERS);
env.deployer()
.extend_ttl_for_code(addr.clone(), MATCH_TTL_LEDGERS, MATCH_TTL_LEDGERS);
}
env.as_contract(&contract_id, || {
env.storage()
.persistent()
.extend_ttl(&DataKey::ActiveMatches, MATCH_TTL_LEDGERS, MATCH_TTL_LEDGERS);
});

// Advance past the default timeout
env.ledger().set_sequence_number(100 + DEFAULT_MATCH_TIMEOUT_LEDGERS);

for addr in [&contract_id, &token] {
env.deployer()
.extend_ttl_for_contract_instance(addr.clone(), MATCH_TTL_LEDGERS, MATCH_TTL_LEDGERS);
env.deployer()
.extend_ttl_for_code(addr.clone(), MATCH_TTL_LEDGERS, MATCH_TTL_LEDGERS);
}
env.as_contract(&contract_id, || {
env.storage()
.persistent()
.extend_ttl(&DataKey::ActiveMatches, MATCH_TTL_LEDGERS, MATCH_TTL_LEDGERS);
});

client.expire_match(&id);

let events = env.events().all();
let expected_topics = vec![
&env,
Symbol::new(&env, "match").into_val(&env),
symbol_short!("expired").into_val(&env),
];
let matched = events
.iter()
.find(|(_, topics, _)| *topics == expected_topics);
assert!(matched.is_some(), "match expired event not emitted");

let (_, _, data) = matched.unwrap();
let ev_id: u64 = TryFromVal::try_from_val(&env, &data).unwrap();
assert_eq!(ev_id, id);
}

// ── Task #2: lowering timeout after match creation affects expiry immediately ─
#[test]
fn test_lowering_timeout_after_match_creation_affects_expiry_immediately() {
let (env, contract_id, _oracle, player1, player2, token, _admin) = setup();
let client = EscrowContractClient::new(&env, &contract_id);

env.ledger().set_sequence_number(100);

let id = client.create_match(
// #618 — instance TTL is refreshed after create_match
#[test]
fn test_instance_ttl_refreshed_after_create_match() {
Expand All @@ -2698,6 +2771,7 @@ fn test_instance_ttl_refreshed_after_create_match() {
});

client.create_match(

&player1,
&player2,
&100,
Expand Down
Loading