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
30 changes: 21 additions & 9 deletions app/onchain/contracts/aid_escrow/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,15 +124,27 @@ pub struct Aggregates {

## Events

All state-changing operations emit events with stable topics for indexer consumption:

- `EscrowFunded` — pool funded
- `PackageCreated` — package created
- `PackageClaimed` — recipient claimed
- `PackageDisbursed` — admin disbursed
- `PackageRevoked` — admin revoked
- `PackageRefunded` — admin refunded
- `BatchCreatedEvent` — batch creation
All state-changing operations emit events with stable topics and payloads optimized for off-chain indexers. Payloads are compact, exclude any dynamic maps/PII, and are structured under `EVENT_SCHEMA_VERSION = 2`.

### Schema Versioning
`EVENT_SCHEMA_VERSION` (currently `2`) is exported by the contract and should be tracked by the backend indexer to detect breaking schema updates.

### Event Definitions

- **`EscrowFunded`**: `from: Address`, `token: Address`, `amount: i128`, `timestamp: u64`
- **`PackageCreated`**: `package_id: u64`, `recipient: Address`, `amount: i128`, `token: Address`, `actor: Address`, `timestamp: u64`
- **`PackageClaimed`**: `package_id: u64`, `recipient: Address`, `amount: i128`, `token: Address`, `actor: Address`, `timestamp: u64`
- **`PackageDisbursed`**: `package_id: u64`, `recipient: Address`, `amount: i128`, `token: Address`, `actor: Address`, `timestamp: u64`
- **`PackageRevoked`**: `package_id: u64`, `recipient: Address`, `amount: i128`, `token: Address`, `actor: Address`, `timestamp: u64`
- **`PackageRefunded`**: `package_id: u64`, `recipient: Address`, `amount: i128`, `token: Address`, `actor: Address`, `timestamp: u64`
- **`BatchCreatedEvent`**: `ids: Vec<u64>`, `admin: Address`, `token: Address`, `total_amount: i128`, `timestamp: u64`
- **`ExtendedEvent`**: `id: u64`, `admin: Address`, `old_expires_at: u64`, `new_expires_at: u64`, `timestamp: u64`
- **`SurplusWithdrawnEvent`**: `to: Address`, `token: Address`, `amount: i128`, `timestamp: u64`
- **`ContractPausedEvent`**: `admin: Address`, `timestamp: u64`
- **`ContractUnpausedEvent`**: `admin: Address`, `timestamp: u64`
- **`ActionPausedEvent`**: `admin: Address`, `action: Symbol`, `timestamp: u64`
- **`ActionUnpausedEvent`**: `admin: Address`, `action: Symbol`, `timestamp: u64`


## Testing

Expand Down
55 changes: 51 additions & 4 deletions app/onchain/contracts/aid_escrow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ const KEY_PAUSE_WITHDRAW: Symbol = symbol_short!("p_wdrw");
const KEY_TOTAL_CLAIMED: Symbol = symbol_short!("claimed"); // Map<Address, i128>
const META_MERKLE_ROOT_KEY: &str = "merkle_root";

/// Semantic version for the event schema. Bump whenever event topics or
/// payload shapes change so off-chain indexers can detect incompatibilities.
pub const EVENT_SCHEMA_VERSION: u32 = 2;

// --- Data Types ---

#[contracttype]
Expand Down Expand Up @@ -109,6 +113,8 @@ pub enum Error {

// --- Contract Events (indexer-friendly; stable topics & payloads) ---
// Topic = struct name in snake_case (e.g. package_created). Do not rename without versioning.
// EVENT_SCHEMA_VERSION must be bumped whenever event fields or topics change.
// No PII or opaque metadata maps are included — payloads are compact and safe.

/// Emitted when the escrow pool is funded. Actor = funder.
#[contractevent]
Expand All @@ -124,6 +130,7 @@ pub struct PackageCreated {
pub package_id: u64,
pub recipient: Address,
pub amount: i128,
pub token: Address,
pub actor: Address,
pub timestamp: u64,
}
Expand All @@ -133,6 +140,7 @@ pub struct PackageClaimed {
pub package_id: u64,
pub recipient: Address,
pub amount: i128,
pub token: Address,
pub actor: Address,
pub timestamp: u64,
}
Expand All @@ -142,6 +150,7 @@ pub struct PackageDisbursed {
pub package_id: u64,
pub recipient: Address,
pub amount: i128,
pub token: Address,
pub actor: Address,
pub timestamp: u64,
}
Expand All @@ -151,6 +160,7 @@ pub struct PackageRevoked {
pub package_id: u64,
pub recipient: Address,
pub amount: i128,
pub token: Address,
pub actor: Address,
pub timestamp: u64,
}
Expand All @@ -160,6 +170,7 @@ pub struct PackageRefunded {
pub package_id: u64,
pub recipient: Address,
pub amount: i128,
pub token: Address,
pub actor: Address,
pub timestamp: u64,
}
Expand All @@ -168,7 +179,9 @@ pub struct PackageRefunded {
pub struct BatchCreatedEvent {
pub ids: Vec<u64>,
pub admin: Address,
pub token: Address,
pub total_amount: i128,
pub timestamp: u64,
}

#[contractevent]
Expand All @@ -177,35 +190,41 @@ pub struct ExtendedEvent {
pub admin: Address,
pub old_expires_at: u64,
pub new_expires_at: u64,
pub timestamp: u64,
}

#[contractevent]
pub struct SurplusWithdrawnEvent {
pub to: Address,
pub token: Address,
pub amount: i128,
pub timestamp: u64,
}

#[contractevent]
pub struct ContractPausedEvent {
pub admin: Address,
pub timestamp: u64,
}

#[contractevent]
pub struct ContractUnpausedEvent {
pub admin: Address,
pub timestamp: u64,
}

#[contractevent]
pub struct ActionPausedEvent {
pub admin: Address,
pub action: Symbol,
pub timestamp: u64,
}

#[contractevent]
pub struct ActionUnpausedEvent {
pub admin: Address,
pub action: Symbol,
pub timestamp: u64,
}

#[contract]
Expand Down Expand Up @@ -364,7 +383,8 @@ impl AidEscrow {
let admin = Self::get_admin(env.clone())?;
admin.require_auth();
env.storage().instance().set(&KEY_PAUSED, &true);
ContractPausedEvent { admin }.publish(&env);
let timestamp = env.ledger().timestamp();
ContractPausedEvent { admin, timestamp }.publish(&env);
Ok(())
}

Expand All @@ -377,7 +397,8 @@ impl AidEscrow {
let admin = Self::get_admin(env.clone())?;
admin.require_auth();
env.storage().instance().set(&KEY_PAUSED, &false);
ContractUnpausedEvent { admin }.publish(&env);
let timestamp = env.ledger().timestamp();
ContractUnpausedEvent { admin, timestamp }.publish(&env);
Ok(())
}

Expand All @@ -390,7 +411,13 @@ impl AidEscrow {
let key = Self::get_pause_key(action.clone())?;
env.storage().instance().set(&key, &true);

ActionPausedEvent { admin, action }.publish(&env);
let timestamp = env.ledger().timestamp();
ActionPausedEvent {
admin,
action,
timestamp,
}
.publish(&env);
Ok(())
}

Expand All @@ -403,7 +430,13 @@ impl AidEscrow {
let key = Self::get_pause_key(action.clone())?;
env.storage().instance().set(&key, &false);

ActionUnpausedEvent { admin, action }.publish(&env);
let timestamp = env.ledger().timestamp();
ActionUnpausedEvent {
admin,
action,
timestamp,
}
.publish(&env);
Ok(())
}

Expand Down Expand Up @@ -602,6 +635,7 @@ impl AidEscrow {
package_id: id,
recipient: recipient.clone(),
amount,
token,
actor: operator,
timestamp: created_at,
}
Expand Down Expand Up @@ -727,6 +761,7 @@ impl AidEscrow {
package_id: id,
recipient: recipient.clone(),
amount,
token: token.clone(),
actor: operator.clone(),
timestamp: created_at,
}
Expand All @@ -742,10 +777,13 @@ impl AidEscrow {
env.storage().instance().set(&KEY_PKG_IDX, &idx);

// Emit batch event
let batch_timestamp = env.ledger().timestamp();
BatchCreatedEvent {
ids: created_ids.clone(),
admin: operator,
token,
total_amount,
timestamp: batch_timestamp,
}
.publish(&env);

Expand Down Expand Up @@ -882,6 +920,7 @@ impl AidEscrow {
package_id: id,
recipient: package.recipient.clone(),
amount: package.amount,
token: package.token.clone(),
actor: admin.clone(),
timestamp,
}
Expand Down Expand Up @@ -918,6 +957,7 @@ impl AidEscrow {
package_id: id,
recipient: package.recipient.clone(),
amount: package.amount,
token: package.token.clone(),
actor: admin.clone(),
timestamp,
}
Expand Down Expand Up @@ -981,6 +1021,7 @@ impl AidEscrow {
package_id: id,
recipient: package.recipient.clone(),
amount: package.amount,
token: package.token.clone(),
actor: admin.clone(),
timestamp,
}
Expand Down Expand Up @@ -1026,6 +1067,7 @@ impl AidEscrow {
package_id,
recipient: package.recipient.clone(),
amount: package.amount,
token: package.token.clone(),
actor: admin.clone(),
timestamp,
}
Expand Down Expand Up @@ -1093,11 +1135,13 @@ impl AidEscrow {
package.expires_at = new_expires_at;
env.storage().persistent().set(&key, &package);

let ext_timestamp = env.ledger().timestamp();
ExtendedEvent {
id,
admin,
old_expires_at,
new_expires_at,
timestamp: ext_timestamp,
}
.publish(&env);

Expand Down Expand Up @@ -1145,10 +1189,12 @@ impl AidEscrow {
Self::transfer_token(&env, &token, &env.current_contract_address(), &to, &amount)?;

// 7. Emit event
let surplus_timestamp = env.ledger().timestamp();
SurplusWithdrawnEvent {
to: to.clone(),
token: token.clone(),
amount,
timestamp: surplus_timestamp,
}
.publish(&env);

Expand Down Expand Up @@ -1310,6 +1356,7 @@ impl AidEscrow {
package_id,
recipient: payout_recipient.clone(),
amount: package.amount,
token: package.token.clone(),
actor: payout_recipient.clone(),
timestamp: now,
}
Expand Down
Loading
Loading