用 Yul 编写 ERC20 智能合约
创始人
2024-11-05 14:39:58
0

Yul 是一种提供与以太坊虚拟机(EVM)直接交互的中间语言。
使用 Yul 编写 ERC20 代币合约,实现一个高度Gas 优化,同时遵循 ERC20 标准的合约

这篇指南的需求源于优化智能合约以提高性能和安全性,同时遵循 ERC20 标准。Yul 通过实现对合约代码的更低级别控制,从而实现更高效和安全的智能合约部署。

我们将通过详细介绍以下过程来解决这些挑战:

  • 在 Yul 中构建 ERC20 函数以实现有效的代币管理
  • 实施针对漏洞的安全措施
  • 优化 gas 使用以最小化交易成本

在结束时,读者将全面了解 Yul 中的 ERC20 合约开发。在开始之前,请确保你已经阅读了关于 Yul 的全面指南。

ERC20 和 Yul 简介

什么是 ERC20?

想象一下,你想创建自己的一种数字货币,可以在以太坊网络上流畅地进行交易、共享,甚至在在线游戏和应用程序中使用。ERC20 本质上是一组规则,可以帮助你以一种在以太坊网络上流畅运行的方式创建这种数字货币。它就像一份食谱,确保你的数字货币可以轻松地被交换和他人使用。

它涵盖了以下内容:

  • 创建和跟踪代币:它告诉你如何创建新的代币/硬币,可以创建多少代币,并跟踪谁拥有多少代币。
  • 发送代币:它向你展示如何安全地将代币从一个人转移到另一个人。
  • 使用带有权限的代币:它允许代币所有者让其他人花费其代币的一定数量,对于自动化服务或交易非常有用。

什么是 Yul?

Yul 就像是一种用于直接与 EVM 交流的秘密代码语言。当人们创建智能合约时,他们通常会用一种称为 Solidity 的语言来编写,这种语言更容易理解和使用。但有时,开发人员需要非常具体地告诉以太坊如何做事情,特别是如果他们想要在交易费用上节省 gas 或执行一些非常定制的操作。

这就是 Yul 的用武之地。把 Yul 想象成更接近机器语言,允许开发人员给出更精确和直接的指令。

Yul 让开发人员可以:

  • 控制细节:他们可以管理合约工作的细节,这在 Solidity 中很难做到
  • 节省 Gas:通过更直接的方式,可以使他们的合约使用更少的 gas
  • 执行高级技巧:对于非常专业的任务,Yul 允许开发人员编写更灵活和强大的代码。

现在我们对 ERC20 和 Yul 有了基本的了解,让我们开始使用 Yul 创建我们的智能合约。

设置你的开发环境

准备在 Yul 中编写 ERC20 合约非常简单。按照以下步骤进行设置:

  • 打开你的网络浏览器,转到Remix IDE网站。Remix IDE 是一个在线工具,用于编写、测试和部署以太坊合约。
  • 进入 Remix 后,你将开始在默认工作区。该区域允许你使用类似contracts(用于合约文件)、scripts(用于部署脚本)和tests(用于测试文件)的文件夹组织你的工作。
  • 转到contracts文件夹,并创建一个名为ERC20Yul.sol的新文件。该文件将包含你的 ERC20 代币的 Yul 代码。
  • 创建了你的ERC20Yul.sol文件后,你就可以开始使用 Yul 编写智能合约了。

在 Yul 中编写 ERC20 合约

首先,我们将为我们的智能合约奠定基础,并设置我们将使用的所有变量。

// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; contract ERC20Yul { } 

设置变量和常量

