单公证人模式实现测试链间跨链【入门教程(上)】

注:为方便表述,本文将Ropsten链的原生代币称为RETH,Rinkeby链的原生代币称为BETH

1.1什么是跨链

跨链(cross-chain),顾名思义,就是通过连接相对独立的区块链系统,实现资产、数据等的跨链互操作。

跨链的主要实现形式包括跨链资产互换和跨链资产转移。

跨链资产互换指将一条链上的资产兑换成等值的另一条链上的资产,每条链上的资产总量不变。跨链资产互换的一个简单例子如下:

Alice用1个ETH交换Bob的5个BNB,互换成功的结果应该是Alice的ETH地址收到Bob的5个BNB,Bob的ETH地址收到Alice的1个ETH。

在这里插入图片描述

							跨链资产互换

跨链资产转移指将一条链上资产转移到另一条链上,原链上的资产锁定,另一条链上重新铸造等量等值的资产,每条链上的资产总值发生变化,但两条链的资产总值之和不变。跨链资产转移的一个简单例子如下:

Alice将以太坊上的10个ETH转移到币安链,则以太坊上的1个ETH被冻结,币安链上新生成10个WETH(等价替代品)。
在这里插入图片描述

				跨链资产转移图示

1.2为什么要跨链?

突破底层公链性能和功能瓶颈。随着区块链网络的快速发展,性能逐渐成为制约区块链发展的重大瓶颈,通过将部分事务处理转移到侧链或链下能够提升区块链网络的性能。部分功能创新也可以通过侧链实现,从而保证主链的安全性。

实现跨链互操作。单一的区块链系统相对封闭,随着区块链技术的迅速发展,链与链之间的“互操作性”问题逐渐凸显。跨链互操作的具体应用场景包括但不限于跨链支付结算、非中心化交易所、跨链信息交互等。

1.3跨链的解决方案

目前跨链协议有四个类型:中心化或者多方签名的公证人形式,侧链或者中继形式,哈希时间锁,分布式私钥控制等密码学形式。

(1)公证人机制(Notaryschemes)

在公证人机制下,跨链双方共同引入一个共同信任的第三方作为中介,由这个共同信任的中介进行跨链消息的验证和转发。(2/3门限签名–公证人)

(2)侧链/中继(Sidechains/relays)

侧链技术是相对于主链的概念,侧链需要一份实现主链网络数据获取的智能合约,其中
包含侧链数据和主链数据切换机制的方法,通过智能合约使主链和其它侧链进行交互。

(3)哈希锁定(Hash-locking)

哈希锁定可以构建多个不同的链下支付通道,让这些通道一起形成一个网络。交易双方
的数目比较小的微支付可以通过一系列的链下协议完成,从而拓展主链的性能,同时实现跨链的目的。

(4)分布式私钥控制(Distributedprivatekeycontrol)

分布式私钥控制是基于密码学中的多方计算和门限密钥的一种技术,通过采用分布式节
点来控制区块链系统中各种资产的私钥,将数字资产的使用权和所有权进行分离,使得链上资产的控制权能够安全地转移到非中心化系统中,同时将原链上的资产映射到跨链中,实现不同区块链系统间的资产流通和价值转移。

2 实验场景设计

目标计划是区块链间信息交互之资产转移系统,简称资产跨链系统。以解决区块链之间信息孤岛问题为出发点,将该笼统抽象的问题具体至资产转移这一功能上,实现跨链方案的现实应用。跨链方案有四种:公证人方案,中继链/侧链方案,哈希锁定方案,分布式私钥方案。本跨链设计方案中采用的是单公证人方案。

当前区块链中的代币分为两种:①原生代币(NativeToken);②应用代币(token);
①原生代币:原生代币是指区块链系统正常运行所需要的,体现权益的代币,比如比特币、以太币。在某些共识中持有原生代币的数量可以成为算法中权重的一部分。
②应用代币:应用代币是指在一条区块链中,非原生代币的代币,就是应用代币。如常听到的ERC20代币,ERC666代币等。准确来说ERC20,ERC666代币,他不是一种代币,而是一种代币设计标准。任何人都可以参考这些设计标准去发行任意名称与数量的代币。但是是否有人认可你发行的代币的价值那就是另一回事了。

