xDai - DAI Bridge

Transfer xDai on xDai to DAI on Mainnet using Etherspot SDK

Before we continue, please ensure that you have had a look at our Supported Ethereum Chains, followed the steps in Install Etherspot SDK and how to Bootstrap Etherspot SDK. We're assuming that you have completed these steps before going forward.

In this example, we're going to show you how to send xDai to Dai using their TokenBridge and Etherspot.

๐Ÿ›‘ Before we continue...

We're going to be using two Etherspot SDK instances here:

  • A mainnet version

โ†—๏ธ We will use this instance to send our DAI from and ETH to pay the gas fees.

  • A xDai version

โ†˜๏ธ We will use this instance to receive our xDai on the xDai chain.

โš ๏ธ Make sure you've checked out Supported Ethereum Chains before you continue as we also show you the code to instantiate mainnet and xDai versions of the SDK. Remember to use the same private key for both SDK instances to get the same Ethereum address on both mainnet and xDai.

This example use case is more complex than the previous guide in this series: DAI - xDai Bridge, but completes the cycle of DAIโ†”๏ธxDai.

Please make sure your that your xDai Etherspot address is funded with enough xDAI to pay the gas fees required.

Sending xDai to TokenBridge

The first step in our journey to change xDai back to DAI is to send our xDai, using the xDai version of the Etherspot SDK, to the xDai TokenBridge.

Let's define our required variables:

/**
* Note: Make sure that your `mainnet` and `xDai`
* instances of the Etherspot SDK are available here.
*
* For the purposes of this demonstration, we're going
* to assume the following:
* 
* The mainnet Etherspot SDK:
* - const mainnetEtherspotSdk
*
* The xDai Etherspot SDK:
* - const xdaiEtherspotsdk
*/

import { ethers } from 'etherspot';
const xdaiBridgeAddress = "0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6";
const xdaiBridgeContract = "0x6A92e97A568f5F58590E8b1f56484e6268CdDC51";
// Visit https://blockscout.com/xdai/mainnet/address/0x6A92e97A568f5F58590E8b1f56484e6268CdDC51/contracts.
// Under the "Code" tab, scroll down to "Contract ABI" 
// and copy the code into a JSON file and import it here.
const xdaiBridgeAbi = require('xDaiBridgeAbi.json');

const daiContractAddress = "0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016";
// Visit https://etherscan.io/address/0x7e7669bdff02f2ee75b68b91fb81c2b38f9228c2#code and scroll down to Contract ABI and copy the whole code and paste it in a JSON file and import it to this variable.
const daiContractAbi = require('daiContractAbi.json');

Next, let's specify how much xDai we would like to send.

// Note: TokenBridge requires a minimum of 10 xDai
let xdaiTransferAmount = ethers.utils.parseEther("10");

๐Ÿงน Let's ensure that we have no batches of transactions waiting in the queue to be sent. We like to keep the house clean.

await xdaiEtherspotsdk
  .clearGatewayBatch()
  .catch(console.error);

Next, we're going to perform a series of steps to:

  1. Add the transaction to the batch

  2. Estimate the gas required to submit and execute this batch

  3. Send the batch to Etherspot to be processed

  4. Check to see when the transaction has been sent

/**
* Step 1: Add the transaction to a clean
* "batch" of transactions.
*
* Note: You can batch many transactions together and
* submit them as one request for a more gas-efficient
* operation. Here, we're just adding 1 transaction to
* this batch.
*/
const batchResponse = await xdaiEtherspotsdk
  .batchExecuteAccountTransaction({
    to: xdaiBridgeAddress,
    value: xdaiTransferAmount
  })
  .catch(console.error);
  
/**
* Step 2: Estimate the gas required to perform this
* operation. This is useful for presenting to users
* and allowing them to make a final decision.
*/
const estimateResponse = await xdaiEtherspotsdk
  .estimateGatewayBatch()
  .catch(console.error);
  
/**
* Step 3: Next, send this batch to Etherspot for
* processing. We'll manage the transaction, queuing,
* retries and endevour to do whatever it takes to
* get this transaction on the chosen blockchain.
*/
const submitGatewayResponse = await xdaiEtherspotsdk
  .submitGatewayBatch()
  .catch(console.error);
  
