文章

Web 3 学习笔记 3 | Fungible Token 同质化代币合约开发学习


免责声明

本文所述行为仅限于技术设计和本地模拟部署,不涉及链上操作。所有内容仅供学习和研究之用,读者在进行相关操作时,请遵循所在地区的法律法规,确保操作合法合规。


在之前的学习中,我们了解了同质化代币是 Web3 应用中的重要一部分,今天我们将学习如何使用 Solidity 开发并构建一个简单的同质化代币合约。

构建合约

首先是代币合约骨架的构建,以下是构建合约所需要的几个重要步骤:

  1. 指定合约使用的编译器版本和对合约名称进行定义

  2. 定义一个变量来表示代币的发行者

  3. 定义一个构造函数,帮助我们完成合约的初始化

创建合约

首先,我们需要在代码中指定合约的编译器版本,这样可以确保后续代码能够正确编译。以下是指定版本的示例:

pragma solidity 0.8.17;

这里我们选择了 0.8.17 版本的编译器。值得注意的是,版本号只能是 0.8.17,或者使用 ^0.8.17 表示版本需大于等于 0.8.17,但小于 0.9.0。

指定版本之后,我们创建合约:

pragma solidity 0.8.17;

contract NxToken { }

定义代币发行者

对于代币合约,我们通常需要一个发行者,使用 address 来表示合约的归属人。我们可以在合约中定义一个私有变量 owner

pragma solidity 0.8.17;

contract NxToken {
  address private owner;
}

发行者就是合约的部署者,使用 Solidity 的全局变量 msg.sender 来获取部署者地址。我们将在构造函数中初始化 owner

pragma solidity 0.8.17;

contract NxToken {
  address private owner;

  constructor() {
    owner = msg.sender;
  }
}

定义重要变量

在完成合约框架后,接下来需要完善合约的数据结构。首先,我们定义 balances 映射,跟踪每个用户地址对应的代币余额。我们可以使用 mapping 来存储用户地址和代币余额:

pragma solidity 0.8.17;

contract NxToken {
  address private owner;
  mapping (address => uint256) private balances;

  constructor() {
    owner = msg.sender;
  }
}

为了简化计算,我们还可以定义一个变量 totalSupply,用于存储已发行代币的总量:

pragma solidity 0.8.17;

contract NxToken {
  address private owner;
  mapping (address => uint256) private balances;
  uint256 public totalSupply;

  constructor() {
    owner = msg.sender;
  }
}

代币铸造

我们将实现代币合约的第一个功能:铸造代币。首先定义 mint 函数,该函数接受两个参数:接收代币的地址 recipient 和铸造的代币数额 amount

pragma solidity 0.8.17;

contract NxToken {
  address private owner;
  mapping (address => uint256) private balances;
  uint256 public totalSupply;

  constructor() {
    owner = msg.sender;
  }

  function mint(address recipient, uint256 amount) public { }
}

在铸造代币之前,我们需要检查函数调用者是否为代币发行者,确保只有发行者可以铸造代币:

pragma solidity 0.8.17;

contract NxToken {
  address private owner;
  mapping (address => uint256) private balances;
  uint256 public totalSupply;

  constructor() {
    owner = msg.sender;
  }

  function mint(address recipient, uint256 amount) public {
    require(msg.sender == owner, "Only the owner can mint tokens");
  }
}

铸造代币时,我们需要指定接收代币的账户地址和代币数量,并更新相关的余额和总供应量:

pragma solidity 0.8.17;

contract NxToken {
  address private owner;
  mapping (address => uint256) private balances;
  uint256 public totalSupply;

  constructor() {
    owner = msg.sender;
  }

  function mint(address recipient, uint256 amount) public {
    require(msg.sender == owner, "Only the owner can mint tokens");
    balances[recipient] += amount;
    totalSupply += amount;
  }
}

完善功能

查询余额

为了让用户能够查看自己或其他地址的余额,我们需要实现 balanceOf 函数。

pragma solidity 0.8.17;

contract NxToken {
  address private owner;
  mapping (address => uint256) private balances;
  uint256 public totalSupply;

  constructor() {
    owner = msg.sender;
  }

  function mint(address recipient, uint256 amount) public {
    require(msg.sender == owner, "Only the owner can mint tokens");
    balances[recipient] += amount;
    totalSupply += amount;
  }

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

实现转账

转账功能是代币系统中非常关键的一部分。转账过程包括以下几个步骤:

  1. 检查转账者的余额是否足够;

  2. 获取转账者和接收者的余额;

  3. 更新转账者的余额;

  4. 更新接收者的余额。

实现 transfer 函数如下:

function transfer(address recipient, uint256 amount) public returns (bool) {
  require(amount <= balances[msg.sender], "Not enough balance.");
  balances[msg.sender] -= amount;
  balances[recipient] += amount;
  return true;
}

编译和部署

最终,得到完整的合约内容如下:

pragma solidity 0.8.17;

contract NxToken {
  mapping (address => uint256) private balances;
  uint256 public totalSupply;
  address private owner;

  constructor() {
    owner = msg.sender;
  }

  function mint(address recipient, uint256 amount) public {
    require(msg.sender == owner, "Only the owner can mint tokens");
    balances[recipient] += amount;
    totalSupply += amount;
  }

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

  function transfer(address recipient, uint256 amount) public returns (bool) {
    require(amount <= balances[msg.sender], "Not enough balance.");
    balances[msg.sender] -= amount;
    balances[recipient] += amount;
    return true;
  }
}

我们可以尝试在在线 IDE 中进行编译运行

https://remix.ethereum.org/

如下图所示,我们创建一个 .sol 文件,并将代码复制粘贴过来

然后在旁边的 Solidity Complier 中,选取合适的编译器版本,点击编译

在 DEPLOY 页面中选择 Remix VM,然后点击 Deploy,就可以在虚拟环境中进行部署,可以看到,我们用来部署的虚拟地址的账户余额不再是一开始的 100,因为每次链上的行为都需要支付 gas 费才能在链上持久化,此处只是模拟的效果

窗口下方可以对每个函数模拟调用,并实时获得结果,如图所示:

总结

我们学习了如何使用 Solidity 创建一个简单的同质化代币合约。我们从基础的合约结构开始,逐步实现了代币的铸造、余额查询以及转账功能。在实际操作中,我们使用了 mapping 来存储每个地址的代币余额,并且通过构造函数和权限控制确保了合约的安全性。

希望这篇分享能为你带来启发!如果你有任何问题或建议,欢迎在评论区留言,与我共同交流探讨。

License:  CC BY 4.0