ERC721 Deposit and Withdraw Guide

This tutorial uses the Matic Testnet ( Mumbai ) which is mapped to the Goerli Network to demonstrate the asset transfer to and fro the two blockchains. An important thing to be noted while following this tutorial is that you should always use a Proxy address whenever it is available. For eg. The RootChainManagerProxy address has to be used for interaction instead of the RootChainManager address. The PoS contract addresses, ABI, Test Token Addresses and other deployment details of the PoS bridge contracts can be found here.

Mapping your assets is necessary to integrate the PoS bridge on your application. You can submit a mapping request here. But for the purpose of this tutorial, we have already deployed the Test tokens and Mapped then on the PoS bridge. You may need it for trying out the tutorial on your own. You can request the desired Asset from the faucet. If the test tokens are unavailable on the faucet, do reach us on discord

In the upcoming tutorial, every step will be explained in detail along with a few code snippets. However, you can always refer to this repository which will have all the example source code that can help you to integrate and understand the working of PoS bridge.

High Level Flow

Deposit ERC721 -

  1. Approve ERC721Predicate contract to spend the tokens that have to be deposited.
  2. Make depositFor call on RootChainManager.

Withdraw ERC721 -

  1. Burn tokens on matic chain.
  2. Call exit function on RootChainManager to submit proof of burn transaction. This call can be made after checkpoint is submitted for the block containing burn transaction.

Step Details


Configuring Matic SDK

Install Matic SDK (2.0.2)

npm install --save @maticnetwork/maticjs

While creating MaticPOSClient object network,version,maticProvider, parentProvider, posERC721Predicate and posRootChainManager need to be provided.

const MaticPOSClient = require("@maticnetwork/maticjs").MaticPOSClient;
const config = require("./config");
const HDWalletProvider = require("@truffle/hdwallet-provider");
const getMaticPOSClient = () => {
return new MaticPOSClient({
network: "testnet", // optional, default is testnet
version: "mumbai", // optional, default is mumbai
parentProvider: new HDWalletProvider(
config.user.privateKey,
config.root.RPC
),
maticProvider: new HDWalletProvider(
config.user.privateKey,
config.child.RPC
),
posRootChainManager: config.root.POSRootChainManager,
posERC721Predicate: config.root.posERC721Predicate, // optional, required only if working with ERC721 tokens
parentDefaultOptions: { from: config.user.address }, // optional, can also be sent as last param while sending tx
maticDefaultOptions: { from: config.user.address }, // optional, can also be sent as last param while sending tx
});
};

The configuration file should be structured like this. Make sure you add your Address, Privatekey and Goerli RPC into this file.

module.exports = {
root: {
RPC: "GOERLI-RPC",
POSRootChainManager: "0xBbD7cBFA79faee899Eaf900F13C9065bF03B1A74",
DERC721: "0x084297B12F204Adb74c689be08302FA3f12dB8A7",
posERC721Predicate: "0x74D83801586E9D3C4dc45FfCD30B54eA9C88cf9b",
posEtherPredicate: "0xe2B01f3978c03D6DdA5aE36b2f3Ac0d66C54a6D5",
},
child: {
RPC: "https://rpc-mumbai.matic.today",
DERC721: "0x757b1BD7C12B81b52650463e7753d7f5D0565C0e",
MaticWETH: "0x714550C2C1Ea08688607D86ed8EeF4f5E4F22323",
},
user: {
privateKey:
"0xPVT_KEY",
address: "0xPUB_ADDR",
amount: "5000000000000000000", // 0.005
amount2: "700000000000000000", // 0.007
tokenId: "1234",
tokenId2: "6789",
},
};

Approve

This is a normal ERC721 approval so that ERC721Predicate can call transferFrom function. Matic POS client exposes approveERC721ForDeposit method to make this call.

await maticPOSClient.approveERC721ForDeposit(rootToken, tokenId, { from });

Deposit

Deposit can be done by calling depositFor on RootChainManager contract. Note that token needs to be mapped and approved for transfer beforehand. Once tokens are transferred deposit proceeds using StateSync mechanism. Matic POS client exposes depositERC721ForUser method to make this call.

await maticPOSClient.depositERC721ForUser(rootToken, from, tokenId, {
from,
gasPrice: "10000000000",
});

deposit function of ChildToken is called by the ChildChainManager. Tokens should be minted when this call is made.

NOTE: Deposits from Ethereum to Matic happen using a state sync mechanism and takes about ~5-7 minutes. After waiting for this time interval, it is recommended to check the balance using web3.js/matic.js library or using Metamask. The explorer will show the balance only if at least one asset transfer has happened on the child chain. This link explains how to track the deposit events.

Burn

User can call withdraw function of ChildToken contract. This function should burn the tokens. Matic POS client exposes burnERC721 method to make this call.

await maticPOSClient.burnERC721(childToken, tokenId, { from });

Store the transaction hash for this call and use it while generating burn proof.

Exit

Once the checkpoint has been submitted for the block containing burn transaction, user should call the exit function of RootChainManager contract and submit the proof of burn. Upon submitting valid proof tokens are transferred to the user. Matic POS client exposes exitERC721 method to make this call. This function can be called only after the checkpoint is included in the main chain. The checkpoint inclusion can be tracked by following this guide.

await maticPOSClient.exitERC721(burnTxHash, { from });