Alert Source Discuss
Standards Track: ERC

ERC-7837: 扩散型代币

一种可替代代币,在转移时铸造新代币,收取每个代币的原生费用,并强制执行有上限的供应量。

Authors Cheng Qian (@jamesavechives) <james.walstonn@gmail.com>
Created 2024-12-07

摘要

本 ERC 提出了一种新型可替代代币的标准,称为 扩散型代币 (DIFF)。与传统的 ERC-20 代币不同,转移 DIFF 代币不会减少发送者的余额。相反,它铸造新的代币直接给接收者,在每次转移操作时增加总供应量。每个转移的代币收取固定的原生货币费用,此费用由发送者支付给合约所有者。供应量的增长受到所有者设定的最大供应量的限制。代币持有者也可以销毁他们的代币以减少总供应量。这些功能实现了一种受控的、激励性的代币分配模型,该模型将可替代性与内置的经济机制相结合。

动机

传统的 ERC-20 代币保持恒定的总供应量,仅在转移时重新分配余额。虽然这种模型很普遍,但某些用例受益于一种在转移期间不断扩大供应量的代币设计,模拟了一种受控的价值“扩散”。扩散型代币模型可能适用于表示对现实世界商品的债权(例如,像 iPhone 15 的单位这样的产品批次)、数字商品或受控的资产分配,其中初始代币分配和持续可用性需要以不同的方式管理。

该模型还包括每个转移代币的原生货币费用,从而激励谨慎的、价值驱动的转移,并为代币发行者提供收入来源。最大供应量上限可防止无限制的通货膨胀,确保长期稀缺性。所有者销毁代币以兑换基础商品或服务的能力直接将链上资产映射到现实世界的赎回。

用例

  • 现实世界资产支持:制造商可以发行代表一批产品(例如,iPhone)的 DIFF 代币。每个代币都可以兑换(销毁)一件实物商品。

  • 费用驱动的激励:转移费用确保了通过不断转移进行无限铸造在经济上是不划算的。该费用还支持代币发行者或提供资金机制。

规范

术语

  • 扩散型代币:一种在转移时铸造的可替代代币单位。
  • 最大供应量:代币可以达到的最大总供应量。
  • 转移费用:必须由发送者为每个转移的代币支付的以原生区块链货币(例如,ETH)计价的费用。总费用 = transferFee * amount
  • 销毁:销毁代币的行为,减少持有者的余额和总供应量。

数据结构

  • 总供应量和最大供应量

    uint256 public totalSupply;
    uint256 public maxSupply;
    
  • 转移费用

    uint256 public transferFee; // 以 wei 为单位的每个转移代币的费用
    address public owner;
    

    owner 设置和更新 transferFeemaxSupply

代币语义

  1. 转移时铸造 当从 AB 发生转移时:
    • A 不会损失任何代币。
    • B 收到新铸造的代币(增加他们的余额和 totalSupply)。
    • totalSupply 增加转移的数量,但不得超过 maxSupply
  2. 以原生货币支付的固定转移费用 每次转移都需要发送者支付 transferFee * amount 的原生货币。如果 msg.value 不足,则交易回滚。

  3. 最大供应量 如果转移会导致 totalSupply + amount > maxSupply,则必须回滚。

  4. 销毁代币 代币持有者可以销毁代币以:
    • 将他们的余额减少销毁的数量。
    • totalSupply 减少销毁的数量。

    这可以映射到赎回基础商品或只是让代币通货紧缩。

接口

DIFF 标准与 ERC-20 部分对齐,但重新定义了某些行为:

核心功能:

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

  • function transfer(address to, uint256 amount) external payable returns (bool);

    • 修改后的行为:将 amount 个代币铸造到 to,需要 msg.value >= transferFee * amount
  • function burn(uint256 amount) external;

    • 减少发送者的余额和 totalSupply

管理功能(仅所有者):

  • function setMaxSupply(uint256 newMax) external;

  • function setTransferFee(uint256 newFee) external;

  • function withdrawFees(address payable recipient) external;

    • 提取累积的原生货币费用。

可选的审批接口(用于兼容性):

  • function approve(address spender, uint256 amount) external returns (bool);
  • function transferFrom(address from, address to, uint256 amount) external payable returns (bool);

    • 修改后的行为:与 transfer 类似,但使用 allowance,并且仍然将代币铸造到 to,而不是从 from 重新分配。