在资产跨链的过程中,需要了解跨链的实质:跨链的资产永远无法真正的从一条链(例:以太链)转移到另一条链(例:币安链),因此只能在将A链的资产锁定在一个特定的地址中,并在B链铸造与之对应数量的等价替代代币。

例如:我想将10个ETH从以太链转移到币安链中,因此我先将我的10个ETH转移到交易所的地址,交易所再在币安链中铸造对应的10个WETH,并转移到我在币安链上的地址中。由于交易使用智能合约,而智能合约部署在区块链上,任何人都可以进行审查,也可以确定币安链上的每一个WETH都在以太坊中有锁定的ETH与之锚定,即:币安链上的每一个WETH转移到币安链的交易所时,交易所能在以太坊上将等量的ETH提取归还。
在这里插入图片描述
资产兑换,转移的需求
随着越来越多区块链的出现,每一种链都有自己的原生代币。当越来越多人认可这条链时,这条链的原生代币的价值也会越来越高。当我们想购买这种新币但是手上没有现金只有数字货币时,资产转移与兑换的需求就应运而生。无论是将数字货币兑换成USDT后再跨链去购买新币,或者是在本链上先将数字货币兑换成新币再将资产跨链,这里将资产兑换后最终都需要进行跨链,才能真正将新币握在手里,而不是新币的等价替代品。当单一区块链无法满足规模扩张的需求,且新链需要时间搭建完善生态的情况时,各式各样的跨链桥在不同公链与L2间运行,解决了资金流转的问题,为用户带来了极大的便利。
在这里插入图片描述

3、实验模块

本实验采用单公证人模式实现跨链,合约部分使用solidity语言编写,编译器为REMIX。

在资产跨链的过程中,需要了解跨链的实质跨链的资产永远无法真正的从一条链(例:以太链)转移到另一条链(例:币安链),因此只能在将A链的资产锁定在一个特定的地址中,并在B链铸造与之对应数量的等价替代代币。 因此在另一条链中需要使用ERC20合约进行铸币(产生与之对应的等量的代币)与销币(防止通货膨胀)。

跨链的过程是在两条链之间进行信息交互,我们已知区块链交易是不能主动触发的,需要被认为提交交易,区块链被动执行交易,因此两条区块链间无法主动形成互动,故需要一个第三方来执行这一步,这个第三方本文称之为监视器,是链下的代码,本文使用web3.js编写,目的是:1、监控链上合约发生的事情;2、往链上发送交易;

对于原生代币与ERC20代币有不同的转账与记录方法,因此执行过程需要将他们稍加区分一下。

在这个过程中我们将这个资产转移的合约称之为跨链桥,用于资产跨链。

综上,我们知道我们需要做的事情有: ERC20合约,原生代币合约,跨链桥合约,链下监控器

3.1ERC20合约

这是一个通用的ERC20合约,我们接下来根据需求对这个ERC20合约进行稍微的改动

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.7.0;

interface IERC20 {
    
      //ERC20标准
    function totalSupply() external view returns (uint256);  //总余额

    function balanceOf(address account) external view returns (uint256); 
    // 查询账户余额 

    function allowance(address owner, address spender) external view returns (uint256); 
    // 给定一个委托人地址和一个被委托地址,返回被委托代币余额。

    function approve(address spender, uint256 amount) external returns (bool);
    // 指定一个被委托地址和委托代币数量,被委托地址可以在不超过委托数量的前提下多次从委托账户转移代币
    // 被委托人代替委托人转帐,多用于交易所或公司

    function transfer(address recipient, uint256 amount) external returns (bool);  // 用户给别人转账

    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    // 从一个账户到另一个账户,指定发送者,接收者和转移的代币数量
    // 与approve结合使用。
    // TODO:实现待定,路由与金库之间可能需要使用,再使用一种合约作为逻辑/工具,金库负责存放金额。

