Polygon POS 桥实现原理

名词解释

RootChain polygon的基链,部署了polygon的staking等一系列合约,指 EthereumMainnet 或 Goerli
ChildChain Polygon 主网或 Mumbai 测试网
RootToken 桥的 RootChain 侧对应的token,即在以太坊链上部署的token
ChildToken 桥的ChildChain 侧对应的token,即在Polygon链上部署的token

PoS-Bridge & Plasma Bridge

桥是用来帮助资产在 RootChain 和ChildChain 之间转移的,由一系列合约实现。
Polygon 提供了两种桥, Plasma Bridge 和 PoS Bridge.
由于Plasma的退出机制,Plasma桥更加安全, 并且从Polygon到Etherum方向的withdraw 有七天的提现周期.
相应的,PoS Bridge 只需要一个 checkpoint 周期.

PoS Bridge 实现原理

PoS Bridge 由一系列合约实现,合约链接 https://github.com/maticnetwork/contracts/blob/main/contracts .

1. 创建token映射

如果你的token需要PoS Bridge 支持以太坊-Polygon间传输,需要添加一个映射,即 RootToken 与 ChildToken 的对应关系.
Polygon 提供了操作页面 https://mapper.polygon.technology/ ,帮助你提交映射请求。提交请求之后,由Polygon Team 帮助你在合约中加入映射关系。

映射的合约代码在 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);
    }

只有Polygon团队有权限调用这个接口。

2. token从以太坊转至Polygon

当需要将token从以太坊转到Polygon时,由以太坊上部署的Depositmanager合约来处理.
https://github.com/maticnetwork/contracts/blob/main/contracts/root/depositManager/DepositManager.sol

rootchain

以ERC20为例, 当用户将token从以太坊转移至Polygon时,在以太坊的合约中进行了锁定,并通过StateSender机制,将此消息发送到了 Polygon 的 ChildERC20合约中. StateSender 机制在其他文档中有描述,是Polygon实现的一种 以太坊和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

在Polygon链上的 ChildChain 合约代码地址
https://github.com/maticnetwork/contracts/blob/main/contracts/child/ChildChain.sol

当root chain 通过stateSender 发送了deposit消息后,经过一个 checkpoint周期 ,在childchain合约中会收到该信息. 并调用相应的childToken合约的deposit接口.

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 是由Polygon 团队部署的,不必每个token开发者部署。token开发者必须在合约中实现 deposit 和 withdraw 方法.

当在childchain 中调用deposit方法时,token合约为该跨链用户 mint 出相应数量的token.

示例如下:

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);
    }

}

资产从 Polygon 转移到 以太坊的实现原理和上面的过程基本一致.

猜你喜欢

转载自blog.csdn.net/mynameislu/article/details/124802180