# Detailed Withdrawal Flow with State Transitions

## Detailed Withdrawal Flow with State Transitions

{% @mermaid/diagram content="sequenceDiagram
participant User
participant Bridge as CanonicalBridge
participant Treasury
participant Auth as Withdraw Authority

```
User->>Bridge: Request Withdrawal
Bridge->>Bridge: Verify Message
Bridge->>Auth: Check Authorization
Auth->>Bridge: Approve Withdrawal
Bridge->>Bridge: Start Fraud Window
Note over Bridge: Wait for fraudWindowDuration
Bridge->>Treasury: Request ETH
Treasury->>User: Transfer ETH" %}
```

**State Transition Analysis:**

1. **UNKNOWN → PROCESSING**
   * Triggered by: `authorizeWithdraw()` call from WITHDRAW\_AUTHORITY\_ROLE
   * Requirements: Valid WithdrawMessage, contract not paused
   * State data stored: Message hash, start time, message details
   * Events emitted: WithdrawAuthorized
   * This transition initiates the withdrawal process and starts the fraud window
2. **PROCESSING → PENDING**
   * Triggered by: Time passage (fraud window duration)
   * No explicit function call required
   * The status is calculated dynamically based on current time vs. start time + fraud window
   * This transition occurs automatically after the fraud window expires
   * It indicates that the withdrawal is ready to be claimed
3. **PENDING → CLOSED**
   * Triggered by: `claimWithdraw()` call from user
   * Requirements: Fraud window expired, valid message
   * State changes: Message status set to CLOSED
   * Actions: ETH transferred from Treasury to user
   * Events emitted: WithdrawClaimed
   * This transition completes the withdrawal process
4. **PROCESSING → CLOSED (Cancellation)**
   * Triggered by: `deleteWithdrawMessage()` call from WITHDRAW\_CANCELLER\_ROLE
   * Requirements: Message in PROCESSING state
   * State changes: Message deleted
   * Events emitted: WithdrawCancelled
   * This transition cancels a suspicious withdrawal during the fraud window

### State Management

The CanonicalBridge contract manages withdrawal states using an enum and a mapping:

```solidity
enum WithdrawStatus { UNKNOWN, PROCESSING, PENDING, CLOSED }

mapping(bytes32 => WithdrawMessageInfo) private withdrawMessages;

struct WithdrawMessageInfo {
    uint256 startTime;
    WithdrawMessage message;
}
```

**State Calculation Logic:**

```solidity
function getWithdrawStatus(bytes32 messageHash) public view returns (WithdrawStatus) {
    WithdrawMessageInfo storage info = withdrawMessages[messageHash];
    
    if (info.startTime == 0) {
        return WithdrawStatus.UNKNOWN;
    }
    
    if (withdrawalClosed[messageHash]) {
        return WithdrawStatus.CLOSED;
    }
    
    if (block.timestamp >= info.startTime + fraudWindowDuration) {
        return WithdrawStatus.PENDING;
    }
    
    return WithdrawStatus.PROCESSING;
}
```

This function calculates the current status of a withdrawal based on its state data. The status is determined by:

* If the withdrawal doesn't exist (startTime is 0), it's UNKNOWN
* If the withdrawal has been explicitly closed, it's CLOSED
* If the fraud window has expired, it's PENDING
* Otherwise, it's still in PROCESSING

This dynamic calculation approach allows for efficient state management without requiring explicit state transitions for time-based changes.

### Key Operations

#### Deposit Operation - Technical Implementation

The deposit function in the CanonicalBridge contract:

```solidity
function deposit(bytes32 recipient, uint256 amountWei)
    external
    payable
    virtual
    override
    whenNotPaused
    bytes32Initialized(recipient)
    validDepositAmount(amountWei)
    nonReentrant
{
    bool success;
    (success,) = payable(address(TREASURY)).call{value: amountWei}(abi.encodeWithSignature("depositEth()"));
    if (!success) revert CanonicalBridgeTransactionRejected(0, "failed to transfer funds to the treasury");

    // Emit deposit message
    uint256 amountLamports = amountWei / WEI_PER_LAMPORT;
    emit Deposited(msg.sender, recipient, amountWei, amountLamports);
}
```

This function handles the deposit process, validating the parameters, transferring ETH to the Treasury, and emitting an event for L2 processing. The modifiers ensure that the function is non-reentrant, can only be called when the contract is not paused, and validates the deposit parameters.

The deposit validation modifier:

```solidity
modifier validDepositAmount(uint256 amountWei) {
    if (msg.value != amountWei) revert CanonicalBridgeTransactionRejected(0, "Deposit amount mismatch");
    if (msg.value % WEI_PER_LAMPORT != 0) revert CanonicalBridgeTransactionRejected(0, "Fractional value not allowed");
    if (msg.value < MIN_DEPOSIT) revert CanonicalBridgeTransactionRejected(0, "Deposit less than minimum");
    _;
}
```

This modifier validates the deposit parameters, ensuring that the deposit amount meets the minimum requirement, and the sent ETH matches the specified amount. These validations prevent common errors and potential attacks.

#### Withdrawal Operation - Technical Implementation

The withdrawal authorization function:

```solidity
function _authorizeWithdraw(
    WithdrawMessage memory message
)
    private
    validWithdrawMessage(message)
{
    bytes32 messageHash = withdrawMessageHash(message);
    uint256 messageStartTime = block.timestamp + fraudWindowDuration;

    /// @dev CanonicalBridgeV1 replay protection.
    if ((ICanonicalBridge(CANONICAL_BRIDGE_V1).withdrawMsgIdProcessed(message.withdrawId) != 0)) {
        revert CanonicalBridgeTransactionRejected(message.withdrawId, "Message replay protection");
    }

    /// @dev This would occur if the relayer passed the same message twice.
    if (withdrawMessageStatus(messageHash) != WithdrawStatus.UNKNOWN) {
        revert CanonicalBridgeTransactionRejected(message.withdrawId, "Message already exists");
    }
    /// @dev This would only occur if the same message Id was used for two different messages.
    if (withdrawMsgIdProcessed[message.withdrawId] != 0) {
        revert CanonicalBridgeTransactionRejected(message.withdrawId, "Message Id already exists");
    }
    startTime[messageHash] = messageStartTime;
    withdrawMsgIdProcessed[message.withdrawId] = block.number;

    emit WithdrawAuthorized(
        msg.sender,
        message,
        messageHash,
        messageStartTime
    );
}
```

This function authorizes a withdrawal, validating the message, storing the withdrawal information, and emitting an event. The modifiers ensure that only entities with the WITHDRAW\_AUTHORITY\_ROLE can call this function, it can only be called when the contract is not paused, and the withdrawal message is valid.

The claim withdrawal function:

```solidity
function claimWithdraw(
    WithdrawMessage calldata message
)
    external
    override
    whenNotPaused
    nonReentrant
    validWithdrawMessage(message)
{
    bool authorizedWithdrawer = (msg.sender == message.destination || hasRole(CLAIM_AUTHORITY_ROLE, msg.sender));
    if (!authorizedWithdrawer) {
        revert IAccessControl.AccessControlUnauthorizedAccount(msg.sender, CLAIM_AUTHORITY_ROLE);
    }

    bytes32 messageHash = withdrawMessageHash(message);
    if (withdrawMessageStatus(messageHash) != WithdrawStatus.PENDING) revert WithdrawUnauthorized();

    startTime[messageHash] = NEVER;
    emit WithdrawClaimed(message.destination, message.from, messageHash, message);

    /// @dev Transfer amountWei - feeWei to recipient.
    bool success = ITreasury(TREASURY).withdrawEth(
        message.destination,
        message.amountWei - message.feeWei
    );

    /// @dev The following condition should never occur and the error should be unreachable code.
    if (!success) revert WithdrawFailed();

    /// @dev Transfer feeWei to fee receiver.
    success = ITreasury(TREASURY).withdrawEth(
        message.feeReceiver,
        message.feeWei
    );

    /// @dev The following condition should never occur and the error should be unreachable code.
    if (!success) revert WithdrawFailed();
}
```

This function allows users to claim their withdrawal after the fraud window has expired. It verifies that the withdrawal is in the PENDING state, marks it as CLOSED, handles fee distribution if applicable, and transfers the ETH to the destination address. The modifiers ensure that the function is non-reentrant and can only be called when the contract is not paused.

#### Administrative Operations - Technical Examples


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.eclipse.xyz/architecture/eclipse-architecture/eclipse-canonical-bridge/detailed-withdrawal-flow-with-state-transitions.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