/**
* Step 4: Let's keep checking on this batch submission
* to check the status before we move on. Keep calling
* this method below, using an Interval timer for example,
* and move on to the next step when gatewayBatchStatus.state
* is "Sent".
*/
const gatewayBatchStatus = await xdaiEtherspotsdk
  .getGatewaySubmittedBatch({
    hash: submitGatewayResponse.hash,
  })
  .catch(console.error);

gatewayBatchStatus.state

meaning

"Sent"

The transactions that existed in the batch are due to be confirmed.

"Sending"

The transactions that existed in the batch are currently pending broadcast on the blockchain.

"Queued"

The transactions that existed in the batch are queued by Etherspot to be processed.

Get TokenBridge Signature

Once gatewayBatchStatus.state is equal to "Sent", indicating that the transactions in the batch are due to be confirmed, we'll lift the hash of the transaction into its own variable.

const xDaiSubmissionHash = gatewayBatchStatus.transaction.hash;

Next, we need to construct an instance of the xDai bridge contract from the ethers library. We'll communicate with the contract to fetch the required data to continue.

const xDaiTokenBridgeContract = await new ethers.Contract(
  xdaiBridgeContract,
  xdaiBridgeAbi, 
  new ethers.providers.JsonRpcProvider(
    "https://rpc.xdaichain.com/",
  ),
);

Next, we're going to perform three steps:

  1. Query the contract for a corresponding message hash for xDai amount and the xDai Etherspot address

  2. Use the message hash from the previous point to get a message payload from the contract

  3. Check if the message payload is valid, and then get the required signature to continue.

/**
* Step 1: Query the contract for the corresponding
* message hash
*/
const messageHash = await contract.getMessageHash(
  xdaiEtherspotsdk.state.accountAddress, // Your xDai Etherspot address
  xdaiTransferAmount.toString(),
  xDaiSubmissionHash, 
);

/**
* Step 2: Use the message hash to fetch the
* message payload itself
*/
let messagePayload = await contract.getMessage(messageHash);

/**
* Step 3: Let's do a basic check to ensure that the
* available tokens are still available to withdraw
*/
let signature = null;
if (messagePayload != "0x" && messagePayload != "0x0") {
  signature = await contract
    .getSignatures(
      ethers.utils.keccak256(messagePayload)
    );
} else {
  console.error('Please check the contract payload parameters.');
}

Once the transaction above gets confirmed, pass the required parameters along with the hash of the transaction made earlier to the contract methods to get the signature from token bridge.

Withdraw DAI from TokenBridge to Etherspot address

In this final section, we're going to initiate a move from the DAI contract address to our own Etherspot address on mainnet - it's final destination.

First, let's construct a contract interface to communicate with the DAI contract:

// First, construct the interface...
const daiInterface = new ethers.utils.Interface(daiContractAbi); 

// Next, encode the required data to perform the withdrawal
let encodedData = await daiInterface.encodeFunctionData(
  "executeSignatures",
  [messagePayload, signature],
);

๐Ÿงน Let's ensure that we have no batches of transactions waiting in the queue to be sent. We like to keep the house clean.

await xdaiEtherspotsdk
  .clearGatewayBatch()
  .catch(console.error);

Now, we have completed all the required prerequisites to withdraw the DAI from TokenBridge. We're going to perform a series of steps to:

  1. Add the transaction to the batch

  2. Estimate the gas required to submit and execute this batch

  3. Send the batch to Etherspot to be processed

/**
* Step 1: Add the transaction to a clean
* "batch" of transactions.
*
* Note: You can batch many transactions together and
* submit them as one request for a more gas-efficient
* operation. Here, we're just adding 1 transaction to
* this batch.
*/
const daiBatchResponse = await mainnetEtherspotSdk
  .batchExecuteAccountTransaction({
    to: daiContractAddress,
    data: encodedData,
  })
  .catch(console.error);

/**
* Step 2: Estimate the gas required to perform this
* operation. This is useful for presenting to users
* and allowing them to make a final decision.
*/
const daiBatchEstimation = await mainnetEtherspotSdk
  .estimateGatewayBatch()
  .catch(console.error);
  
/**
* Step 3: Next, send this batch to Etherspot for
* processing. We'll manage the transaction, queuing,
* retries and endevour to do whatever it takes to
* get this transaction on the chosen blockchain.
*/
const daiBatchTransaction = await mainnetEtherspotSdk
  .submitGatewayBatch()
  .catch(console.error);

๐ŸŽ‰ Finished!

Last updated