事件

  • event Transfer(address indexed from, address indexed to, uint256 amount);

    当通过转移调用将代币铸造到 to 时发出。

  • event Burn(address indexed burner, uint256 amount);

    当从地址销毁 amount 个代币时发出。

  • event FeeUpdated(uint256 newFee);

    当所有者更新 transferFee 时发出。

  • event MaxSupplyUpdated(uint256 newMaxSupply);

    当所有者更新 maxSupply 时发出。

符合 ERC-20

DIFF 标准实现了 ERC-20 接口,但显着改变了 transfertransferFrom 的语义:

  • 可替代性:每个代币单位都是相同的,并且可以像 ERC-20 中一样分割。
  • 余额和转移balanceOf 函数正常工作。但是,transfertransferFrom 不再重新分配代币。相反,它们铸造新的代币(最多 maxSupply)。
  • 审批approvetransferFrom 函数仍然存在,但它们的逻辑非常规,因为发送者的余额永远不会因转移而减少。

虽然 DIFF 标准可以在接口级别上被视为与 ERC-20 兼容,但底层经济学存在很大差异。

理由

设计决策

  • 无限铸造与最大供应量:允许在每次转移时铸造代币可以提供代币的“扩散”传播。maxSupply 可防止不受控制的通货膨胀。

  • 销毁机制:允许在代币退出流通时进行赎回或通货紧缩。

  • 所有者控制:所有者(例如,发行者)可以调整费用和最大供应量,从而在市场条件变化时保持灵活性。

向后兼容性

DIFF 标准在接口上与 ERC-20 兼容,但在行为上并不相同。任何集成 DIFF 代币的系统都应了解转移时铸造的差异。

  • 钱包和交易所:大多数与 ERC-20 兼容的工具都可以显示余额并启动转移。但是,不寻常的经济学(转移时铸造型)可能会使使用者和定价机制感到困惑。
  • 授权和 TransferFrom:仍然为了互操作性而实现,但预期的逻辑(借记 from 余额)不适用。

测试用例

  1. 初始条件
    • 部署合约,其中 maxSupply = 1,000,000 DIFFtransferFee = 0.001 ETH
    • totalSupply = 0
    • 所有者设置参数并通过 maxSupply()transferFee() getter 进行验证。
  2. 转移时铸造
    • 用户 A 调用 transfer(B, 100),其中 msg.value = 0.1 ETH(假设 transferFee = 0.001 ETH)。
    • 检查 balances[B] == 100totalSupply == 100
    • 检查合约现在持有来自费用的 0.1 ETH。
  3. 超过最大供应量
    • 如果 totalSupply = 999,950 并且有人尝试转移 100 个代币,导致 totalSupply 超过 1,000,000,则交易回滚。
  4. 销毁代币
    • 用户 B 调用 burn(50)
    • 检查 balances[B] == 50totalSupply == 50 小于之前。
    • 发出 Burn 事件。
  5. 更新费用和提取资金
    • 所有者调用 setTransferFee(0.002 ETH)
    • 发出 FeeUpdated 事件。
    • 所有者调用 withdrawFees(ownerAddress)
    • 检查 ownerAddress 是否收到累积的费用。

参考实现

