Documentation Index
Fetch the complete documentation index at: https://docs.keystoneos.xyz/llms.txt
Use this file to discover all available pages before exploring further.
The KeystoneEscrow contract is a minimal lock/release/rollback mechanism deployed on every chain where settlements occur. It holds deposits, automatically notifies the SettlementCoordinator when all deposits are complete, and receives release/rollback instructions from the coordinator.
A lock box, not a bank. The escrow contract does not calculate interest, manage collateral ratios, handle liquidations, or hold assets beyond the settlement window. It has no lending logic of any kind. No upgrade authority, no KeyStone custody.
Access control
The escrow uses OpenZeppelin AccessControl with two roles:
| Role | Holder | Permissions |
|---|
OPERATOR_ROLE | SettlementCoordinator contract | registerSettlement, executeSettlement, rollbackSettlement |
DEFAULT_ADMIN_ROLE | Deployer / multisig | pause, unpause, setCrossChainMode, setLzReceiveGasLimit, role management |
This separates operational access (the coordinator orchestrates settlements) from administrative access (the deployer can pause in emergencies or configure cross-chain settings). The coordinator never needs admin privileges, and the admin never needs to touch settlement operations.
Functions
depositLeg()
Deposits tokens for a specific leg using the commitment scheme. Called directly by the settlement party (or their custody provider) with the deposit secret.
function depositLeg(
bytes32 settlementId,
uint256 legIndex,
bytes32 depositSecret
) external
The contract validates:
- Settlement is registered and not in a terminal state
keccak256(depositSecret) == leg.depositKey - the caller knows the secret
- The leg has not already been deposited
- Token allowance is sufficient
Privacy note: No party addresses are registered at settlement creation time. Authorization is based on knowledge of the deposit secret (a random 32-byte value), not on msg.sender. The party’s address is only revealed on-chain when they submit the deposit transaction.
When all legs for a settlement are deposited, the escrow emits an AllDepositsComplete event. The API’s event indexer watches for this event and calls coordinator.notifyDepositsComplete() to advance the settlement. For cross-chain settlements, the escrow sends a LayerZero message to the coordinator.
Emits: LegDeposited(bytes32 settlementId, uint256 legIndex, address depositor, uint256 amount)
executeSettlement()
Releases all deposits to their intended recipients. Called by an account with OPERATOR_ROLE (the coordinator, directly or via LayerZero) after the atomicity gate confirms all legs are deposited. Recipient addresses are revealed at this point - they are not stored on-chain during registration.
function executeSettlement(
bytes32 settlementId,
address[] calldata recipients
) external
In a single transaction:
- Tokens from each leg are transferred to the corresponding recipient
- Fees (if any) are sent to the fee recipient addresses registered in the LegSpec
For ERC-3643 tokens, the contract uses forcedTransfer() which allows the token’s transfer agent to move tokens between whitelisted addresses.
Emits: SettlementExecuted(bytes32 settlementId)
rollbackSettlement()
Returns all deposited tokens to their original depositors. Called by an account with OPERATOR_ROLE (the coordinator) on failure or timeout.
function rollbackSettlement(bytes32 settlementId) external
Each depositor receives back exactly what they deposited. No fees, no penalties.
Emits: SettlementRolledBack(bytes32 settlementId)
claimTimeout()
Permissionless timeout claim. Anyone can call this after the settlement deadline to return deposits.
function claimTimeout(bytes32 settlementId) external
Validates block.timestamp >= timeoutAt and returns all deposits to their original owners.
Emits: SettlementTimedOut(bytes32 settlementId)
View functions
function allLegsDeposited(bytes32 settlementId) external view returns (bool)
function getLegStatus(bytes32 settlementId, uint256 legIndex) external view returns (LegStatus)
Events
| Event | When | Data |
|---|
LegDeposited | A party deposits tokens for a leg | settlementId, legIndex, depositor, amount |
SettlementExecuted | Atomic swap completed | settlementId |
SettlementRolledBack | All deposits returned on failure | settlementId |
SettlementTimedOut | Timeout triggered, deposits returned | settlementId |
Deposit completion flow
When all legs are deposited, the escrow emits an AllDepositsComplete event. The API’s event indexer detects this and calls notifyDepositsComplete on the coordinator to advance the settlement:
The escrow no longer calls notifyDepositsComplete synchronously. This separation allows the API to add additional validation or logging before advancing the settlement.
Token standards
| Standard | Support | Release mechanism |
|---|
| ERC-20 | Full | Standard transfer() |
| ERC-3643 | Full | forcedTransfer() via transfer agent role |
Cross-chain mode
In Phase 2 (cross-chain), escrow contracts on remote chains communicate with the coordinator via LayerZero:
Security properties
- Role-based access: Operational functions (register/execute/rollback) are restricted to
OPERATOR_ROLE (the coordinator). Admin functions (pause/config) are restricted to DEFAULT_ADMIN_ROLE (deployer/multisig). No single key can do both.
- No upgrade authority: Escrow contracts are immutable - no proxy pattern, no self-destruct
- Commitment-based deposit authorization: Deposit rights are proven by knowledge of a secret, not by address. Party addresses are never stored on-chain at registration time.
- Pre-execution privacy: On public chains, counterparty relationships are hidden until execution. An observer sees deposit keys (hashes), not addresses.
- Recipient reveal at execution: Recipient addresses are only revealed when the coordinator calls
executeSettlement, not at registration time.
- Atomic execution: All legs release in one transaction or none do
- Deposit complete event: Escrow emits AllDepositsComplete when all legs are deposited; API calls coordinator to advance
- Permissionless timeout: Anyone can recover funds after the deadline
- No custody risk: KeyStone never controls deposited funds