    event Transfer(address indexed from, address indexed to, uint256 value); // 在成功转移(调用transfer或者transferFrom)后触发的事件,用于记录事件

    event Approval(address indexed sender, address indexed spender, uint256 value);// 成功调用approve的事件日志。
}

contract ERC20Token is IERC20 {
    
    
    string public name; // 代币名称
    string public symbol;  // 代币简称
    uint8 public decimals;  // 代币数量的小数点位数
    // 以上三项都是ERC20代币的基本标准

    uint256 private _totalSupply;
    mapping(address => uint256) public balances;
    mapping(address => mapping(address => uint256)) public allowed; 
    // ERC20 合约用一个二维映射跟踪委托代币余额,其主键是代币所有者的地址,映射到被委托地址和对应的委托代币余额
    // 第一个参数是金额所有者,第二个参数是所有者指定某人帮他代理资产,最后映射的值是代理的资产金额

    constructor(uint256 initialSupply, string memory tokenName, string memory tokenSymbol) public {
    
    
        _totalSupply = initialSupply * 10**uint256(decimals); // 初始化代币总数
        balances[msg.sender] = _totalSupply; // 创建者拥有所有代币
        name = tokenName;
        symbol = tokenSymbol;
    }

    function transfer(address recipient, uint256 amount) public override returns (bool) // override是函数重载的关键词
    {
    
    
        require(balances[msg.sender] >= amount, "token balance too low"); // 调用此函数的人有足够余额
        balances[msg.sender] -= amount;
        balances[recipient] += amount;
        emit Transfer(msg.sender, recipient, amount); // 抛出事件日志
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
    
     
    	// 理解成信托函数 找人代理理财
        uint256 allowance = allowed[sender][msg.sender];
        // 托管代币余额
        require(allowance >= amount, "allowance too low");
        require(balances[sender] >= amount, "token balance too low");
        allowed[sender][msg.sender] -= amount;
        balances[sender] -= amount;
        balances[recipient] += amount;
        emit Transfer(sender, recipient, amount);
        return true;
    }

    function approve(address spender, uint256 amount) public override returns (bool) 
    // spender就是你要委托的人,amout就是你要让他帮你代管理的资金
    {
    
    
        allowed[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }

    function allowance(address owner, address spender) public override view returns (uint256)
    {
    
    
        return allowed[owner][spender];
    }

    function balanceOf(address account) public override view returns (uint256) {
    
    
        return balances[account];
    }

    function totalSupply() public override view returns (uint256) {
    
    
        return _totalSupply;
    }
}

3.2 改·ERC20 合约

这个合约内容就是我们接下来要用的了。

注:本文将Ropsten链的原生代币称为RETH,Rinkeby链的原生代币称为BETH,第三节中所有的合约部署都是发生在Ropsten上

假设从Rinkeby链将10BETH跨链到Ropsten链上,Ropsten是没有BETH的,因此需要写一个ERC20代币来代替Rinkeby链的BETH。

由于是跨链,在rinkeby锁定了10BETH,那么在ropsten就应该释放对应的BETH,而ropsten没有BETH,因此需要在ropsten链上铸造10BETH;同样的,当从ropsten提10BETH回到rinkeby时,也需要将对应的BETH销毁。故需要增加铸/销币功能,基于我们的需求,我们对ERC20代币进行了适当的改动,如下:

pragma solidity >=0.4.16 <0.7.0;
import "hardhat/console.sol";

interface IERC20 {
    
      //ERC20标准
    function totalSupply() external view returns (uint256);  //总余额

    function balanceOf(address account) external view returns (uint256); 

    function allowance(address owner, address spender) external view returns (uint256); 

    function approve(address spender, uint256 amount) external returns (bool);

    function transfer(address recipient, uint256 amount) external returns (bool);  // 用户给别人转账

    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 indexed value); // 在成功转移(调用transfer或者transferFrom)后触发的事件,用于记录事件

    event Approval(address indexed sender, address indexed spender, uint256 value);// 成功调用approve的事件日志。
}