参考实现位于 EIPs 存储库的资产文件夹下。该实现包括:

  • 实现 DIFF 标准的基本合约。
    contract DiffusiveToken {
      // -----------------------------------------
      // State Variables
      // 状态变量
      // -----------------------------------------
    
      string public name;
      string public symbol;
      uint8 public decimals;
    
      uint256 public totalSupply;
      uint256 public maxSupply;
      uint256 public transferFee; // Fee per token transferred in wei
      // 以 wei 为单位的每个转移代币的费用
    
      address public owner;
    
      // -----------------------------------------
      // Events
      // 事件
      // -----------------------------------------
    
      event Transfer(address indexed from, address indexed to, uint256 amount);
      event Burn(address indexed burner, uint256 amount);
      event FeeUpdated(uint256 newFee);
      event MaxSupplyUpdated(uint256 newMaxSupply);
      event Approval(address indexed owner, address indexed spender, uint256 value);
    
      // -----------------------------------------
      // Modifiers
      // 修饰符
      // -----------------------------------------
    
      modifier onlyOwner() {
          require(msg.sender == owner, "DiffusiveToken: caller is not the owner");
          _;
      }
    
      // -----------------------------------------
      // Constructor
      // 构造函数
      // -----------------------------------------
    
      /**
       * @dev Constructor sets the initial parameters for the Diffusive Token.
       * @param _name Token name
       * @param _symbol Token symbol
       * @param _decimals Decimal places
       * @param _maxSupply The max supply of tokens that can ever exist
       * @param _transferFee Initial fee per token transferred in wei
       * @dev 构造函数设置 Diffusive Token 的初始参数。
       * @param _name 代币名称
       * @param _symbol 代币符号
       * @param _decimals 小数位数
       * @param _maxSupply 可以存在的代币的最大供应量
       * @param _transferFee 以 wei 为单位的每个转移代币的初始费用
       */
      constructor(
          string memory _name,
          string memory _symbol,
          uint8 _decimals,
          uint256 _maxSupply,
          uint256 _transferFee
      ) {
          name = _name;
          symbol = _symbol;
          decimals = _decimals;
          maxSupply = _maxSupply;
          transferFee = _transferFee;
          owner = msg.sender;
          totalSupply = 0; // Initially, no tokens are minted
          // 最初,没有铸造任何代币
      }
    
      // -----------------------------------------
      // External and Public Functions
      // 外部和公共函数
      // -----------------------------------------
    
      /**
       * @notice Returns the token balance of the given address.
       * @param account The address to query
       * @notice 返回给定地址的代币余额。
       * @param account 要查询的地址
       */
      function balanceOf(address account) external view returns (uint256) {
          return balances[account];
      }
    
      /**
       * @notice Transfers `amount` tokens to address `to`, minting new tokens in the process.
       * @dev Requires payment of native currency: transferFee * amount.
       * @param to Recipient address
       * @param amount Number of tokens to transfer
       * @return True if successful
       * @notice 将 `amount` 个代币转移到地址 `to`,在此过程中铸造新的代币。
       * @dev 需要支付原生货币:transferFee * amount。
       * @param to 接收者地址
       * @param amount 要转移的代币数量
       * @return 如果成功则为 True
       */
      function transfer(address to, uint256 amount) external payable returns (bool) {
          require(to != address(0), "DiffusiveToken: transfer to zero address");
          require(amount > 0, "DiffusiveToken: amount must be greater than zero");
    
          uint256 requiredFee = transferFee * amount;
          require(msg.value >= requiredFee, "DiffusiveToken: insufficient fee");
    
          // Check max supply limit
          // 检查最大供应量限制
          require(totalSupply + amount <= maxSupply, "DiffusiveToken: would exceed max supply");
    
          // Mint new tokens to `to`
          // 将新代币铸造到 `to`
          balances[to] += amount;
          totalSupply += amount;
    
          emit Transfer(msg.sender, to, amount);
          return true;
      }
    
      /**
       * @notice Burns `amount` tokens from the caller's balance, decreasing total supply.
       * @param amount The number of tokens to burn
       * @notice 从调用者的余额中销毁 `amount` 个代币,从而减少总供应量。
       * @param amount 要燃烧的代币数量
       */
      function burn(uint256 amount) external {
          require(amount > 0, "DiffusiveToken: burn amount must be greater than zero");
          require(balances[msg.sender] >= amount, "DiffusiveToken: insufficient balance");
    
          balances[msg.sender] -= amount;
          totalSupply -= amount;
    
          emit Burn(msg.sender, amount);
      }
    
      /**
       * @notice Approves `spender` to transfer up to `amount` tokens on behalf of `msg.sender`.
       * @param spender The address authorized to spend
       * @param amount The max amount they can spend
       * @notice 批准 `spender` 代表 `msg.sender` 转移最多 `amount` 个代币。
       * @param spender 授权消费的地址
       * @param amount 他们可以消费的最大金额
       */
      function approve(address spender, uint256 amount) external returns (bool) {
          require(spender != address(0), "DiffusiveToken: approve to zero address");
          allowances[msg.sender][spender] = amount;
          emit Approval(msg.sender, spender, amount);
          return true;
      }
    
      /**
       * @notice Returns the current allowance of `spender` for `owner`.
       * @param _owner The owner of the tokens
       * @param _spender The address allowed to spend the tokens
       * @notice 返回 `spender` 对 `owner` 的当前津贴。
       * @param _owner 代币的所有者
       * @param _spender 允许花费代币的地址
       */
      function allowance(address _owner, address _spender) external view returns (uint256) {
          return allowances[_owner][_spender];
      }
    
      /**
       * @notice Transfers `amount` tokens from `from` to `to` using the allowance mechanism.
       * @dev The `from` account does not lose tokens; this still mints to `to`.
       * @param from The address from which the allowance has been given
       * @param to The recipient address
       * @param amount The number of tokens to transfer (mint)
       * @notice 使用津贴机制将 `amount` 个代币从 `from` 转移到 `to`。
       * @dev `from` 帐户不会丢失代币;这仍然会铸造到 `to`。
       * @param from 已授予该津贴的地址
       * @param to 收件人地址
       * @param amount 要转移(铸造)的代币数量
       */
      function transferFrom(address from, address to, uint256 amount) external payable returns (bool) {
          require(to != address(0), "DiffusiveToken: transfer to zero address");
          require(amount > 0, "DiffusiveToken: amount must be greater than zero");
    
          uint256 allowed = allowances[from][msg.sender];
          require(allowed >= amount, "DiffusiveToken: allowance exceeded");
    
          // Deduct from allowance
          // 从津贴中扣除
          allowances[from][msg.sender] = allowed - amount;
    
          uint256 requiredFee = transferFee * amount;
          require(msg.value >= requiredFee, "DiffusiveToken: insufficient fee");
    
          // Check max supply
          // 检查最大供应量
          require(totalSupply + amount <= maxSupply, "DiffusiveToken: would exceed max supply");
    
          // Mint tokens to `to`
          // 将代币铸造到 `to`
          balances[to] += amount;
          totalSupply += amount;
    
          emit Transfer(from, to, amount);
          return true;
      }
    
      // -----------------------------------------
      // Owner Functions
      // 所有者函数
      // -----------------------------------------
    
      /**
       * @notice Updates the maximum supply of tokens. Must be >= current totalSupply.
       * @param newMaxSupply The new maximum supply
       * @notice 更新代币的最大供应量。必须 >= 当前 totalSupply。
       * @param newMaxSupply 新的最大供应量
       */
      function setMaxSupply(uint256 newMaxSupply) external onlyOwner {
          require(newMaxSupply >= totalSupply, "DiffusiveToken: new max < current supply");
          maxSupply = newMaxSupply;
          emit MaxSupplyUpdated(newMaxSupply);
      }
    
      /**
       * @notice Updates the per-token transfer fee.
       * @param newFee The new fee in wei per token transferred
       * @notice 更新每个代币的转移费用。
       * @param newFee 以 wei 为单位的每个转移代币的新费用
       */
      function setTransferFee(uint256 newFee) external onlyOwner {
          transferFee = newFee;
          emit FeeUpdated(newFee);
      }
    
      /**
       * @notice Allows the owner to withdraw accumulated native currency fees.
       * @param recipient The address that will receive the withdrawn fees
       * @notice 允许所有者提取累积的原生货币费用。
       * @param recipient 将收到提取费用的地址
       */
      function withdrawFees(address payable recipient) external onlyOwner {
          require(recipient != address(0), "DiffusiveToken: withdraw to zero address");
          uint256 balance = address(this).balance;
          (bool success, ) = recipient.call{value: balance}("");
          require(success, "DiffusiveToken: withdrawal failed");
      }
    
      // -----------------------------------------
      // Fallback and Receive
      // 回退和接收
      // -----------------------------------------
    
      // Allows the contract to receive Ether.
      // 允许合约接收 Ether。
      receive() external payable {}
    }
    
  • 用于测试和演示目的的接口和辅助合约。

安全考虑

  • 重入:使用 Checks-Effects-Interactions 模式处理费用转移。考虑使用 OpenZeppelin 中的 ReentrancyGuard 来防止重入调用。
  • 溢出/下溢:Solidity 0.8.x 默认情况下会防止这种情况。
  • 合约余额管理:确保发送了足够的原生货币来支付费用。如果费用不足,则回滚。
  • 访问控制:只有所有者可以更新 transferFeemaxSupply。使用正确的 onlyOwner 修饰符。

版权

版权及相关权利通过 CC0 放弃。

Citation

Please cite this document as:

Cheng Qian (@jamesavechives) <james.walstonn@gmail.com>, "ERC-7837: 扩散型代币," Ethereum Improvement Proposals, no. 7837, December 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7837.