Start an In-flight Exit

Exits allow a user to withdraw funds from the OMG Network back onto the root chain. There are two types of exit: standard exits and in-flight exits (IFEs). This section will cover in-flight-exits.

A transaction is considered to be in-flight if it has been broadcasted but not yet been included in the Childchain. It may also be in-flight from the perspective of an individual user if that user does not have access to the block where said transaction is included.

A user may consider an exit in-flight in the following scenarios:

  • The user has signed and broadcast a transaction but is unable to verify its inclusion in a block.

  • The user can see that the transaction has been included in a block, but believes that the block is invalid due to a dishonest operator.

A user can initiate an IFE regardless of whether the above is correct, but one must commit an exit bond. The purpose of the exit bond is to deter users from initiating exits dishonestly, as this bond will be awarded to any party who successfully proves that the exit is dishonest.

Implementation

1. Install omg-js, web3

To access network features from your application, use our official libraries:

Requires Node >= 8.11.3 < 13.0.0

npm install @omisego/omg-js web3

2. Import dependencies

In-Flight exit involves using 3 omg-js objects. Here's an example of how to instantiate them:

import Web3 from "web3";
import BigNumber from "bn.js";
import { ChildChain, RootChain, OmgUtil } from "@omisego/omg-js";const web3 = new Web3(new Web3.providers.HttpProvider(web3_provider_url));
const rootChain = new RootChain({ web3, plasmaContractAddress });
const childChain = new ChildChain({ watcherUrl });
const ethExit = {
  currency: OmgUtil.transaction.ETH_CURRENCY,
  feeCurrency: OmgUtil.transaction.ETH_CURRENCY,
  amount: new BigNumber("12000000000000000"),
  sender: "0x8CB0DE6206f459812525F2BA043b14155C2230C0",
  senderPrivateKey: "CD55F2A7C476306B27315C7986BC50BD81DB4130D4B5CFD49E3EAF9ED1EDE4F7",
  receiver: "0xA9cc140410c2bfEB60A7260B3692dcF29665c254"
}
const erc20Exit = {
  currency: "0xd92e713d051c37ebb2561803a3b5fbabc4962431",
  feeCurrency: OmgUtil.transaction.ETH_CURRENCY,
  amount: new BigNumber("5000000"),
  sender: "0xA9cc140410c2bfEB60A7260B3692dcF29665c254",
  senderPrivateKey: "E4F82A4822A2E6A28A6E8CE44490190B15000E58C7CBF62B4729A3FDC9515FD2",
  receiver: "0x8CB0DE6206f459812525F2BA043b14155C2230C0" 
}
  • web3_provider_url - the URL to a full Ethereum RPC node (local or from infrastructure provider, e.g. Infura).

  • plasmaContractAddress - CONTRACT_ADDRESS_PLASMA_FRAMEWORK for defined environment.

  • watcherUrl - the Watcher Info URL for defined environment (personal or from OMG Network).

3. Start an in-flight exit

To start an in-flight exit, you need to create a new transaction that has been signed but hasn't been submitted to the network.

A transaction is considered to be “in-flight” if it has been broadcasted but not yet been included in the Childchain. It may also be in-flight from the perspective of an individual user if that user does not have access to the block where the defined transaction is included.

The in-flight exit process is the same for both ETH and ERC20 UTXOs. The tutorial shows how to work with ERC20 tokens. For working with ETH, change erc20Exit into ethExit across the code sample.

async function startInflightExit() {
  
  const hasToken = await rootChain.hasToken(erc20Exit.currency);
  if (!hasToken) {
    
    await rootChain.addToken({
      token: erc20Exit.currency,
      txOptions: {
        from: erc20Exit.sender,
        privateKey: erc20Exit.senderPrivateKey,
        gas: 600000
      },
    });
  }  
  const transactionBody = await childChain.createTransaction({
    owner: erc20Exit.sender,
    payments: [
      {
        owner: erc20Exit.receiver,
        currency: erc20Exit.currency,
        amount: erc20Exit.amount,
      },
    ],
    fee: {
      currency: erc20Exit.feeCurrency
    },
  });  
  
  const typedData = OmgUtil.transaction.getTypedData(
    transactionBody.transactions[0],
    plasmaContractAddress
  );  
  const privateKeys = new Array(
    transactionBody.transactions[0].inputs.length
  ).fill(erc20Exit.senderPrivateKey);  
  const signatures = childChain.signTransaction(typedData, privateKeys);  
  const signedTxn = childChain.buildSignedTransaction(
    typedData,
    signatures
  );  
  const exitData = await childChain.inFlightExitGetData(
    OmgUtil.hexPrefix(signedTxn)
  );  
  const exitReceipt = await rootChain.startInFlightExit({
    inFlightTx: exitData.in_flight_tx,
    inputTxs: exitData.input_txs,
    inputUtxosPos: exitData.input_utxos_pos,
    inputTxsInclusionProofs: exitData.input_txs_inclusion_proofs,
    inFlightTxSigs: exitData.in_flight_tx_sigs,
    txOptions: {
      from: erc20Exit.sender,
      privateKey: erc20Exit.senderPrivateKey,
      gas: gasLimit
    },
  });  
  const outputIndex = transactionBody.transactions[0].outputs.findIndex(
    (e) => e.owner === erc20Exit.sender
  );  
  await rootChain.piggybackInFlightExitOnOutput({
    inFlightTx: exitData.in_flight_tx,
    outputIndex: outputIndex,
    txOptions: {
      from: erc20Exit.sender,
      privateKey: erc20Exit.senderPrivateKey
    },
  });
  return exitReceipt;
}
  • gasLimit - gas limit for your transaction. Please check the current data on Gas Station or similar resources.

