Polygon POS bridge implementation principle

Glossary

RootChain The base chain of the polygon, deploying a series of contracts such as polygon staking, refers to EthereumMainnet or Goerli
ChildChain Polygon mainnet or Mumbai testnet
RootToken The token corresponding to the RootChain side of the bridge, that is, the token deployed on the Ethereum chain
ChildToken The token corresponding to the ChildChain side of the bridge, that is, the token deployed on the Polygon chain

PoS-Bridge & Plasma Bridge

The bridge is used to help transfer assets between RootChain and ChildChain, and is realized by a series of contracts.
Polygon provides two types of bridges, Plasma Bridge and PoS Bridge.
Due to Plasma's exit mechanism, Plasma Bridge is more secure, and there is a seven-day withdrawal period for withdrawal from Polygon to Ethereum. Correspondingly,
PoS Bridge only needs one checkpoint period.

PoS Bridge Implementation Principle

PoS Bridge is implemented by a series of contracts, the contract link https://github.com/maticnetwork/contracts/blob/main/contracts .

1. Create token mapping

If your token needs PoS Bridge to support the transmission between Ethereum and Polygon, you need to add a mapping, that is, the corresponding relationship between RootToken and ChildToken.
Polygon provides an operation page https://mapper.polygon.technology/ to help you submit mapping requests. After submitting the request, the Polygon Team will help you add the mapping relationship to the contract.

The mapped contract code is at https://github.com/maticnetwork/contracts/blob/main/contracts/common/Registry.sol

	/**
     * @dev Map root token to child token
     * @param _rootToken Token address on the root chain
     * @param _childToken Token address on the child chain
     * @param _isERC721 Is the token being mapped ERC721
     */
    function mapToken(
        address _rootToken,
        address _childToken,
        bool _isERC721
    ) external onlyGovernance {
        require(_rootToken != address(0x0) && _childToken != address(0x0), "INVALID_TOKEN_ADDRESS");
        rootToChildToken[_rootToken] = _childToken;
        childToRootToken[_childToken] = _rootToken;
        isERC721[_rootToken] = _isERC721;
        IWithdrawManager(contractMap[WITHDRAW_MANAGER]).createExitQueue(_rootToken);
        emit TokenMapped(_rootToken, _childToken);
    }

Only the Polygon team has permission to call this interface.

2. Token transfer from Ethereum to Polygon

When the token needs to be transferred from Ethereum to Polygon, it is handled by the Depositmanager contract deployed on Ethereum.
https://github.com/maticnetwork/contracts/blob/main/contracts/root/depositManager/DepositManager.sol

rootchain

Taking ERC20 as an example, when the user transfers the token from Ethereum to Polygon, it is locked in the contract of Ethereum, and this message is sent to the ChildERC20 contract of Polygon through the StateSender mechanism. The StateSender mechanism is described in other documents Description is a two-way communication mechanism between Ethereum and Polygon implemented by Polygon.

	function depositERC20(address _token, uint256 _amount) external {
        depositERC20ForUser(_token, msg.sender, _amount);
    }
    
	function depositERC20ForUser(
        address _token,
        address _user,
        uint256 _amount
    ) public {
        require(_amount <= maxErc20Deposit, "exceed maximum deposit amount");
        IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);
        _safeCreateDepositBlock(_user, _token, _amount);
    }
	
    function _safeCreateDepositBlock(
        address _user,
        address _token,
        uint256 _amountOrToken
    ) internal onlyWhenUnlocked isTokenMapped(_token) {
        _createDepositBlock(
            _user,
            _token,
            _amountOrToken,
            rootChain.updateDepositId(1) /* returns _depositId */
        );
    }
	
    function _createDepositBlock(
        address _user,
        address _token,
        uint256 _amountOrToken,
        uint256 _depositId
    ) internal {
        deposits[_depositId] = DepositBlock(keccak256(abi.encodePacked(_user, _token, _amountOrToken)), now);
        stateSender.syncState(childChain, abi.encode(_user, _token, _amountOrToken, _depositId));
        emit NewDepositBlock(_user, _token, _amountOrToken, _depositId);
    }
child chain

ChildChain contract code address on the Polygon chain
https://github.com/maticnetwork/contracts/blob/main/contracts/child/ChildChain.sol

After the root chain sends the deposit message through the stateSender, after a checkpoint cycle, the message will be received in the childchain contract. And call the deposit interface of the corresponding childToken contract.

function onStateReceive(
        uint256, /* id */
        bytes calldata data
    ) external onlyStateSyncer {
        (address user, address rootToken, uint256 amountOrTokenId, uint256 depositId) = abi
            .decode(data, (address, address, uint256, uint256));
        depositTokens(rootToken, user, amountOrTokenId, depositId);
    }
	
	function depositTokens(
        address rootToken,
        address user,
        uint256 amountOrTokenId,
        uint256 depositId
    ) internal {
        // check if deposit happens only once
        require(deposits[depositId] == false);

        // set deposit flag
        deposits[depositId] = true;

        // retrieve child tokens
        address childToken = tokens[rootToken];

        // check if child token is mapped
        require(childToken != address(0x0));

        ChildToken obj;

        if (isERC721[rootToken]) {
            obj = ChildERC721(childToken);
        } else {
            obj = ChildERC20(childToken);
        }

        // deposit tokens
        obj.deposit(user, amountOrTokenId);

        // Emit TokenDeposited event
        emit TokenDeposited(
            rootToken,
            childToken,
            user,
            amountOrTokenId,
            depositId
        );
    }

ChildChain is deployed by the Polygon team, not every token developer. Token developers must implement the deposit and withdraw methods in the contract.

When the deposit method is called in the childchain, the token contract will issue a corresponding number of tokens for the cross-chain user mint.

Examples are as follows:

pragma solidity 0.6.6;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";

contract ChildERC20 is ERC20,
{
    using SafeMath for uint256;

    constructor(string memory name, string memory symbol, uint8 decimals) public ERC20(name, symbol) {
        
        _setupDecimals(decimals);
        // can't mint here, because minting in child chain smart contract's constructor not allowed
        // _mint(msg.sender, 10 ** 27);
    
    }

    function deposit(address user, bytes calldata depositData) external {
        uint256 amount = abi.decode(depositData, (uint256));

        // `amount` token getting minted here & equal amount got locked in RootChainManager
        _totalSupply = _totalSupply.add(amount);
        _balances[user] = _balances[user].add(amount);
        
        emit Transfer(address(0), user, amount);
    }

    function withdraw(uint256 amount) external {
        _balances[msg.sender] = _balances[msg.sender].sub(amount, "ERC20: burn amount exceeds balance");
        _totalSupply = _totalSupply.sub(amount);
        
        emit Transfer(msg.sender, address(0), amount);
    }

}

The principle of transferring assets from Polygon to Ethereum is basically the same as the above process.

Guess you like

Origin blog.csdn.net/mynameislu/article/details/124802180