When to Use
A forced exit applies when:- The order’s output deadline has passed (the fill window expired)
- The solver did not fill the order
- The solver did not issue a refund
- The user’s deposit was attested and minted on the Hub, but the balance was never transferred to a solver
How It Works
The forced exit flow spans the Relay Chain, Aurora (where the Allocator is deployed), and the origin chain where the user’s deposit sits in the Depository. Step by step:-
Force exit on Relay Chain — The user calls
forceExiton the ForcedExit contract, passing the order and deposit info. The contract verifies the output deadline has passed, computes the intent address, and transfers the intent’s Hub balance to a deterministic withdrawal address. AForcedExitExecutedevent is emitted with the withdrawal details. -
Submit withdrawal request — The user calls
submitWithdrawRequeston the Allocator, passing the withdrawal parameters from the force exit event. The Allocator stores the request and starts the security delay timer. - Wait for security delay — A configurable delay must pass before the withdrawal can be signed. This gives the Security Council time to intervene if something is wrong.
-
Sign withdrawal payload — After the delay, the user calls
signWithdrawPayloadHashon the Allocator. This triggers the MPC signing process via the NEAR chain signatures network. The user polls for the signature. - Execute on origin chain — The user submits the MPC-signed proof to the Depository on the origin chain. The Depository verifies the signature and releases the funds.
Manually Perform a Forced Exit
This section walks through each contract interaction. You need:- The order JSON (the original order struct)
- The deposit info (timestamp and depositor address for each input)
- A wallet with ETH on the Relay Chain and Aurora for gas
Step 1: Call forceExit
Call ForcedExit.forceExit(order, deposits) on the Relay Chain. Ensure the order’s output deadline has passed.
order and deposits arguments:
ForcedExitExecuted event:
withdrawalAddress, amount, and withdrawalNonce from this event — you need them for the next step.
Step 2: Submit Withdrawal Request
CallRelayAllocator.submitWithdrawRequest(params) on Aurora, using the values from the force exit event.
Step 3: Wait for Security Delay
After submitting, a security delay must pass before the payload can be signed. Query the delay:delay seconds to pass from the time of the submitWithdrawRequest transaction.
Step 4: Sign the Withdrawal Payload
After the delay, trigger MPC signing:Step 5: Execute on Origin Chain
Submit the MPC-signed proof to the Depository on the origin chain:Full Script
Using the Forced Exit UI
An open-source forced exit frontend is in development. Documentation will be added here once available.
Costs
- Gas on Relay Chain and Aurora — Step 1 is on the Relay Chain, steps 2-4 are on Aurora. Gas costs on both are minimal.
- Gas on origin chain — Step 5 requires a transaction on the origin chain to execute the withdrawal.
- No protocol fees — Forced exits do not incur additional protocol fees beyond gas costs.
Common Errors
| Error | Cause |
|---|---|
DeadlineNotPassed(deadline, currentTime) | The order’s output deadline hasn’t expired yet. Wait until block.timestamp > order.output.deadline. |
NoBalanceToExit(intentAddress, tokenId) | The intent address has zero balance on the Hub. The deposit may not have been attested, or a solver already claimed it. |
NoRefundConfigured(inputIndex) | The order input at this index has no refund configuration. |
ChainNotConfigured(chainId) | The target chain is not registered in the ForcedExit contract. |