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
35 changes: 35 additions & 0 deletions app/backend/src/onchain/utils/soroban-error.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,31 @@ export class SorobanErrorMapper {
message: 'Claim cooldown is still active',
errorCode: INTEGRATION_ERROR_CODES.ONCHAIN_INVALID_STATE,
},
23: {
code: 409,
message: 'Address is already a distributor',
errorCode: INTEGRATION_ERROR_CODES.ONCHAIN_CONTRACT_ERROR,
},
24: {
code: 404,
message: 'Address is not a distributor',
errorCode: INTEGRATION_ERROR_CODES.ONCHAIN_CONTRACT_ERROR,
},
25: {
code: 400,
message: 'Maximum number of distributors reached',
errorCode: INTEGRATION_ERROR_CODES.ONCHAIN_CONTRACT_ERROR,
},
26: {
code: 400,
message: 'Surplus withdrawal timelock has not elapsed yet',
errorCode: INTEGRATION_ERROR_CODES.ONCHAIN_INVALID_STATE,
},
27: {
code: 400,
message: 'No pending surplus withdrawal in progress',
errorCode: INTEGRATION_ERROR_CODES.ONCHAIN_CONTRACT_ERROR,
},
};

/**
Expand Down Expand Up @@ -435,6 +460,16 @@ export class SorobanErrorMapper {
message: 'Claim cooldown is still active',
errorCode: INTEGRATION_ERROR_CODES.ONCHAIN_INVALID_STATE,
},
TimelockNotElapsed: {
code: 400,
message: 'Surplus withdrawal timelock has not elapsed yet',
errorCode: INTEGRATION_ERROR_CODES.ONCHAIN_INVALID_STATE,
},
NoPendingWithdrawal: {
code: 400,
message: 'No pending surplus withdrawal in progress',
errorCode: INTEGRATION_ERROR_CODES.ONCHAIN_CONTRACT_ERROR,
},
};

for (const [errorKey, errorInfo] of Object.entries(errorMap)) {
Expand Down
6 changes: 5 additions & 1 deletion app/onchain/contracts/aid_escrow/EVENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ When making event schema changes:
| `package_refunded` | `refund` | Admin refunds an expired/cancelled package. |
| `package_swept` | `sweep_expired_packages` | Sweep transitions an expired `Created` package to terminal `Expired` (funds released from locked). |
| `extended_event` | `extend_expiration` | Admin extends a package expiry. |
| `surplus_withdrawn_event` | `withdraw_surplus` | Admin withdraws unallocated surplus from the pool. |
| `surplus_withdrawal_proposed` | `propose_surplus_withdrawal` | Admin proposes a surplus withdrawal, starting the timelock. |
| `surplus_withdrawal_cancelled` | `cancel_surplus_withdrawal` | Admin cancels a pending surplus withdrawal. |
| `surplus_withdrawn_event` | `execute_surplus_withdrawal` | A proposed surplus withdrawal clears its timelock and funds move. |
| `contract_paused_event` | `pause` | Admin pauses the whole contract. |
| `contract_unpaused_event` | `unpause` | Admin unpauses the whole contract. |
| `action_paused_event` | `pause_action` | Admin pauses a single action (create/claim/withdraw). |
Expand Down Expand Up @@ -107,6 +109,8 @@ Pool / administrative events:
| `EscrowFunded` | `schema_version: u32`, `from: Address`, `token: Address`, `amount: i128`, `timestamp: u64` |
| `BatchCreatedEvent` | `schema_version: u32`, `ids: Vec<u64>`, `admin: Address`, `total_amount: i128` |
| `ExtendedEvent` | `schema_version: u32`, `package_id: u64`, `admin: Address`, `old_expires_at: u64`, `new_expires_at: u64` |
| `SurplusWithdrawalProposed` | `schema_version: u32`, `admin: Address`, `to: Address`, `token: Address`, `amount: i128`, `unlock_time: u64`, `timestamp: u64` |
| `SurplusWithdrawalCancelled` | `schema_version: u32`, `admin: Address`, `to: Address`, `token: Address`, `amount: i128`, `timestamp: u64` |
| `SurplusWithdrawnEvent` | `schema_version: u32`, `to: Address`, `token: Address`, `amount: i128` |
| `ContractPausedEvent` | `schema_version: u32`, `admin: Address` |
| `ContractUnpausedEvent` | `schema_version: u32`, `admin: Address` |
Expand Down
16 changes: 13 additions & 3 deletions app/onchain/contracts/aid_escrow/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ expires and is refunded.
| `pause_campaign(env, campaign_ref)` | Admin | Pauses `claim`/`disburse`/`refund` for packages tagged with this `campaign_ref`. |
| `unpause_campaign(env, campaign_ref)` | Admin | Unpauses the campaign. |
| `is_campaign_paused(env, campaign_ref)` | — | Returns true if the campaign is paused (or the contract is globally paused). |
| `get_surplus_withdrawal_delay(env)` | — | Returns the configured surplus withdrawal timelock delay, in seconds. |
| `set_surplus_withdrawal_delay(env, delay_seconds)` | Admin | Configures the surplus withdrawal timelock delay. |

### Funding

Expand Down Expand Up @@ -75,7 +77,10 @@ expires and is refunded.
| `get_package(env, id)` | — | Returns full package details. |
| `view_package_status(env, id)` | — | Returns only the status (cheaper for polling). |
| `get_aggregates(env, token)` | — | Returns aggregate stats: total committed, claimed, expired/cancelled for a token. |
| `withdraw_surplus(env, token, to, amount)` | Admin | Withdraws surplus (unlocked) tokens from the contract. |
| `propose_surplus_withdrawal(env, to, amount, token)` | Admin | Proposes a surplus (unlocked) withdrawal; executable only after the configured timelock delay. Overwrites any existing proposal. |
| `get_pending_surplus_withdrawal(env)` | — | Returns the pending surplus withdrawal proposal, if any. |
| `cancel_surplus_withdrawal(env)` | Admin | Cancels the pending surplus withdrawal proposal. |
| `execute_surplus_withdrawal(env)` | Admin | Executes the pending surplus withdrawal once its timelock has elapsed. |

## Package Lifecycle

Expand Down Expand Up @@ -104,7 +109,7 @@ Cancelled --> Refunded (admin refunds)
| 10 | `PackageIdExists` | Duplicate ID in `create_package`. |
| 11 | `InvalidState` | Generic state violation (e.g. paused, bad config). |
| 12 | `MismatchedArrays` | `recipients` and `amounts` lengths differ in batch create. |
| 13 | `InsufficientSurplus` | `withdraw_surplus` amount exceeds available surplus. |
| 13 | `InsufficientSurplus` | Requested surplus withdrawal amount exceeds available surplus. |
| 14 | `ContractPaused` | Operation blocked because contract is paused. |
| 15 | `ClaimTooEarly` | Claim attempted before the claim window opens. |
| 16 | `InvalidProof` | Claim proof is invalid or missing. |
Expand All @@ -114,6 +119,11 @@ Cancelled --> Refunded (admin refunds)
| 20 | `InvalidPendingAdmin` | Pending admin address does not match the caller. |
| 21 | `BatchTooLarge` | Batch operation exceeds the maximum allowed size. |
| 22 | `ClaimCooldownActive` | Recipient has not yet completed the claim cooldown. |
| 23 | `DistributorAlreadyExists` | `add_distributor` called for an address that is already a distributor. |
| 24 | `DistributorNotFound` | `remove_distributor` called for an address that is not a distributor. |
| 25 | `DistributorSetFull` | `add_distributor` would exceed the configured maximum distributor set size. |
| 26 | `TimelockNotElapsed` | `execute_surplus_withdrawal` called before the proposal's timelock elapsed. |
| 27 | `NoPendingWithdrawal` | `cancel_surplus_withdrawal` / `execute_surplus_withdrawal` called with no proposal outstanding. |

### Compatibility Policy

Expand All @@ -124,7 +134,7 @@ user-facing messages, so reordering or removing a variant would silently break
that mapping.

- **Adding a new error**: append the new variant with the **next unused code**
(currently `23`). Never reuse, renumber, or skip codes.
(currently `28`). Never reuse, renumber, or skip codes.
- **Removing an error**: do **not** remove a variant. If it is no longer
emitted, keep the variant and its code so existing mappings remain valid.
- **Renaming**: renaming a variant is allowed only if the numeric code is
Expand Down
8 changes: 6 additions & 2 deletions app/onchain/contracts/aid_escrow/STORAGE_KEYS.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ A singleton key is a bare `Symbol`; exactly one entry exists per key.
| `KEY_RECIPIENT_LAST_CLAIM` | `"lastclaim"` | `Map<Address, u64>` (recipient → successful-claim timestamp) | Successful claim paths only. Enforces the optional `Config.claim_cooldown`; absent entries have no cooldown history. |
| `KEY_PKG_COUNTER` | `"pkg_cnt"` | `u64` | Package creation. Highest assigned id + 1; upper bound for id scans (`get_campaign_package_count`, etc.). |
| `KEY_PKG_IDX` | `"pkg_idx"` | `u64` | Package creation. Count of aggregation-index entries; positional bound for `get_aggregates`. May exceed `KEY_PKG_COUNTER` when explicit ids are used. |
| `KEY_SURPLUS_WITHDRAWAL_DELAY` | `"wd_delay"` | `u64` | `set_surplus_withdrawal_delay`. Falls back to `DEFAULT_SURPLUS_WITHDRAWAL_DELAY` (1 day) when absent. Permanent. |
| `KEY_PENDING_SURPLUS_WITHDRAWAL` | `"pend_wd"` | `PendingSurplusWithdrawal` | `propose_surplus_withdrawal`. **Ephemeral**: removed by `execute_surplus_withdrawal` / `cancel_surplus_withdrawal`. Absent when no withdrawal is proposed. |

### Persistent storage

Expand Down Expand Up @@ -113,12 +115,14 @@ above. Rules of thumb:
`KEY_ADMIN`, `KEY_PENDING_ADMIN` *(if a transfer is mid-flight)*,
all `("pkg", id)` records, `KEY_TOTAL_LOCKED`, `KEY_TOTAL_CLAIMED`,
`KEY_CAMPAIGN_TOKEN_LOCKED`, `KEY_CAMPAIGN_TOKEN_CLAIMED`,
`KEY_PKG_COUNTER`, `KEY_PKG_IDX`, all `("pidx", position)` entries, and the
`KEY_PKG_COUNTER`, `KEY_PKG_IDX`, all `("pidx", position)` entries, the
three delegate keys (`KEY_DELEGATES`, `KEY_DELEGATE_HISTORY`,
`KEY_DELEGATE_EXPIRY`).
`KEY_DELEGATE_EXPIRY`), and `KEY_PENDING_SURPLUS_WITHDRAWAL` *(if a
withdrawal is mid-flight)*.
3. **Safe to drop/reset without fund impact** (policy flags only):
`KEY_PAUSED`, `KEY_PAUSE_*`, `KEY_CAMPAIGN_PAUSED`, `KEY_DISTRIBUTORS`,
`KEY_MAX_DISTRIBUTORS` *(resets to `DEFAULT_MAX_DISTRIBUTORS`)*,
`KEY_SURPLUS_WITHDRAWAL_DELAY` *(resets to `DEFAULT_SURPLUS_WITHDRAWAL_DELAY`)*,
`KEY_CONFIG` *(re-initialize before unpausing)*. Dropping them changes
behaviour, not solvency.
4. **Derived/recomputable**: `KEY_TOTAL_LOCKED` can be rebuilt by scanning all
Expand Down
17 changes: 15 additions & 2 deletions app/onchain/contracts/aid_escrow/src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ pub const KEY_PAUSE_CREATE: Symbol = symbol_short!("p_create");
pub const KEY_PAUSE_CLAIM: Symbol = symbol_short!("p_claim");
/// Per-action pause flag for `refund` (`bool`).
pub const KEY_PAUSE_REFUND: Symbol = symbol_short!("p_refund");
/// Per-action pause flag for `withdraw_surplus` (`bool`).
/// Per-action pause flag for `propose_surplus_withdrawal` /
/// `execute_surplus_withdrawal` (`bool`).
pub const KEY_PAUSE_WITHDRAW: Symbol = symbol_short!("p_wdrw");
/// Campaign pause registry (`Map<String, bool>` keyed by `campaign_ref`).
pub const KEY_CAMPAIGN_PAUSED: Symbol = symbol_short!("camp_pzd");
Expand Down Expand Up @@ -93,6 +94,16 @@ pub const KEY_PKG_COUNTER: Symbol = symbol_short!("pkg_cnt");
/// upper bound for `get_aggregates`; may exceed the counter when explicit
/// ids are used.
pub const KEY_PKG_IDX: Symbol = symbol_short!("pkg_idx");
/// Configurable delay in seconds a proposed surplus withdrawal must wait
/// before it can be executed (`u64`). Falls back to
/// `DEFAULT_SURPLUS_WITHDRAWAL_DELAY` when absent. Admin-managed via
/// `set_surplus_withdrawal_delay`.
pub const KEY_SURPLUS_WITHDRAWAL_DELAY: Symbol = symbol_short!("wd_delay");
/// Pending surplus withdrawal proposal (`Option<PendingSurplusWithdrawal>`).
/// Written by `propose_surplus_withdrawal`; removed by
/// `execute_surplus_withdrawal` / `cancel_surplus_withdrawal`. Absent when no
/// withdrawal is proposed.
pub const KEY_PENDING_SURPLUS_WITHDRAWAL: Symbol = symbol_short!("pend_wd");

// --- Singleton keys: persistent storage ---
// Persistent-storage singletons owned by the delegate module.
Expand Down Expand Up @@ -146,7 +157,7 @@ mod tests {
use super::*;

/// Every singleton key, both storage families.
fn singleton_keys() -> [Symbol; 22] {
fn singleton_keys() -> [Symbol; 24] {
[
KEY_ADMIN,
KEY_PENDING_ADMIN,
Expand All @@ -170,6 +181,8 @@ mod tests {
KEY_DELEGATES,
KEY_DELEGATE_HISTORY,
KEY_DELEGATE_EXPIRY,
KEY_SURPLUS_WITHDRAWAL_DELAY,
KEY_PENDING_SURPLUS_WITHDRAWAL,
]
}

Expand Down
Loading
Loading