JavaScript 不可用。

我们检测到浏览器禁用了 JavaScript。请启用 JavaScript 或改用支持的浏览器来继续访问

Token Mapping 项目,这是一个将 A 代币映射为 B 代币的合约

作者:Anban Chu

发表日期:2020年12月25日

所属目录:合约开发

需求场景说明

两种代币之间的互换,禁止原生代币映射为其他币,但是允许 Token 币映射为原生币,关系图如下。

From 币 To 币 是否支持
ERC20 ERC20
ERC20 原生代币
原生代币 原生代币
原生代币 ERC20

做这个合约的目的:将没有价值的代币,转为有价值的代币。 比如市场做营销的时候,发放的代币积分,或者某个活动合约,得到的活动 Token。可以将这些转为有价值的币。接到的需求是 1:1 映射,但是为了更好的兼容,我在合约内部增加了映射比例的设置口子。这样使用起来更加灵活。

除了上面说的以外,还可以设置稳定币的映射关系。比如设置 USDT --> USDCUSDC --> USDT ,每次映射设置 X % 的手续费,这样可以极大的提高资金利用率,如果是主流生态,需要监控 ChainLink 预言机来进行汇率调整,如果是不常用的链,比如 USDT 和 USDC 都是项目方自己做的币,那么直接设置汇率,比如设置收取 0.5% 的手续费,也是完全没问题的。

合约介绍

从下面 5 个方面介绍

  1. 只读方法
  2. 构造函数 (部署时候需要的配置)
  3. 核心方法
  4. 辅助方法
  5. 管理方法
  6. 合约事件

这是一个基于 @openzeppelin/contracts 做的合约,使用了 Ownable/ IERC20/ SafeERC20

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import '@openzeppelin/contracts/access/Ownable.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';

/// @title Contract for exchanging ERC20 tokens
/// @author Anbang
/// @notice One-way conversion, irreversible
contract TokenMapping is Ownable {
    using SafeERC20 for IERC20;
    // ....
}

1 只读方法

  • mappingStatus: 当前合约的映射状态,只有开启状态的情况下,用户才可以进行映射。
    • true: 开启状态
    • false: 关闭状态;
  • getPairs: 获取所有映射对
    • 返回数组,数组内结果如下
    fromToken: 0xAAA,
    toToken: 0xBBB,
    
  • mappingRates(_from_token,_to_token): 通过 fromToken + toToken 获取汇率
    • 如果映射汇率为 1e18, 则用户收到的 targetAmount 为 user.amount * 1e18 / BASE_MUL
    • 上面的 BASE_MUL 是内部常量,值为 1e18
  • pairLength: 合约内的映射对数量。如返回1, 代表有一个映射对
  • pairs(pair_index): 获取指定索引的映射对

TIPS:大家可以根据实际情况修改的地方

  • 如果需要记录用户映射金额,可以引入 UserInfo 结构体的变量。
  • 如果需要记录全局总映射金额,可以引入 PoolInfo 结构体的变量。
  • 如果打算做基金地址(基金地址用来收取手续费),可以增加 fundAddress 变量

2 构造函数

没有 constructor 函数,这个合约比较简单,没有基金地址。如果打算做基金地址,可以在这里进行初始化。

3 核心方法

  • mappingToken(_from_token,_to_token,_amount): 映射代币
    • from token 兑换为 to tokenfrom token 的数量为 amount
    • 映射说明
      • ✅ 允许 TokenA 映射为 主网币
      • ✅ 允许 TokenA 映射为 TokenB
      • ❌ 禁止 主网币 映射为 TokenA,主网币不能作为 _from_token
    • _to_token 为主网币时候,需要传入 0x0000000000000000000000000000000000000000

合约报错说明

  • 当收到 Mapping closed 错误的时候,此时 mappingStatus 处在 false 状态: 所有代币不允许进行映射。
  • 当收到 Pair doesn't exist 错误的时候,此时 mappingRates[_from_token][_to_token] 为 0,需要添加映射对。
  • 当收到 Invalid amount 错误的时候,此时传入的 _amount 为 0,请输入需要映射的数量。
  • 当收到 ERC20: transfer amount exceeds allowance错误的时候,此时_from_token 为 Token 地址,没有对 TokenMapping 合约做授权。

4 辅助方法

  • 🔒 withdraw(_token_address, _to_address, uint256 _amount): 取出指定 token_addressto address

    • 如果收到错误信息 Invalid to_address : 代表 to_address 地址为 0
    • 如果 _token_address0x0000000000000000000000000000000000000000 表示为提取主网币
  • 🔒 burn(_token_address, uint256 _amount)

    • 销毁指定 token_addressaddress(1)
    • 该方法不是真正的销毁
    • 不支持销毁主网币