An in-flight exit creates an exit receipt. Each exit has an ID that identifies it. After an in-flight exit starts, you'll have to wait for a challenge period before you can process that exit and receive your funds back on Ethereum Network.

Lifecycle

  1. A user calls the hasToken function on the PlasmaFramework contract to check if there's an exit queue for the token in question. If no exit queue is registered, a user needs to register it using the addToken function. The corresponding PlasmaFramework contract functions used in this step are hasExitQueue and addExitQueue. This step is optional but it was included because it prevents any potential issues a user may encounter during an exit.

  2. A user calls the inFlightExitGetData function on the Watcher to get the necessary exit data to start a standard exit. A transaction is termed exitable if it is correctly formed and properly signed by the owner(s) of the transaction input(s).

  3. A user calls the startInFlightExit function on the PaymentExitGame contract and commits an exit bond to the exit.

  4. A user calls the piggybackInFlightExitOnInput or piggybackInFlightExitOnOutput function on the PaymentExitGame contract to piggyback on in-flight exit input or output call. Such a process is required for every in-flight exit before proceeding to the processing stage.

  5. After a challenge period a user can process this exit.

You can only exit one UTXO at a time. It is therefore recommended to merge your UTXOs if you would like to exit multiple ones.

Piggybacking

Once an in-flight exit is initiated, the Watcher emits a piggyback_available event.

{
  "event": "piggyback_available",
  "details": {
    "txbytes": "0xf3170101c0940000...",
    "available_outputs" : [
      {"index": 0, "address": "0xb3256026863eb6ae5b06fa396ab09069784ea8ea"},
      {"index": 1, "address": "0x488f85743ef16cfb1f8d4dd1dfc74c51dc496434"},
    ],
    "available_inputs" : [
      {"index": 0, "address": "0xb3256026863eb6ae5b06fa396ab09069784ea8ea"}
    ],
  }
}

This means that a user can piggyback onto the in-flight exit. This requires placing a piggyback bond on a UTXO from the available set in order to claim ownership and receive it on the root chain once the exit is finalized.

To successfully withdraw an output out to a transaction tx, a transaction tx must prove that transaction is both exitable and canonical.

There are specific scenarios when a user wants to exit the inputs in a non-canonical transaction. It must be established that the transaction is indeed non-canonical for this to be successful. Because a transaction is either canonical or non-canonical, you can withdraw either its inputs or outputs, not both.

The owner of an input can piggyback with the following call:

rootChain.piggybackInFlightExitOnInput({
  inputIndex,
  inFlightTx: exitData.in_flight_tx,
  txOptions: {
    from: "0x8CB0DE6206f459812525F2BA043b14155C2230C0",
    privateKey: "0xCD55F2A7C476306B27315C7986BC50BD81DB4130D4B5CFD49E3EAF9ED1EDE4F7"
  }
})

Demo Project

This section provides a demo project that contains a detailed implementation of the tutorial. If you want to integrate with the OMG Network, you can use this sample to significantly reduce the time of development. You also get step-by-step instructions and sufficient code guidance.

JavaScript

For running a full omg-js code sample for the tutorial, please use the following steps:

  1. Clone omg-js-samples repository:

git clone https://github.com/omgnetwork/omg-js-samples.git
  1. Create .env file and provide the required configuration values.

  2. Run these commands:

npm install
npm run start
  1. Open your browser at http://localhost:3000.

  2. Select Start an In-flight ETH Exit on the left side, observe the logs on the right.

Code samples for all tutorials use the same repository — omg-js-samples, thus you have to set up the project and install dependencies only one time.

Last updated