# Escrow add-on

Source: https://docs.settlemint.com/docs/concepts/custody-and-signing/escrow
An on-chain contract that holds tokens for their owners. One controller locks and pays units, and a token custodian can still force-transfer them.

An escrow is an on-chain contract that holds tokens for their owners while something is pending, such as an order waiting to fill, a cash-out waiting for the bank, or a custody gateway withdrawal waiting to be paid. Each escrow has one controller, such as a trading venue, a custody gateway or a bank rail wallet, which locks the owners' units and pays them out. Without an escrow, each feature would keep its own pooled balance with its own accounting, and the escrow gives every controller the same lock, payout and compliance rules. Every DALP system, the set of contracts an organisation deploys on a network, gets an escrow factory, and a controller asks that factory for an escrow it controls. A holder of the token's custodian role can still move an owner's units out of an escrow with a forced transfer, even while the owner is frozen.

A withdrawal from a managed custody wallet uses no escrow, and its hold is a gateway withdrawal record, as [Custody deposits and withdrawals](https://docs.settlemint.com/docs/concepts/custody-and-signing/custody-deposits-and-withdrawals) explains.

## What holds tokens in an escrow [#what-holds-tokens-in-an-escrow]

Three features ask the factory for an escrow. Each escrow is labelled when it is created, and the label is how an operator tells escrows apart.

| Feature | Escrows | What waits in the escrow |
| --- | --- | --- |
| Trading venue | One per venue, created with the venue and labelled with its name | The units each open order commits, until the order fills, is cancelled or expires. Each fill's legs, until delivery. |
| Custody gateway | One per client gateway, created with the gateway | Deposited tokens held back for screening, the gateway's own funding that pays withdrawals, and the custody receipts a client returns to withdraw |
| Bank rail wallet | One per network for the rail wallet of a delegate-mode token link, labelled "Bank rail" | The tokens of each cash-out, locked until the bank pays or the cash-out is released |

An XvP settlement does not use an escrow. Each sender's leg sits in the settlement contract itself until the settlement executes, is cancelled or expires. [XvP settlement](https://docs.settlemint.com/docs/concepts/settlement/xvp-settlement) explains that flow.

When a controller deposits for an owner, the escrow pulls the tokens from an approval the owner gave the escrow itself. A trader approves the venue's escrow once, and a bank rail client approves the rail wallet's escrow before a cash-out, so the escrow never takes more than the owner approved.

## Who controls an escrow [#who-controls-an-escrow]

An escrow has exactly one controller, fixed when it is created, and no roles of its own. Only the controller locks, unlocks, relocks and pays. Others can still move units without it: a token custodian through a [forced transfer](#forced-transfer), a [lost-wallet recovery](#recovering-a-lost-wallet) that moves unlocked units to the replacement wallet, the token's supply management role by burning at the escrow's address, a feature attached by the token's governance role together with Supply management and Custodian or the admin role, and a system manager or the add-on registry replacing the escrow's code. A controller that needs a second arrangement asks the factory for a second escrow.

Two kinds of caller can create an escrow: an add-on factory registered in the system, or a wallet that holds the system's escrow manager role. The bank rail is not an add-on, so it creates an escrow for its rail wallet through the escrow manager route. A controller of the zero address is refused.

Each escrow gets its own on-chain identity, registered with a country the creator names, and is placed on the system's compliance bypass list so that owners can deposit into it. On a token with the current compliance contract, a transfer into a bypass-listed address skips the rules that judge the recipient's eligibility, such as country and identity lists and identity verification. The address block list still checks the escrow's address.

Every escrow runs the implementation the factory points to. A holder of the system manager role, or the add-on registry, can move that pointer, which changes the code of every escrow of the system at once. The factory asks for no second approval, and it checks only that the new code reports two interfaces through ERC-165: the escrow interface and the interface of a contract with its own on-chain identity.

## What the escrow books [#what-the-escrow-books]

Per token and per owner, the escrow keeps an unlocked balance and any number of locks. The unlocked balance is what the owner may withdraw, which the API returns as `free` and the Console shows as `Free`.

- A deposit books an amount to the owner's unlocked balance. The owner deposits directly, or the controller deposits on the owner's behalf, pulling from the owner's approval. The escrow must receive exactly the amount it books, so a fee-on-transfer or rebasing token reverts.
- A withdrawal pays the caller's unlocked balance to the caller. It is refused while the token reports the caller frozen.
- A lock holds units until a date, after which the owner may take them back. A lock can also carry no owner exit, for units the owner already agreed to hand over, such as a matched trade.

Every unit an owner deposits is booked, so what the escrow owes in a token stays at or below its balance of that token. The exception is a token that moves tokens out itself, which [Sweep, and what the escrow cannot prevent](#sweep-and-what-the-escrow-cannot-prevent) explains. The contract's `owed` view reports what the escrow owes its owners in a token, and its `lockOf` view reports one lock, with an unopened identifier reading as all zeros.

The controller picks each lock's identifier. An identifier can be used once and stays used after the lock empties, so every lock and every event stays unambiguous. A new lock's end date must lie in the future unless the lock carries no owner exit.

## How a controller moves units [#how-a-controller-moves-units]

Only the controller posts moves, and a call applies its list of moves all or nothing. There are four kinds.

| Move | What it does |
| --- | --- |
| Lock | Takes units from the owner's unlocked balance into a new lock. |
| Unlock | Returns all or part of a lock to the owner's unlocked balance. |
| Relock | Moves all or part of a lock into a new lock of the same owner and token. |
| Pay | Pays from a lock to any eligible wallet, or from the unlocked balance to the owner only. |
| Book | Moves units from a lock into another owner's unlocked balance in the same escrow, with no token transfer. |

An Unlock, Relock, Pay or Book can take the whole remainder of a lock. A Book makes no call to the token, so it runs no compliance check. It still refuses a frozen lock owner, and a recipient that is the zero address, the escrow itself or on the bypass list. The trading venue uses it to park a delivery leg whose receiver the token refused: the units wait in the receiver's unlocked balance, and the receiver's withdraw runs the token's compliance again. A Relock that would move nothing reverts, so relocking a lock that a payout already emptied fails. A Pay from the unlocked balance that names anyone other than the owner reverts. A controller moves an owner's unlocked units to anyone else only by locking them first and paying the lock.

A Pay is the token's own transfer from the escrow, so the token's compliance runs on it and the owner's freeze check applies. One more rule covers recipients on the compliance bypass list:

- A Pay to a bypass-listed recipient is refused. A token's compliance can skip recipient-side checks for a bypassed account, so without this rule a controller could pay an owner's lock into a second escrow it controls.
- The controller itself is the exception. A custody gateway pays a client's locked custody receipts to itself to burn them when the client collects a withdrawal.

Anyone may reclaim an expired lock. Reclaim books the whole remaining lock back to its owner's unlocked balance and calls no token, so a token's rules cannot block it. A lock with no owner exit can never be reclaimed.

[Figure] How an owner's units move through an escrow. An owner wallet deposits into the owner's unlocked balance and withdraws from it. The controller locks units from the unlocked balance into a lock, which carries a reclaim date or no owner exit. A lock returns to free through an unlock by the controller or a reclaim by anyone after the date. A relock moves a lock into a new lock of the same owner and token. A Pay, the highlighted path, sends a lock to an eligible recipient through the token's compliance. Labels: deposit; withdraw; Lock; Unlock, reclaim; Relock; Pay; Owner wallet; approves the escrow; once; Unlocked balance; withdraw at any time; leaves only to its owner; Lock; Controller only; held until a date,; or with no owner exit; New lock; same owner and token; new date; Eligible recipient; the token's compliance; runs on the payout.

Deposits and withdrawals move the unlocked balance, the controller locks from it, and a lock leaves by unlock, reclaim, relock or a Pay under token compliance.

## The escrow claim and token compliance [#the-escrow-claim-and-token-compliance]

Some compliance rules check how long the sender has held the units. Every payout comes from the escrow, so those rules would block payouts until the escrow itself had held the units long enough.

The factory issues a `dalpEscrow` claim to each escrow's identity, and the Console shows the claim's topic as "Escrow account". By default the platform binds a time-lock rule to that claim as a sender exemption. A sender that carries the claim skips the rule's check, so a payout from the escrow passes. The rule runs in accounting-only mode for that sender: it still records the transfer but does not refuse it. The owner's deposit runs the token's sender-side rules, such as the time lock and the address block list, with the owner as sender. The recipient-side rules, such as country and identity lists, identity verification and transfer approval, skip the escrow because it is on the bypass list. On a token that uses the first version of the system compliance contract, a deposit into the escrow skips every rule. Units paid back to their owner restart the owner's holding clock. [Composable token](https://docs.settlemint.com/docs/concepts/modeling-assets/composable-token) describes the compliance modules.

A minimum-holding rule is configured differently: it lists `dalpEscrow` among its exempt claims by default. The escrow's own pool may then sit below the minimum, while the owner on a deposit and the recipient on a payout still have to clear it. The issuer can change or remove either setting in the token's compliance settings.

The factory issues the claim when the system has registered the `dalpEscrow` topic and trusts the factory's identity to issue it. Until a system has both, a new escrow gets no claim, these exemptions do not apply to its payouts, and anyone may add the claim later through the factory.

## A corporate action re-bases the escrow [#a-corporate-action-re-bases-the-escrow]

A stock split, bonus issue or other [corporate action](https://docs.settlemint.com/docs/console/asset-management/manage/corporate-actions) that changes every holder's units by a ratio changes what an escrow holds for its owners. The platform counts each owner's free balance and each open lock as if the owner held them directly, and the action mints to or burns from the escrow the sum of those amounts. The escrow keeps a per-token log of the action's steps. Each free balance and each lock moves to the new basis the next time it is read or used, with the action's own rounding, so a round-up-to-one never leaves the escrow owing more than it holds.

To keep the records equal to the units while that happens, the action freezes the escrow for the token. From the action's record date until its step lands, the escrow refuses deposits, withdrawals, reclaims, new locks, relocks, payouts from a lock, sweeps, forced transfers and recoveries of that token. The refusal names the token and the action. An unlock of a whole lock is still accepted, because a venue cancels an order that way: the lock is released to its owner, and its units move to the owner's free balance once the action's step has landed. An unlock of part of a lock is refused. An account with the token's supply management role installs the freeze before the record date, and writing the step lifts it in the same call. A trading venue or custody gateway that rides the escrow is frozen with it, apart from the venue's own order cancels and the gateway's returns caused by the action.

## Sweep, and what the escrow cannot prevent [#sweep-and-what-the-escrow-cannot-prevent]

Tokens can reach the escrow address by a plain transfer that books them to no owner. A sweep pays out exactly the part of the token balance that exceeds what the escrow owes, and only the controller may send it. When the balance is at or below the debt, the sweep reverts with nothing to sweep.

The token can force-transfer from the escrow's address, burn from it, or apply a negative rebase, a supply adjustment that lowers every balance. The escrow cannot prevent any of these. Such a move can leave it owing more than it holds. `owed` reports the debt so monitoring can compare it with the balance.

## Forced transfer [#forced-transfer]

A holder of the token's custodian role, the role that may freeze an address and force a transfer, can move an owner's units out of the escrow, even while the owner is frozen. The transfer draws the owner's unlocked balance first, then each lock the caller names, in the order given. Its event names each lock and the amount drawn from it. A lock of another owner or token is refused, and so is an amount larger than the unlocked balance plus the listed locks. Through the Platform API, a lock that is no longer open is refused as well.

For a DALP token the caller must hold the token's custodian role. For a plain ERC-20 only the controller may do it. Either way the recipient must still be one the token accepts.

Through the Platform API, a token custodian sends `POST /api/v2/addons/escrows/{escrowAddress}/forced-transfers` with the token, the owner, the recipient, the amount and up to 32 lock identifiers. The request needs wallet verification, and it takes an `Idempotency-Key` header for safe retries. The route decides whether units that a bank rail cash-out still depends on may move, and where they go. The escrow contract checks none of that, so a custodian who calls the escrow directly can move those units to any recipient the token accepts. [Empty an escrow the old wallet controls](https://docs.settlemint.com/docs/api-reference/guides/bank-rail#empty-an-escrow-the-old-wallet-controls) lists the route's rules. [Escrows in the Console](https://docs.settlemint.com/docs/console/asset-lifecycle/escrows) shows how to find the owner, token and lock identifiers.

The Platform API refuses a forced transfer from an escrow sent through the token's own route, `POST /api/v2/tokens/{assetId}/forced-transfers`, both at its preflight and at the write, with `422 TOKEN_FORCED_TRANSFER_FROM_ESCROW` and a message that names the escrow, because it would leave the escrow's balances and locks wrong. Send it through this route instead. The token contract does not refuse it.

## Recovering a lost wallet [#recovering-a-lost-wallet]

Anyone may move a lost wallet's unlocked balance to the wallet its identity registry names. The registry must mark the old wallet lost and name its replacement, and the token must not report the old wallet frozen. Locks stay with the lost wallet until they are unlocked or reclaimed, so recovery moves unlocked units only. A holder of the identity manager role marks the wallet lost and names its replacement, so that role decides where the units go.

## Reading escrows [#reading-escrows]

A platform administrator or the organisation's owner can list escrows and open one to see its balances, locks and history. Every other caller gets not found.

- `GET /api/v2/addons/escrows` lists every escrow the system's escrow factory created, with the kind of add-on that controls it, the controller, how many tokens it holds, what it holds per token and its open locks. The add-on kind is `trading-venue` or `other`, and `other` covers every controller that is not a trading venue, such as a custody gateway or a bank rail wallet. The list takes a page window, `filter[addonKind]`, and a `sort` over label, controller, token count, open lock count or creation time, with a leading minus for descending. `meta.facets` counts every escrow in scope per add-on kind, whatever the filter.
- `GET /api/v2/addons/escrows/{escrowAddress}` reads one escrow with its evidence: the label it was given, its controller, its factory, its identity and country, and its creation time.
- `GET /api/v2/addons/escrows/{escrowAddress}/balances` lists each owner's unlocked and locked units per token.
- `GET /api/v2/addons/escrows/{escrowAddress}/locks` lists each lock with its owner, token, amount, reclaim time, status and open time, and takes `filter[status]`.
- `GET /api/v2/addons/escrows/{escrowAddress}/events` lists the escrow's history, newest first.

The balances, locks and events reads each take a page window and their own `sort`. An escrow outside the caller's system, or one whose escrow factory is archived, answers as not found.

A lock status is `open` while units remain. Once a lock empties, the status names the move that emptied it: `unlocked`, `relocked`, `paid`, `booked`, `reclaimed` or `forced`.

The Console shows the same escrows under Asset lifecycle › Escrows, described in [Escrows](https://docs.settlemint.com/docs/console/asset-lifecycle/escrows).

## Related pages [#related-pages]

- [Composable token](https://docs.settlemint.com/docs/concepts/modeling-assets/composable-token): the compliance modules that decide whether a deposit or a payout passes.
- [Custody gateway](https://docs.settlemint.com/docs/concepts/custody-and-signing/custody-gateway): an add-on that asks the factory for one escrow per client gateway.
- [Custody receipt](https://docs.settlemint.com/docs/concepts/custody-and-signing/custody-receipt): the token a client returns to its gateway, locked in the gateway's escrow, when it withdraws.
- [Trading venue](https://docs.settlemint.com/docs/concepts/issuing-and-markets/trading-venue): an add-on that holds an order's committed units in its escrow until a fill, a cancel or expiry.
- [XvP settlement](https://docs.settlemint.com/docs/concepts/settlement/xvp-settlement): settlement that holds each side's leg in its own contract until the exchange.
- [Bank rail API](https://docs.settlemint.com/docs/api-reference/guides/bank-rail): the cash-out flow that locks a client's tokens in the rail wallet's escrow.
- [Custody deposits and withdrawals](https://docs.settlemint.com/docs/concepts/custody-and-signing/custody-deposits-and-withdrawals): the direct custody route, which holds in the gateway withdrawal record and uses no escrow.