contract BEHT_ERC20Token is IERC20 {
    
    
    string public name = "Rinkeby Substitute Ether"; // 代币名称
    string public symbol = "BETH";  // 代币简称
    uint8 public decimals = 18;  // 代币数量的小数点位数
    // 以上三项都是ERC20代币的基本标准

    address public CrossChain_bridge;//用于铸币,只有跨链桥才可以调用这个函数进行铸币,也是我们初始化需要设置的参数

    uint256 private _totalSupply ;
    mapping(address => uint256) public balances;
    mapping(address => mapping(address => uint256)) public allowed; 

    constructor(uint256 initialSupply, address Cross_Bridge ) public {
    
    
        // 初始化代币总数
        _totalSupply = initialSupply * 10**uint256(decimals); 
        // 创建者拥有所有代币 
        // 此处初始化代币总数是为了实验其他功能,如果是原生代币的替代品此处应该为0,如果是普通的ERC20代币可以考虑初始化一定数量的代币
        balances[msg.sender] = _totalSupply*10**uint256(decimals);
       // 跨链桥合约地址
        CrossChain_bridge = Cross_Bridge;
    }

    function transfer(address recipient, uint256 amount) public override returns (bool) {
    
     
        // transfer是msg.sender自己发送代币的函数    override是函数重载的关键词
        require(balances[msg.sender] >= amount, "token balance too low"); 
        // 调用此函数的人有足够余额
        balances[msg.sender] -= amount;
        balances[recipient] += amount;
        emit Transfer(msg.sender, recipient, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount ) public override returns (bool){
    
    // 信托函数 找人代理理财
        uint256 allowance = allowed[sender][msg.sender];// 托管代币余额
        require(allowance >= amount,"allowance too low");
        require(balances[sender]>=amount,"token balance too low");
        allowed[sender][msg.sender] -= amount;
        balances[sender] -= amount;
        balances[recipient] += amount;
        emit Transfer(sender,recipient,amount);// 抛出事件日志
        return true;
    }

    function approve (address spender, uint256 amount ) public override returns (bool) {
    
     //spender就是你要委托的人,amout就是你要让他帮你代管理的资金——此处应该填写KB_CrossChain_Bridge合约地址而不是该合约的owner的地址 
        require(balances[msg.sender]>=amount,"token balances too low");
        allowed[msg.sender][spender] = amount;
        emit Approval(msg.sender,spender,amount);
        return true;
    }

    function crossED_transfer(address to, uint256 value) public only_brdige returns (bool) {
    
     //只有跨链桥合约才可以调用
        mine_ERC20(value);//铸币
        transfer(to,value);//给目标合约转币并抛出日志
        return true;
    }

    function mine_ERC20( uint256 value) internal returns (bool){
    
     //铸币合约  由于资产转移并不能实现真正的资产转移,因此只能生产一种代币作为转移
        _totalSupply += value;
        balances[msg.sender] += value;
        return true;
    }

    function burnt ( uint256 value ) public returns (bool) {
    
     // 销币合约
        require(balances[msg.sender] >=value);
        balances[msg.sender] -= value;
        _totalSupply -= value;
        return true;
    }


    function allowance (address owner, address spender) public override view returns (uint256){
    
    
        return allowed[owner][spender];
    }

    function balanceOf(address account) public override view returns (uint256) {
    
    
        return balances[account];
    }

    function totalSupply() public override view returns (uint256) {
    
    
        return _totalSupply;
    }

    modifier only_brdige() {
    
    
        require ( msg.sender == CrossChain_bridge,"Only KB_CrossChain_Bridge can call this function.");
        _;
    }
}

3.3 NATIVE 代币合约

这个代币合约主要是为了记录原生代币的信息,用作备用或者未来拓展,实际用途在本次实验中较小

pragma solidity >=0.4.16 <0.7.0;

contract KETH{
    
    
    
    // 处理原生代币的合于代码逻辑
    // 负责接受转账,提款,及抛出日志
    string public name = "Ropsten Ether";
    string public symbol = "RETH";
    uint8 public decimals = 18;
    address public owner;

    constructor(address _owner) public {
    
    
        owner = _owner;
    }

    // event Approval(address indexed src, address indexed guy, uint wad );
    event Transfer(address indexed src, address indexed dst, uint wad );
    event Deposit(address src, address dst, uint wad ); 
    event Withdrawal(address indexed dst, uint wad );

    mapping (address => uint ) public balances;
    // mapping (address => mapping(address=>uint) ) public allowance;
    
    /*function() public payable{
        deposit();
    }*/

    function deposit() public payable {
    
    // 由于是原生代币,使用deposit{value:msg.value}时就会将代币转到这个合约内了
        //此时记录该人转入合约内多少钱,为下面转账做标记。此时钱实际存在了KETH合约中,ERC20合约用来记账?
        balances[msg.sender] += msg.value;
        emit Deposit(msg.sender,address(this),msg.value);

    }

    function mark(uint256 value) public only_bridge returns (bool){
    
    
        balances[msg.sender] += value;
        return true;
    }

    function reduce(uint256 value) public returns (bool){
    
    
        balances[msg.sender] -= value;
        return true;
    }
    function balanceOf(address account) public view returns (uint256){
    
    
        return balances[account];
    }

    modifier only_bridge(){
    
    
        require (owner == msg.sender,"Only owner can call this function.");
        _;       
    }
}

3.4 跨链桥合约

跨链桥合约的主要功能是实现跨链资产转移,因此包括本链跨往其他链的执行函数general_corss_transfer(),执行结束后会抛出日志 emit LogBridge_SwapOut( ERC20_CONTRACT_ADDRESS, symbol, FromChainID, msg.sender, toChainID, to, value ); 链下监控器监控跨链合约的这个日志信息,并解析日志内容,根据需求建立新的交易,然后打包发往目标链。目标链收到交易后先确定发件人是不是指定的公证人(此处使用modifier处理,见合约最底部),是的话执行接下来的转账操作等。

除了跨链外还包括了一些基本的操作,例如,增删可支持的区块链,可支持的代币;同链内的转账,往交易所存款(TODO:添加流动性,返还利息等,可以作为下一个版本的更新内容),提款等。

此示例部署在Ropsten上

pragma solidity >=0.4.16 <0.7.0;

import "hardhat/console.sol";


interface KChain_ERC20{
    
    
    function totalSupply() external view returns (uint256);  //总余额

    function balanceOf(address account) external view returns (uint256); 

    function allowance(address owner, address spender) external view returns (uint256); 

    function approve(address spender, uint256 amount) external returns (bool);

    function transfer(address recipient, uint256 amount) external returns (bool); 

    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    function crossED_transfer(address to, uint256 value) external returns (bool);

    function burnt ( uint256 value ) external returns (bool);

    function mark(uint256 value) external returns (bool);

    function reduce(uint256 value) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value); 

    event Approval(address indexed sender, address indexed spender, uint256 value);
}

