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
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ If a bug turns up after mainnet, the alternative to a pause is asking every
integrator to stop calling the contract. Since issue #35 there is a narrow
on-chain lever instead: one `pause_admin` address, fixed at deployment by
the contract's constructor, can flip a flag that makes every deposit path
fail with `Paused` (provisional error code 3500; the maintainer fold
renumbers it).
fail with `Paused` (error code 38).

Nothing else changes. `release`, `refund`, the timeout refund, and both
`settle_many` ops (release and refund) never read the flag, so **a pause can never lock funds that are already escrowed** —
Expand Down
19 changes: 17 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,8 @@ An opt-in creation method can require the payee to authorize `accept` before rel
Alternatives rejected in the ADR: permissionless pause (trivial
griefing), auto-expiring pause (silently lapses mid-incident), token
allowlist (a different problem), and doing nothing. New error codes
`Paused`, `AlreadyPaused` and `NotPaused`, added with the provisional
codes 3500–3502 (`contract-changes.d/35-emergency-pause.md`);
`Paused`, `AlreadyPaused` and `NotPaused`, with codes
38–40 (contract 2.5.0);
`Paused`/`Unpaused` events carry the custodian. 20 tests in
`contracts/escrow/tests/pause.rs`, including release, releaser refund,
stranger timeout refund, and release-after-deadline while paused, plus a
Expand Down Expand Up @@ -1049,3 +1049,18 @@ An opt-in creation method can require the payee to authorize `accept` before rel
- Issue #40: allow the stored payee to authorize a voluntary full refund of a funded single-payee escrow to its stored payer destination, and emit a distinct `payee_refunded` event.
- Issue #45 (Phase E5): new `transfer_payee(escrow_id, current_payee, new_payee)` entry point — only the current payee may authorize it, only while the escrow is `Funded`, and `new_payee` may be neither the contract address nor the payer. Emits a `pay_xfer` event and adds `Error::InvalidNewPayee` (code 37). Rotation tool, not key-loss recovery; covered by `contracts/escrow/tests/transfer_payee.rs`.
Final error codes: Error::InvalidNewPayee 4500 -> 37.
- 2026-10-31 — Issue #35 (Phase E5): deposit-only emergency pause. The contract's
`__constructor` now also takes a `pause_admin` address; only that custodian
can call `pause`/`unpause`, and while paused every deposit path fails with
`Paused` before any funds move — including pooled `create_pooled_escrow`
and `contribute` (`Paused`, plus `AlreadyPaused`/`NotPaused` for the
transition guards — codes 38–40). `release`, `refund`, the timeout refund and pooled
`claim_refund` never read the flag, so a
pause can stop new exposure but never lock escrowed funds (invariant I8,
threat model T12, `docs/adr/0011`). The SDK gains `pause`/`unpause`/
`isPaused`/`getPauseAdmin` (minor changeset) and regenerated bindings. This
is a deployment change: the live testnet contract predates the constructor
and cannot pause until it is redeployed.
- 2026-10-31 — Contract 2.5.0 (was 2.4.0), folded from 1 change(s):
- Issue #35 (Phase E5): deposit-only emergency pause (ADR 0011). The constructor gains a second argument (`pause_admin`) and the contract gains `pause`/`unpause`/`is_paused`/`get_pause_admin` entry points plus the `Paused`/`Unpaused` events and the `Paused`, `AlreadyPaused`, `NotPaused` errors (codes 38–40). Every deposit path is gated — `create_escrow` and its variants, `top_up`, and pooled `create_pooled_escrow`/`contribute` — while `release`, `refund`, the timeout refund, pooled `claim_refund` and both `settle_many` ops never read the flag, so a pause can never lock funds already escrowed.
Final error codes: Error::Paused 3500 -> 38, Error::AlreadyPaused 3501 -> 39, Error::NotPaused 3502 -> 40.
13 changes: 0 additions & 13 deletions changelog.d/35-emergency-pause.md

This file was deleted.

11 changes: 0 additions & 11 deletions contract-changes.d/35-emergency-pause.md

This file was deleted.

12 changes: 5 additions & 7 deletions contracts/escrow/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ use soroban_sdk::{

// Bump this value whenever a contract interface or behavior change is made.
// CI checks this constant against the base branch when this file changes.
pub const CONTRACT_VERSION: &str = "2.4.0";
pub const CONTRACT_VERSION: &str = "2.5.0";

include!(concat!(env!("OUT_DIR"), "/contract_metadata.rs"));

Expand Down Expand Up @@ -366,16 +366,14 @@ pub enum Error {
/// retryable and may still use its refund path.
Undeliverable = 36,
/// A deposit path was called while the emergency pause is on — see
/// docs/adr/0011. `main` already spent 35 (`InvalidFee`) and 36
/// (`Undeliverable`), so this uses a provisional code (issue #35 × 100);
/// the maintainer fold renumbers it — see contract-changes.d/README.md.
Paused = 3500,
/// docs/adr/0011.
Paused = 38,
/// `pause` on an already-paused contract. The transition did not
/// happen, so no event is emitted.
AlreadyPaused = 3501,
AlreadyPaused = 39,
/// `unpause` on a running contract. The transition did not happen, so
/// no event is emitted.
NotPaused = 3502,
NotPaused = 40,
}

#[contract]
Expand Down
9 changes: 4 additions & 5 deletions docs/adr/0011-emergency-pause-for-new-deposits-only.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ Option 1.
an event never claims a transition that didn't happen.
3. **Scope: new deposits only.** Every creation entry point funnels through
`create_escrow_inner`, which checks the flag first and returns the new
`Error::Paused` (provisional code 3500) without pulling funds, without
`Error::Paused` (code 38) without pulling funds, without
consuming an escrow id, and without writing an escrow. `top_up` — a deposit path added after
this ADR was first written — applies the same check before its transfer,
exactly as the ADR originally required of any future deposit path.
Expand Down Expand Up @@ -178,10 +178,9 @@ the deadline.
keys (`PauseAdmin`, `Paused`) are added. Existing escrow entries are
unaffected — and there is no migration to worry about, because a
redeploy is a new contract id by definition.
- **New error codes**: `Paused`, `AlreadyPaused`, `NotPaused`, added with
the provisional codes 3500–3502 (issue #35 × 100 + n) described in
`contract-changes.d/README.md`; the maintainer fold assigns the final
numbers. Every existing code keeps its number, because consumers map
- **New error codes**: `Paused`, `AlreadyPaused`, `NotPaused`, codes 38–40,
assigned by the contract 2.5.0 maintainer fold from the provisional codes
3500–3502 (issue #35 × 100 + n) described in `contract-changes.d/README.md`. Every existing code keeps its number, because consumers map
codes, not names. They were originally reserved as 26–28, but batch
settlement (`BatchTooLarge = 25`), pooled escrows (`NotPooled` through
`InvalidFundingDeadline`, 26–34) and the protocol fee / outbound-transfer
Expand Down
Loading
Loading