LogoLogo
zkBob AppLinks & Resources
  • 🦹zkBob Overview
    • zkBob
    • Basic Concepts
      • Getting Started
      • Open-Source and Decentralized
      • Multichain Deployment
      • Usage Statistics
      • Use Cases
        • Employee Salary
        • Vendor Purchasing
      • Development Timeline
      • zk Privacy Solution Comparison
    • zkBob Pools
      • USDC Pool on Polygon (sunsets January 31, 2025)
      • USDC Pool on Optimism
      • ETH Pool on Optimism
      • USDT Pool on Tron (sunsets Oct 29, 2024)
    • Fees
      • Unspent note handling
    • Deposit & Withdrawal Limits
    • Compliance & Security
      • TRM Labs Integration
    • Conferences, Workshops, Videos
      • International Videos
    • Governance
    • BOB Stablecoin
    • zkBob FAQ
  • πŸ¦Έβ€β™‚οΈzkBob Application
    • UI Overview
    • Account Creation
      • Login to an existing account
      • Lost Password
      • Metamask / Web3 Wallet Warning
    • Deposits
    • Transfers
      • Multitransfers
    • Withdrawals
      • Native Token Conversion
    • Generate a Receiving Address
    • Optional KYC
    • zkBob Direct Deposits
    • Support ID
    • Payment Links
    • Integrated Services
    • Multilingual support
      • PortuguΓͺs
      • Русский
      • δΈ­ζ–‡
  • πŸ‘©β€βš•οΈTechnical Implementation
    • zkBob Application Overview
    • Deployed Contracts
    • Smart Contracts
      • zkBob Pool Contract
        • Transaction Calldata
      • Bob Token Contract
      • Verifier contracts
      • Operator Manager Contract
        • Mutable Operator Manager
      • Voucher (XP) Token Contract
    • Accounts and Notes
      • Accounts
      • Notes
    • Relayer Node
      • Relayer Operations
      • Optimistic State
      • REST API
    • zkBob Keys
      • Address derivation
      • Ephemeral keys
    • zkSNARKs & Circuits
      • Transfer verifier circuit overview
    • zkBob Merkle Tree
      • The Poseidon Hash
    • Elliptic Curve Cryptography
    • Transaction Overview
      • Common Structure
      • Memo Block
        • Memo Block Encryption
      • Transaction Types
      • Nullifiers
      • Signing a Transaction
      • The Transaction Lifecycle
    • Client Library SDK
      • Configuration
        • Initializing the client
          • Client Configuration
        • Attaching a User Account
          • Account Configuration
        • Switching Between Pools
      • Account-less Mode Operations
        • Converting Token Amounts
        • Transaction Fees
        • Transaction Constraints
        • Using the Delegated Prover
        • Getting the State
        • Gift Cards
        • Client Library Status
        • Helpers
        • Versioning
      • Full Mode Operations
        • Balances and History
        • Shielded Addresses
        • Account State
        • Fee Estimations
        • Transaction Configuration
        • Sending Transactions
        • Transaction Maintenance
        • Direct Deposits
        • Gift Cards Maintenance
        • Ephemeral Deposits
        • Forced Exit
        • Other Routines
      • Common Types
      • Full Functions List
      • Utilities
  • πŸ‘©β€πŸ«Deployment
    • Trusted Setup Ceremony
    • Contract Deployment
    • Relayer Subsystem
  • πŸ‘·β€β™‚οΈRoadmap
    • On the Roadmap
    • Exploratory Features
      • XP (Experience Points)
        • XP-based Auctions
      • Multi-chain Custom Rollup Deployment
      • Round-robin Operator Manager
      • Compounding
  • πŸ§‘β€πŸ’»Jobs
    • Zero-Knowledge Researcher & Protocol Developer
  • 🧩Resources
    • Visual Assets
    • Hackathons
      • zkBob Cloud
    • Release Notes
      • October 11, 2023
      • July 13, 2023
      • June 13, 2023
      • March 28, 2023
      • January 30, 2023
      • January 16, 2023
      • January 2, 2023
      • Releases 2022
    • Security Audit
    • Github
    • Link tree