owner 权限相关的函数,这是来自第三方代码库 '@openzeppelin/contracts/access/Ownable.sol' 中的

  • transferOwnership : 设置新的 owner 地址,后期转移给 DAO 地址。
  • renounceOwnership : 放弃所有权 (该方法谨慎考虑后才能操作)
    • 放弃所有权将使合约没有 owner,将无法再调用 onlyOwner 函数。

5 管理方法

  • 🔒 addMappingPair(_from_token,_to_token,_mapping_rate): 添加映射对
    • _to_token 为主网币时候,需要传入 0x0000000000000000000000000000000000000000
    • _mapping_rate 是映射汇率,基础乘数是 1e18(“1000000000000000000”)
      • ✅✅✅ 重要: _mapping_rate 计算公式为 18 - FromDecimals + ToDecimals
      • 代码内的转换逻辑为: taretAmount = user.amount * _mapping_rate / 1e18
      • From 和 To 币种的精度都一样,且都是 1e18,假设映射前的 user.amount = 1e18
        • 如果是 1:1 映射,则传入 _mapping_rate 应该为 1e18
          • taretAmount = 1e18 * 1e18 / 1e18 => 1e18
        • 如果是 1:0.5 映射,则传入 _mapping_rate 应该为 1e18/2
          • taretAmount = 1e18 * 1e18/2 / 1e18 = 5e17
        • 如果是 1:2 映射,则传入 _mapping_rate 应该为 1e18*2
          • taretAmount = 1e18 * 1e18*2/ 1e18 = 2e18
      • From 和 To 币种的精度不一样,
        • 如果是 1:1 映射
          • FromDecimals 为 18
            • ToDecimals 为 8 : 则传入 _mapping_rate1e8(18-18+8)
            • ToDecimals 为 6 : 则传入 _mapping_rate1e6(18-18+6)
            • ToDecimals 为 2 : 则传入 _mapping_rate1e2(18-18+2)
          • ToDecimals 为 18
            • FromDecimals 为 8 : 则传入 _mapping_rate1e28(18-8+18)
            • FromDecimals 为 6 : 则传入 _mapping_rate1e30(18-6+18)
            • FromDecimals 为 2 : 则传入 _mapping_rate1e34(18-2+18)
          • FromDecimals 为 8
            • ToDecimals 为 18 : 则传入 _mapping_rate1e28(18-8+18)
            • ToDecimals 为 8 : 则传入 _mapping_rate1e18(18-8+8)
            • ToDecimals 为 6 : 则传入 _mapping_rate1e16(18-8+6)
            • ToDecimals 为 2 : 则传入 _mapping_rate1e12(18-8+2)
        • 如果是 1:0.5 映射,则传入 _mapping_rate(18 - FromDecimals + ToDecimals)/2
        • 如果是 1:2 映射,则传入 _mapping_rate(18 - FromDecimals + ToDecimals)*2
    • 如果收到错误信息 Pair already exists: 表示该映射对已经存在了,如果想要修改映射汇率,请使用 updatePairRate 方法。
    • 如果收到错误信息 Invalid from address: 代表 _from_token 为 0,不支持从主网币映射为 token 币;
    • 如果收到错误信息 Same address: 代表 _from_token_to_token 相同,需要核对地址参数。
  • 🔒 updatePairRate(_from_token,_to_token,_mapping_rate): 更新映射对的映射比例
    • 如果收到错误信息 Pair doesn't exist;表示该映射对不存在,请使用 addMappingPair 方法添加。
    • 注意: 如果将 _mapping_rate 设置为 0,代表关闭该映射对,以后如果再次开启此映射,需要重新添加该添加对。
  • 🔒 setMappingStatus (bool _status): 设置兑换状态
    • true: 开启状态
    • false: 关闭状态;

6 合约事件

event PairAdd(address indexed from_token, address indexed to_token, uint256 mapping_rate);
event PairUpdate(address indexed from_token, address indexed to_token, uint256 mapping_rate);
event MappingStatus(bool status);
event Withdraw(address indexed token_address, address indexed to_address, uint256 amount);
event Burn(address indexed token_address, uint256 amount);
event MappingToken(
    address indexed from_token, address indexed to_token, address indexed to_address,
    uint256 source_amount, uint256 target_amount
    );
event Error(address caller, bytes data);




以上就是本篇文章的全部内容了,希望对你有帮助。

>> 本站不提供评论服务,技术交流请在 Twitter 上找我 @anbang_account