Withdraw
The Withdraw Facet is the primary interface for the new multi-provider withdrawal system introduced in v0.8.5. It replaces the legacy single-queue withdrawal mechanism with a more flexible architecture that supports classic (same-chain, cooldown-based), express (instant, provider-fronted), and virtual (cross-chain, provider-mediated) withdrawals.
The withdrawal cooldown is now tied to the user's last deallocation time (deallocateTimestamp), not the withdrawal initiation. If funds have been idle for the full cooldown period (typically 12 hours), withdrawals can be finalized immediately after initiation. Subsequent deallocations do not affect already-initiated withdrawals.
Overview
The Withdraw Facet provides the following key functionalities:
Initiate Withdrawal: Users create a withdrawal request with one or more receiver parts, each optionally specifying an express and/or virtual provider.
Provider Accept/Reject: Registered providers accept or reject withdrawal requests via callbacks.
Finalize Withdrawal: After cooldown expiry, anyone can finalize the request, triggering token transfers and provider notifications.
Cancel Withdrawal: Users request cancellation; behavior depends on the withdrawal type and provider state.
Force Cancel: Admins can force-cancel any withdrawal before cooldown expiry.
Accept Cancel: Providers accept user-initiated cancellation requests.
Suspend Withdrawal: Admins can suspend problematic withdrawal requests.
Speed-Up: Designated users can request reduced cooldowns, subject to admin approval.
Withdrawal Types
A withdrawal request consists of one or more receiver parts. The combination of providers across parts determines the withdrawal type:
Normal (Classic)
—
—
Standard cooldown, same-chain delivery. No provider involvement.
Pure Express
Yes
—
Instant same-chain. Express provider fronts funds, reclaims after cooldown.
Pure Virtual
—
Yes
Standard cooldown, cross-chain delivery via virtual provider.
Virtual Express
Yes
Yes
Instant cross-chain. Express provider fronts, virtual provider delivers.
Each request has a single coordinating provider — the entity Symmio calls, that accepts the request, and that gets notified at finalization. For pure-virtual requests, the virtual provider is the coordinator, so all parts must use the same one. For express and virtual-express requests, the express provider is the coordinator; individual parts may reference different virtual providers since the express provider manages them off-chain.
Cooldown Mechanics
If the cooldown has already elapsed since the last deallocation,
cooldownEndTimeequalsblock.timestampand the withdrawal can be finalized immediately.The
cooldownEndTimeis stored in the request and returned byinitiateWithdraw.Use
getWithdrawableTime(user)on the ViewFacet to preview when a withdrawal initiated now could be finalized.
initiateWithdraw()
Creates a new withdrawal request. The user's internal balance is debited immediately. If providers are referenced, they receive onWithdrawRequest callbacks.
Function Signature:
Parameters:
parts: Array of withdrawal instructions. Each part specifies an amount, destination chain, receiver address, and optional express/virtual providers.speedUp: Whether to request a speed-up for this withdrawal (user must be on the speed-up whitelist).data: Additional provider-specific metadata (e.g., signed withdrawal options from the provider's bot).
Returns: The request ID and the computed cooldown end timestamp.
Events Emitted:
WithdrawInitiated(uint256 requestId, address user, WithdrawReceiverPart[] parts, bool speedUp, bytes data, uint256 cooldownEndTime)
acceptWithdrawRequest()
Provider accepts a withdrawal request, transitioning it to PROVIDER_ACCEPTED status. Must be called by the provider contract itself (not an operator).
If any express provider part exists, the express provider must accept. Otherwise, the single virtual-only provider must accept.
Function Signature:
Parameters:
user: The owner of the withdrawal request.requestId: The ID of the withdrawal request.
Access: Only callable by the registered provider contract.
Events Emitted:
WithdrawAccepted(uint256 requestId, address user)
rejectWithdrawRequest()
Provider rejects a withdrawal request, refunding the user's internal balance immediately. The request transitions to PROVIDER_REJECTED status.
Function Signature:
Parameters:
user: The owner of the withdrawal request.requestId: The ID of the withdrawal request.
Access: Only callable by the registered provider contract.
Events Emitted:
WithdrawRejected(uint256 requestId, address user)
finalizeWithdrawRequest()
Finalizes a withdrawal request after the cooldown has expired. Can be called by anyone. Classic parts transfer tokens directly to receivers. Express provider parts reimburse the provider. Virtual provider parts trigger onWithdrawComplete callbacks.
Function Signature:
Parameters:
user: The owner of the withdrawal request.requestId: The ID of the withdrawal request.
Preconditions: The request must be in PENDING (classic-only), PROVIDER_ACCEPTED, or CANCEL_REQUESTED status. The cooldown must have expired.
Events Emitted:
WithdrawFinalized(uint256 requestId, address caller)
requestCancelWithdraw()
User requests cancellation of a withdrawal. The outcome depends on the request's current state and type:
PENDING (classic): Refunded immediately, status →
CANCELLED.PROVIDER_ACCEPTED (pure virtual, outside blackout): Refunded immediately, provider notified via
onWithdrawCancelRequest, status →CANCELLED.PROVIDER_ACCEPTED (pure virtual, inside blackout): Transitions to
CANCEL_REQUESTED, awaits provider approval.PROVIDER_ACCEPTED (express involved): Transitions to
CANCEL_REQUESTED, provider receives callback.
Function Signature:
Parameters:
requestId: The ID of the withdrawal request.
Events Emitted:
WithdrawCancelRequested(uint256 requestId, address user)
acceptWithdrawCancelRequest()
Provider accepts a user's cancellation request, refunding the remaining provider-held parts and marking the request CANCELLED.
Function Signature:
Parameters:
user: The user who initiated the withdrawal.requestId: The ID of the withdrawal request.
Access: Only callable by the registered provider contract.
Events Emitted:
WithdrawCancelled(uint256 requestId, address user)
forceCancelWithdraw()
Admin force-cancels a withdrawal request before the cooldown expires. Works on all withdrawal types. The user's locked balance is refunded, and providers are notified via onForceWithdrawCancel callbacks.
Function Signature:
Parameters:
user: The owner of the withdrawal request.requestId: The ID of the withdrawal request.
Access: Requires WITHDRAW_FORCE_CANCEL_ROLE.
Preconditions: Request must be in PENDING, PROVIDER_ACCEPTED, or CANCEL_REQUESTED status.
Events Emitted:
WithdrawCancelled(uint256 requestId, address user)
suspendWithdrawRequest()
Admin suspends a problematic withdrawal request. Suspended requests cannot progress until manually resolved.
Function Signature:
Parameters:
user: The user who initiated the withdrawal.requestId: The ID of the withdrawal request.
Access: Requires SUSPENDER_ROLE.
Events Emitted:
WithdrawSuspended(uint256 requestId, address user)
acceptSpeedUpRequest()
Admin accepts a speed-up request for a withdrawal, reducing its cooldown period. The user must have been added to the speed-up whitelist and must have initiated the withdrawal with speedUp = true.
Function Signature:
Parameters:
user: The user who initiated the withdrawal.requestId: The ID of the withdrawal request.newCooldown: The new cooldown duration in seconds. Must be at leastminWithdrawCooldown.
Access: Requires WITHDRAW_SPEED_UP_ROLE.
Events Emitted:
WithdrawSpeedUpAccepted(uint256 requestId, address user, uint256 newCooldown)
Data Structures
Pure Virtual Cancel Blackout
For pure virtual withdrawals, a configurable pureVirtualCancelBlackout period creates a window before cooldown expiry during which users cannot cancel. This gives virtual providers certainty that accepted requests won't be cancelled at the last moment, since they may have already begun the cross-chain fund delivery process.
Last updated