bytes32 internal constant _TRANSFER_HASH =         0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; bytes32 internal constant _APPROVAL_HASH =         0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;     bytes32 internal constant _INSUFFICIENT_BALANCE_SELECTOR =         0xf4d678b800000000000000000000000000000000000000000000000000000000;     bytes32 internal constant _INSUFFICIENT_ALLOWANCE_SELECTOR =         0x13be252b00000000000000000000000000000000000000000000000000000000;     bytes32 internal constant _RECIPIENT_ZERO_SELECTOR =         0x4c131ee600000000000000000000000000000000000000000000000000000000;     bytes32 internal constant _INVALID_SIG_SELECTOR =         0x8baa579f00000000000000000000000000000000000000000000000000000000;     bytes32 internal constant _EXPIRED_SELECTOR =         0x203d82d800000000000000000000000000000000000000000000000000000000;     bytes32 internal constant _STRING_TOO_LONG_SELECTOR =         0xb11b2ad800000000000000000000000000000000000000000000000000000000;     bytes32 internal constant _OVERFLOW_SELECTOR =         0x35278d1200000000000000000000000000000000000000000000000000000000;     bytes32 internal constant _EIP712_DOMAIN_PREFIX_HASH =         0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;     bytes32 internal constant _PERMIT_HASH =         0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;     bytes32 internal constant _VERSION_1_HASH =         0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;     bytes32 internal constant _MAX =         0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff;     bytes32 internal immutable _name;     bytes32 internal immutable _symbol;     uint256 internal immutable _nameLen;     uint256 internal immutable _symbolLen;     uint256 internal immutable _initialChainId;     bytes32 internal immutable _initialDomainSeparator;     mapping(address => uint256) internal _balances;     mapping(address => mapping(address => uint256)) internal _allowances;     uint256 internal _supply;     mapping(address => uint256) internal _nonces;     event Transfer(address indexed src, address indexed dst, uint256 amount);     event Approval(address indexed src, address indexed dst, uint256 amount); 
  • _TRANSFER_HASH_APPROVAL_HASH和类似的常量:

这些常量是特定字符串的预计算哈希,通常是事件签名或函数选择器。它们在内联汇编块中使用,以通过避免运行时计算这些哈希来优化 gas 使用。

  • _name_symbol

这些不可变变量以固定大小的bytes32格式存储代币的名称和符号。它们在合约部署期间设置,并且旨在存储和访问这些属性,而无需动态字符串存储。

  • _nameLen_symbolLen

这些不可变变量捕获了代币名称和符号的长度。这是必要的,因为名称和符号存储为bytes32,并且在需要时正确将它们转换回字符串。

  • _initialChainId_initialDomainSeparator

  • _initialChainId存储合约部署时的链 ID,用于 EIP-2612 的域分离符,以防止在不同链上的重放攻击。

  • _initialDomainSeparator是基于初始链 ID 预先计算的 EIP-712 域分隔符,同样在 EIP-2612 的上下文中使用。

  • _balances_allowances

  • _balances是一个映射,跟踪每个地址的代币余额,这是任何 ERC20 代币的基本部分

  • _allowances是一个映射的映射,跟踪一个地址被允许代表另一个地址花费多少代币,对于approvetransferFrom函数至关重要。

  • _supply

此变量跟踪代币的总供应量,在铸造或销毁代币时进行更新。

  • _nonces

用于 EIP-2612 permit 功能,此映射跟踪每个地址的 nonce,以确保每个许可调用都是唯一的,并防止重放攻击。

  • 事件声明(Transfer 和 Approval):

声明这些事件是为了通知外部订阅者代币的转移和授权,这对于 ERC20 代币的可用性至关重要。

实现构造函数