contract KB_CrossChain_Bridge{
    
    
    uint64 public  FromChainID = 3;
    //  通过链id查到链名称
    mapping(uint64 => string) public ChainID_TO_NAME;
    //  查看某链的某币我是否支持跨链
    mapping(uint64=>mapping(uint=> string)) public SYMBOL;
    // 币种对应的合约
    mapping(string=>address) public ERC20_CONTRACT;
    //uint8 SYMBOL_NUMBER;
    mapping(address=>mapping(string=>uint256)) public balance_of_deopsit;

    address owner;//合约创建者
   
    string NATIVE_COIN;
   
    uint64 NATIVE_CHAINID=3;
    
    event LogBirdge_Refund_COIN(address indexed src,address indexed dst,uint256 indexed value);
    
    event LogBridge_SwapOut(address token, string symbol, uint64 fromChainID, address from, uint64 toChainID, address to, uint256 amount);
    
    event Transfer(address indexed from, address indexed to, uint256 value);
    

//-----------------------------------合约初始化------------------------------------------------------------------------------------
    constructor(string memory _NATIVE) public payable{
    
    
        owner = msg.sender;
        NATIVE_COIN = _NATIVE;
     
        ChainID_TO_NAME[3]="Ropsten";
        ChainID_TO_NAME[4]="Rinkeby";

        SYMBOL[3][1]="RETH";// Rosten
        SYMBOL[3][2]="BETH";// Rinkeby
        SYMBOL[4][1]="RETH";// Rosten
        SYMBOL[4][2]="BETH";// Rinkeby
        //SYMBOL_NUMBER=2;
    }


//-----------------------------非原生代币在跨链前需要先获得用户approve,然后才能开始下面步骤。此函数是跨链函数-----------------------------------------------
    function general_corss_transfer( string memory symbol,uint64 toChainID, address to, uint256 value) public payable returns (bool){
    
     //msg.sender就是转钱发起地址    returns (bool)
        
        address ERC20_CONTRACT_ADDRESS;
        
        //标记是否支持此次跨目标链,币种
        bool mark;

        //检查该币是否被我的交易所支持
        mark = check_legally(symbol, toChainID);   

        // 如果使用原生代币进行的转账
        if ( compare_string( NATIVE_COIN,symbol) ) {
    
     
            if ( msg.value != value ) {
    
    
                console.log('Because transfer symbol(NATIVE_COIN) is Native Token ,so actutally transfer value is msg.value instead of value. Transfer asset(Wei):',msg.value);
                }
            NATIVE_ETH_cross_transfer ( to, toChainID);
            emit Transfer(msg.sender, address(this), msg.value);
            emit LogBridge_SwapOut( ERC20_CONTRACT[NATIVE_COIN], symbol, FromChainID, msg.sender, toChainID, to, msg.value );// 抛出日志,监控器监控此LogBridge_SwapOut日志确信已经跨链了
            return true;
        }
//                      ----------------以下是的通用跨链转账,当前仅支持其他区块链的原生代币的跨链---------------------
        if ( mark != true ) {
    
    
            // 如果本交易所不支持此次跨链,取消交易
            return false;
        }

        //获取某代币对应的合约
        ERC20_CONTRACT_ADDRESS =  ERC20_CONTRACT[symbol]; 

        //将调用对应的合约 通过接口调用起来 可参考https://benpaodewoniu.github.io/2022/01/03/solidity27/
        KChain_ERC20 KC_ERC20 = KChain_ERC20( ERC20_CONTRACT_ADDRESS ); 

        require (KC_ERC20.allowance( msg.sender, address(this)) >= value,"allowance too low");
        require (KC_ERC20.balanceOf( msg.sender) >= value ,"token balance too low");

        // 将钱转到当前合约中,并抛出Transfer日志.
        assert (KC_ERC20.transferFrom( msg.sender, address(this), value));  

        // 烧毁对应数量的代币,为了保证市场不会膨胀
        assert (KC_ERC20.burnt(value));

        // 监控器监控此日志并开始跨链。
        emit LogBridge_SwapOut( ERC20_CONTRACT_ADDRESS, symbol, FromChainID, msg.sender, toChainID, to, value );  

        // TODO: 如果网络堵塞应该将钱返还
        return true;
    }

    function NATIVE_ETH_cross_transfer ( address to, uint64 toChainID) public payable returns (bool) {
    
    // 这个函数负责直接转原生代币

        address ERC20_CONTRACT_ADDRESS;
        bool mark;
        mark = check_legally( NATIVE_COIN, toChainID); // 检查目标链是否支持KETH

        if ( mark != true ) {
    
     //如果目标链不支持KETH跨链,那么将资金转回
            msg.sender.transfer(msg.value); 
            emit LogBirdge_Refund_COIN (address(this), msg.sender, msg.value );//抛出退款日志
            return false;
        }

        ERC20_CONTRACT_ADDRESS = ERC20_CONTRACT[NATIVE_COIN];//使用KETH对应的合约进行操作
        KChain_ERC20 KC_ERC20 = KChain_ERC20( ERC20_CONTRACT_ADDRESS );

        // 人家往这个合约转eth了,记录这个合约的eth数量
        KC_ERC20.mark(msg.value);

        // TODO:如果自身池子内的KETH不够用了(流动性不足),则发放KB_KETH代币作为等价替换,需要提前告知。
        
        return true;
    }


//---------------------------以下为收到监视器发来的交易后,验证,发款的操作---------------------------------------------------------------
    function receipt(string memory symbol, address to, uint256 value ) public payable only_signer returns (bool){
    
    
        // modifier运行前检验只有指定的公证人才可以调用这个函数
        // TODO:签名验证升级版——可以使用 https://mirror.xyz/yunmo.eth/q8UqZlpH7y0VFfH0-BKHI55d8EkZ4PqGdjMAjBGzS9M 的方法实现签名校验
        general_crossED_transfer(symbol,to,value);// 此处内部调用
        return true;
    }
    
    function general_crossED_transfer( string memory symbol, address to, uint256 value ) internal returns(bool){
    
     // (内部调用函数)

        address ERC20_CONTRACT_ADDRESS;
        ERC20_CONTRACT_ADDRESS =  ERC20_CONTRACT[symbol];
        KChain_ERC20 KC_ERC20 = KChain_ERC20( ERC20_CONTRACT_ADDRESS );        

        if (compare_string (symbol, NATIVE_COIN)){
    
     // 如果是本链原生代币就使用这个方式转账,如果是ERC20代币就用else
            payable(to).transfer(value); 
            KC_ERC20.reduce(value);
            emit Transfer(address(this),to,value);
            return true;
        }
        else{
    
    
            assert(KC_ERC20.crossED_transfer(to,value));//用合约创造value个代币并且转给目的地址
            emit Transfer(ERC20_CONTRACT_ADDRESS,to,value);
            return true;
        }
        
    }

    function Bridge_ERC20_Balance( string memory symbol) public returns ( uint256 ) {
    
     // 获取本交易所某代币存量
        address ERC20_CONTRACT_ADDRESS =  ERC20_CONTRACT[symbol]; 
        KChain_ERC20 KC_ERC20 = KChain_ERC20( ERC20_CONTRACT_ADDRESS );
        return KC_ERC20.balanceOf( address(this)); // 如果是跨链ERC20的话是不会增加的,因为会被烧毁,但是原生代币不会
    }

    function general_transfer( string memory symbol, address payable to, uint256 value) public payable returns (bool){
    
     //msg.sender就是转钱发起地址    returns (bool)
        address ERC20_CONTRACT_ADDRESS;
        uint64 toChainID = NATIVE_CHAINID;
        //标记是否支持此次跨目标链,币种
        bool mark;

        //检查该币是否已被我的交易所收录
        mark = check_legally(symbol, toChainID);   

        //知道此次跨链目标链与币种合法后,使用mapping(string=>address)获得ERC20合约
        if ( mark != true ) {
    
    
            // 如果本交易所不支持此次跨链,取消交易
            return false;
        }
        // 如果使用原生代币进行的转账
        if ( compare_string( NATIVE_COIN,symbol) ) {
    
     
            if ( msg.value != value ) {
    
    
                console.log('Because transfer symbol(NATIVE_COIN) is Native Token ,so actutally transfer value is msg.value instead of value. Transfer asset(Wei):',msg.value);
                }
            to.transfer(msg.value); 
            emit Transfer(msg.sender, address(this), msg.value);
            return true;
        }
    // ----------------以下是的通用跨链转账,当前仅支持其他区块链的原生代币的跨链---------------------
        ERC20_CONTRACT_ADDRESS =  ERC20_CONTRACT[symbol]; //获取某代币对应的合约
        KChain_ERC20 KC_ERC20 = KChain_ERC20( ERC20_CONTRACT_ADDRESS ); //将调用对应的合约 通过接口调用起来 可参考https://benpaodewoniu.github.io/2022/01/03/solidity27/

        require (KC_ERC20.allowance( msg.sender, address(this)) >= value,"allowance too low");
        require (KC_ERC20.balanceOf( msg.sender) >= value ,"token balance too low");
        // 将钱转到当前合约中,并抛出Transfer日志.
        assert (KC_ERC20.transferFrom( msg.sender, to, value));  
        emit Transfer(msg.sender, address(this), msg.value);
        // TODO: 如果网络堵塞应该将钱返还
        return true;
    }

    function deposit( string memory symbol, uint256 value ) public payable returns(bool) {
    
     //还是要先调用approve函数
        if( compare_string(symbol,NATIVE_COIN)) {
    
    
            value = msg.value;
        }
        else{
    
    
            general_transfer(symbol, payable(address(this)), value);
        }
        balance_of_deopsit[msg.sender][symbol] += value;
        return true;
    }

    function withdraw( string memory symbol, uint256 value ) public payable returns(bool) {
    
    
        address ERC20_CONTRACT_ADDRESS;
        uint64 toChainID = NATIVE_CHAINID;
        bool mark;
        mark = check_legally(symbol, toChainID);   
        if ( mark != true ) {
    
    
            return false;
        }
        //校验余额
        require(balance_of_deopsit[msg.sender][symbol] >= value,"Token balance too low");
        balance_of_deopsit[msg.sender][symbol] -= value;
        //如果是原生代币
        if ( compare_string( NATIVE_COIN,symbol) ) {
    
     
            msg.sender.transfer(value); 
            emit Transfer(msg.sender, address(this), msg.value);
            return true;
        }
        ERC20_CONTRACT_ADDRESS =  ERC20_CONTRACT[symbol]; //获取某代币对应的合约
        KChain_ERC20 KC_ERC20 = KChain_ERC20( ERC20_CONTRACT_ADDRESS ); 
        // 将钱转到当前合约中,并抛出Transfer日志.
        assert (KC_ERC20.transfer( msg.sender, value));  
        emit Transfer( address(this), msg.sender,msg.value);
        // TODO: 如果网络堵塞应该将钱返还
        return true;
    
    }

    function check_legally (string memory symbol,uint64 toChainID) public returns (bool) {
    
    
        bool mark = false;
        for(uint num=1; num<=10; num++) {
    
     
            if ( compare_string ( symbol, SYMBOL[toChainID][num] )) {
    
    //检查所跨币种在目标链上是否支持
                mark = true;
                break;
            }        
        }
        return mark;
    }

    function compare_string(string memory a, string memory b ) public returns (bool){
    
     //string类型校对
        return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
    }

    function ADD_NEW_ERC20 (string memory symbol, address ERC20_CONTRACT_ADDRESS) public only_owner returns (bool){
    
     //TODO:添加新的ERC20代币合约
        ERC20_CONTRACT[symbol]=ERC20_CONTRACT_ADDRESS;
        return true;
    }

    function ADD_ChainID_TO_NAME ( uint64 ChainID, uint8 num, string memory symbol) public only_owner returns (bool){
    
     //TODO: 添加新的代币及链(需要创建者权限)
        SYMBOL[ChainID][num]=symbol;
        return true;
    }

    function DELETE_ERC20 (string memory symbol) public only_owner returns (bool){
    
     //TODO:删除ERC20代币合约
        ERC20_CONTRACT[symbol] = address(0) ; //赋值为零地址
        return true;
    }

    function DELETE_ChainID_TO_NAME ( uint64 ChainID, uint8 num) public only_owner returns (bool){
    
     //TODO: 删除新的代币及链(需要创建者权限)
        SYMBOL[ChainID][num]='0';
        return true;
    }

    modifier only_owner() {
    
    
        require (owner == msg.sender,"Only owner can call this function.");
        _;
    }

    modifier only_signer() {
    
     
    	// 使用这种方式作为单公证人的解决方案
    	// 指定receipt函数只能为指定公证人调用,公证人的账户地址↓
        require (msg.sender == 0x60Bf493654b3E23D9007e56EE7131b6dd305dfDd ,"Only the signer can call this function.");
        _;
    }

}

猜你喜欢

转载自blog.csdn.net/weixin_43380357/article/details/125505012