Powered by GitBook
On this page
  • Checking If Account Destroyed
  • Returns
  • Example
  • Checking If Emergency Exit is Supported by Current Pool
  • Returns
  • Example
  • Getting Forced Exit State for Account
  • Returns
  • Example
  • Getting Committed Forced Exit Details
  • Returns
  • Example
  • Getting Completed Forced Exit Details
  • Returns
  • Example
  • Checking Funds Available for Forced Exit
  • Returns
  • Example
  • Making Commit Forced Exit
  • Parameters
  • Returns
  • Example
  • Executing Forced Exit
  • Parameters
  • Returns
  • Example
  • Cancelling Forced Exit
  • Parameters
  • Returns
  • Example

Was this helpful?

  1. Technical Implementation
  2. Client Library SDK
  3. Full Mode Operations

Forced Exit

Emergency funds withdrawal in case the relayer is unavailable

PreviousEphemeral DepositsNextOther Routines

Last updated 1 year ago

Was this helpful?

The zkBob solution involves interacting with the pool through a relayer. You cannot send deposit, transfer, or withdrawal transactions directly to the contract. This is done to facilitate interaction with the pool without the need to pay fees in the native coin. If the relayer subsystem is unavailable for the any reason, there is an option for emergency withdrawal from the pool. In this case, you must interact directly with the pool contract.

In the event of an emergency exit, all available funds will be withdrawn to the specified address, but the zk-account will be destroyed, and you will no longer be able to use it for transactions.

An emergency exit is performed in two stages. In the first stage, you request an exit window from the pool contract, specifying all the necessary parameters in advance, such as the withdrawal address, the amount of funds, and the computed proof for the withdrawal transaction. This operation is known as commit. The exit window opens 1 hour after the commitment and is available for 24 hours after the commit.

The second stage can be completed only during the window's validity period. To perform it you must send an emergency exit execution transaction.

If you miss the exit window, you will need to cancel the previously committed emergency exit in order to request a new exit window.

If the relayer's functionality is restored before executing an emergency exit, and you send any regular transaction to the pool, the existing emergency exit will be invalidated.

If the relayer is in the operational state it will block your transactions from your account during the active exit window.

After a successful emergency exit, your account is marked as destroyed on the pool contract and all incoming transfers to that account will burned.

It's important to note that in the case of an emergency exit execution, only the exact amount specified in the commit transaction will be withdrawn. If you receive an incoming transfer after the commit but before the withdrawal is executed, it will not be withdrawn. In this case, you must wait until the exit window expires, cancel the exit, and then create a new one (which takes into account the received funds).

The following routines guide you through the emergency exit procedure and help to retrieve related info.

Checking If Account Destroyed

The account can be destroyed only during execution of the forced exit.

async isAccountDead(): Promise<boolean>

This is a single function related to the emergency exit flow which works even if the forced exit feature is unsupported by the privacy pool. For other routines you should first.

Returns

Promise returns boolean indicating if the forced exit procedure was performed for the account.

Example

if (await zkClient.isAccountDead()) {
    console.log('Sorry, you cannot transact anymore: your account was destroyed');
} else {
    console.log('Forced exit was not performed yet');
}
// output: Forced exit was not performed yet

Checking If Emergency Exit is Supported by Current Pool

The forced exit functionality can be unsupported in the certain pools. Use the following routine to check if this feature is supported.

async isForcedExitSupported(): Promise<boolean>

Returns

Promise returns boolean indicating if the forced exit is supported by the current pool contract.

Example

if (await zkClient.isForcedExitSupported()) {
    console.log('Okay, let\'s try to withdraw your funds emergency');
    // ...forced-exit related flow...
} else {
    console.log('Sorry, emergency exit is unsupported by the pool');
}
// output: Okay, let's try to withdraw your funds emergency

Getting Forced Exit State for Account

To check forced exit state for your current account you can use the following routine:

async forcedExitState(): Promise<ForcedExitState>

Returns

Example