constructor(string memory name_, string memory symbol_) {     // get string lengths     bytes memory nameB = bytes(name_);     bytes memory symbolB = bytes(symbol_);     uint256 nameLen = nameB.length;     uint256 symbolLen = symbolB.length; // check strings are <=32 bytes     assembly {         if or(lt(0x20, nameLen), lt(0x20, symbolLen)) {             mstore(0x00, _STRING_TOO_LONG_SELECTOR)             revert(0x00, 0x04)         }     }     // compute domain separator     bytes32 initialDomainSeparator = _computeDomainSeparator(         keccak256(nameB)     );     // set immutables     _name = bytes32(nameB);     _symbol = bytes32(symbolB);     _nameLen = nameLen;     _symbolLen = symbolLen;     _initialChainId = block.chainid;     _initialDomainSeparator = initialDomainSeparator; } 

将 name_ 和 symbol_ 参数从字符串转换为字节,以获取它们的长度。

验证名称和符号是否都在 32 字节的限制内。这个限制是由于将这些参数存储在bytes32变量中,通过避免动态存储来优化 gas 成本。如果任一参数超过此限制,合约将使用自定义错误回滚。

  • 使用name_参数的哈希调用_computeDomainSeparator。此函数计算 EIP-712 域分隔符,对于安全实现 EIP-2612 的permit功能至关重要。域分隔符有助于确保为许可功能而签名的消息是特定于此合约和链的,以防止重放攻击。
  • 初始化_name_symbol_nameLen_symbolLen_initialChainId_initialDomainSeparator变量。

namesymbolbytes32格式直接从输入参数存储,确保高效的存储和访问。存储名称和符号的长度以便在需要时进行字符串转换。

在部署时存储链 ID 到_initialChainId,以支持域分隔符的特定链。

使用预先计算的值设置_initialDomainSeparator,以供permit函数使用。

Transfer函数

function transfer(address dst, uint256 amount)     public     virtual     returns (bool success) {     assembly {         // Check if the destination address is not zero.         if iszero(dst) {             mstore(0x00, _RECIPIENT_ZERO_SELECTOR)             revert(0x00, 0x04)         } // Load the sender's balance, check for sufficient balance, and update it.         mstore(0x00, caller())

相关内容

热门资讯

黑科技辅助!wpk辅助神器(透... 黑科技辅助!wpk辅助神器(透视)软件透明辅助挂(本来是真的有挂)-哔哩哔哩是一款可以让一直输的玩家...
5分钟了解“创思维正版辅助器下... 5分钟了解“创思维正版辅助器下载”详细透视开挂辅助安装-哔哩哔哩;一、创思维正版辅助器下载有挂的是的...
两分钟科普!wpk真吗,哈糖大... 两分钟科普!wpk真吗,哈糖大菠萝可以开挂吗,曝光教程(发现有挂)-哔哩哔哩哈糖大菠萝可以开挂吗辅助...
第一分钟了解(昆仑大厅)外挂辅... 第一分钟了解(昆仑大厅)外挂辅助插件(透视)详细教程(2022已更新)(哔哩哔哩);亲真的是有正版授...
黑科技辅助!wpk俱乐部长期盈... 黑科技辅助!wpk俱乐部长期盈利打法(透视)软件透明挂黑科技(切实存在有挂)-哔哩哔哩;1、让任何用...
第6分钟了解“功夫川嘛辅助器”... 第6分钟了解“功夫川嘛辅助器”详细透视开挂辅助器-哔哩哔哩;人气非常高,ai更新快且高清可以动的一个...
第五分钟辅助!xpoker辅助... 第五分钟辅助!xpoker辅助,德州透视插件,攻略教程(有挂方法)-哔哩哔哩德州透视插件辅助器中分为...
两分钟了解(皮皮跑胡子)外挂透... 两分钟了解(皮皮跑胡子)外挂透明挂辅助工具(辅助挂)透明挂教程(2020已更新)(哔哩哔哩);皮皮跑...
黑科技辅助!微扑克可以加入俱乐... 您好,微扑克可以加入俱乐部这款游戏可以开挂的,确实是有挂的,需要了解加微【136704302】很多玩...
8分钟了解“掌中乐游戏中心辅助... 8分钟了解“掌中乐游戏中心辅助器”详细透视开挂辅助脚本-哔哩哔哩;1、这是跨平台的掌中乐游戏中心辅助...