switch (await zkClient.forcedExitState()) {
    case ForcedExitState.NotStarted:
        console.log('Forced exit was not started');
        break;
    case ForcedExitState.CommittedWaitingSlot:
        console.log('Forced exit commited but not available yet');
        break;
    case ForcedExitState.CommittedReady:
        console.log('Forced exit commited and ready to execute');
        break;
    case ForcedExitState.Completed:
        console.log('Forced exit completed');
        break;
    case ForcedExitState.Outdated:
        console.log('Forced exit outdated');
        break;
    default:
        console.error('Unknown forced exit state');
        break;
}
// output: Forced exit was not started

Getting Committed Forced Exit Details

When you commit a forced exit the set of parameters should be specified. The following method retrieves the committed forced exit parameters (if the exist).

async activeForcedExit(): Promise<CommittedForcedExit | undefined>

This method returns a forced exit object only in the following states: CommittedWaitingSlot, CommittedReady, Outdated

The result is undefined in case the forced exit was executed, cancelled, or not started

Returns

Example

const committed = await zkClient.activeForcedExit()
if (committed) {
    console.log('Found committed forced exit');
    console.log(`Nullifier:  ${committed.nullifier}`);
    console.log(`Operator:   ${committed.operator}`);
    console.log(`Receiver:   ${committed.to}`);
    console.log(`Amount:     ${committed.amount}`);
    console.log(`Start time: ${new Date(committed.exitStart * 1000).toLocaleString()}`);
    console.log(`End time:   ${new Date(committed.exitEnd * 1000).toLocaleString()}`);
    console.log(`Tx hash:    ${committed.txHash`);
}
// Found committed forced exit
// Nullifier:  7151204415055949250553114361348271896976012357469097285489034137873972583466
// Operator:   0x6D16337B9a1651556749230eeAA4Dc602A22DcAf
// Receiver:   0x6D16337B9a1651556749230eeAA4Dc602A22DcAf
// Amount:     677000000000
// Start time: 24.10.2023, 21:49:12
// End time:   25.10.2023, 20:49:12
// Tx hash:    0x2cb5eb49f7b89810675fce988f259b7f47a63710029a303ab0906b59a5bba7b2

Getting Completed Forced Exit Details

The following structure describes the second stage result. It's available only in the ForcedExitState.Completed state.

async executedForcedExit(): Promise<FinalizedForcedExit | undefined>

Returns

Example

const executed = await zkClient.executedForcedExit()
if (executed) {
    console.log('Found completed forced exit');
    console.log(`Nullifier:  ${executed.nullifier}`);
    console.log(`Receiver:   ${executed.to}`);
    console.log(`Amount:     ${executed.amount}`);
    console.log(`Tx hash:    ${executed.txHash`);
}
// Found completed forced exit
// Nullifier:  13575656066236883124312534101991453259859429227583973074085956794292119880492
// Receiver:   0x6e9aE59020788AfCaAb243FCD0e9317189a81435
// Amount:     7000000000
// Tx hash:    0x6df714f0e6100f1755611e0f3e58e23111b4642a3f437fca0062b685ab84db9e

Checking Funds Available for Forced Exit

You should always check available funds and compare with the account balance before committing to the forced exit.

The forced exit flow doesn't require the relayer so you shouldn't pay the relayer fee. But you should have a few native coins to pay the pool contract transaction fee.

async availableFundsToForcedExit(): Promise<bigint>

Returns

Promise returns amount of tokens in pool dimension which can be withdrawn during the forced exit.

Example

const feFunds = await zkClient.availableFundsToForcedExit();
const balance = await zkClient.getTotalBalance();
if (feFunds < balance) {
    console.log(`You can withdraw just ${feFunds} of ${balance}`);
} else {
    console.log(`You can withdraw all of your balance (${feFunds})`);
}
// output: You can withdraw all of your balance (677000000000)

Making Commit Forced Exit

This is the first stage of the emergency exit flow. The following method will prepare all values needed to send the commit transaction and invoke a callback to send it.

async requestForcedExit(
    executerAddress: string,
    toAddress: string,
    sendTxCallback: (tx: PreparedTransaction) => Promise<string>
  ): Promise<CommittedForcedExit>

Parameters

executerAddress - who will be able to send execute transaction (set to zero address to disable constraints).

toAddress - who will receive funds during forced exit execution.

Returns

Example

const committed = await zkClient.requestForcedExit(
    '0x6D16337B9a1651556749230eeAA4Dc602A22DcAf',
    '0x6D16337B9a1651556749230eeAA4Dc602A22DcAf',
    async (tx: PreparedTransaction) => 
        this.sendAndSendTx(tx.to, tx.amount, tx.data);
);
    
if (committed) {
    console.log(`Forced exit has been committed ${committed.txHash}`);
    console.log(`Wait until ${new Date(committed.exitStart * 1000).toLocaleString()}`);
}
// Forced exit has been committed 0x2cb5eb49f7b89810675fce988f259b7f47a63710029a303ab0906b59a5bba7b2
// Wait until 24.10.2023, 21:49:12

Executing Forced Exit

Use the following routine to complete the committed forced exit. It will automatically collect all needed data and invoke the callback to send a transaction.

async executeForcedExit(
    sendTxCallback: (tx: PreparedTransaction) => Promise<string>
): Promise<FinalizedForcedExit>

Parameters

Returns

Example

const completed = await zkClient.executeForcedExit(
    async (tx: PreparedTransaction) => 
        this.sendAndSendTx(tx.to, tx.amount, tx.data);
);
    
if (completed) {
    console.log(`Forced exit has been completed: ${completed.txHash}`);
    console.log(`Check address ${completed.to} for withdrawn funds`);
}
// Forced exit has been completed: 0x6df714f0e6100f1755611e0f3e58e23111b4642a3f437fca0062b685ab84db9e
// Check address 0x6e9aE59020788AfCaAb243FCD0e9317189a81435 for withdrawn funds

Cancelling Forced Exit

If you miss the exit window, you need to first cancel the exit (with the current nullifier) before creating a new one. Use the following method:

async cancelForcedExit(
    sendTxCallback: (tx: PreparedTransaction) => Promise<string>
): Promise<FinalizedForcedExit>

Parameters

Returns

Example

const cancelled = await zkClient.cancelForcedExit(
    async (tx: PreparedTransaction) => this.sendAndSendTx(tx.to, tx.amount, tx.data);
);
    
if (cancelled) {
    console.log(`Forced exit has been cancelled: ${cancelled.txHash}`);
}
// Forced exit has been cancelled: 0x81039f753be2078a467370947807fdbdca571630f21920b6d6337b305fb488d4

Promise returns enum member.

Promise returns object or undefined if unavailable.

Although object contains the cancelled property which can indicate the forced exit was cancelled, the current method doesn't return an object for the cancelled forced exit (because it's difficult to locate it due to nullifier updates).

Promise returns object or undefined if unavailable.

Keep in mind your account configuration may consist of a lot of incoming transfers (notes) which cannot be withdrawn within a single transaction. For the forced exit flow you can use just an account balance with three notes. You cannot initiate an aggregation transaction to collect unspent notes by direct pool contract interaction. Additional details about notes handling can be found

sendTxCallback - callback will be invoked by this method when commit transaction should be sent. You should sign and send the requested transaction (described by ) in your callback and return a transaction hash.

Promise returns object or undefined if unavailable

This routine should be invoked only in ForcedExitState.CommittedReady otherwise an InternalError will thrown

sendTxCallback - callback will be invoked by this method when execute transaction should be sent. You should sign and send requested transaction (described by ) in your callback and return a transaction hash

Promise returns object or undefined if unavailable

This routine should be invoked only in ForcedExitState.Outdated otherwise an InternalError will thrown

sendTxCallback - callback will be invoked by this method when cancel transaction should be sent. You should sign and send requested transaction (described by ) in your callback and return a transaction hash.

Promise returns object or undefined if unavailable.

πŸ‘©β€βš•οΈ
here
check forced exit support
state
state
ForcedExitState
CommittedForcedExit
FinalizedForcedExit
FinalizedForcedExit
PreparedTransaction
CommittedForcedExit
PreparedTransaction
FinalizedForcedExit
PreparedTransaction
FinalizedForcedExit