单个solidity合约的文件结构

  • Louis
  • 更新于 1天前
  • 阅读 93

基本定义本节是我们合约的开头,我们首先来介绍下合约的文件结构。一个单个合约文件的结构需要清晰、有条理,便于阅读、理解和维护。文件头部声明SPDX-License标识符用于声明合约的许可证类型(MIT、Apache-2.0等)。

基本定义

本节是我们合约的开头,我们首先来介绍下合约的文件结构。一个单个合约文件的结构需要清晰、有条理,便于阅读、理解和维护。

文件头部声明

SPDX-License 标识符

  • 用于声明合约的许可证类型(MIT、Apache-2.0 等)。
  • 非强制,但强烈推荐,尤其在开源项目中。在 Remix 中如果不声明这个标识符会报警告
  • 格式:// SPDX-License-Identifier: <license-name>

示例:

// SPDX-License-Identifier: MIT

编译器版本声明

  • 指定 Solidity 的编译器版本范围。
  • 推荐使用 ^>= <,避免版本不兼容问题。

示例

pragma solidity ^0.8.0;

随着 solidity 编译器版本的升级,一些较早时间写的合约使用新版本的编译器编译会出现报错的情况,因此,合约头部声明需要指定版本范围。

导入依赖

  • 文件依赖:使用 import 语句导入其他合约、库或接口。
  • 路径选择

<!---->

    • 使用相对路径导入项目内文件:./
    • 使用绝对路径导入第三方库:@openzeppelin/contracts/...

示例:

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./libraries/SafeMath.sol";
import "./interfaces/IUniswapV2Router.sol";

合约定义

在文件中声明主要合约,以下是常见的内容组织方式:

状态变量

  • 描述合约的全局状态。
  • 一般按以下顺序排列:
    1. 公共变量:使用 public 修饰,如 address public owner
    2. 私有变量:使用 private 修饰 如 uint256 private _balance
    3. 常量:使用 constantimmutable 声明。
    4. 映射或复杂结构:如 mappingstruct

示例:

contract MyContract {
    address public owner; // 合约的管理员
    uint256 public totalSupply; // 总供应量
    mapping(address => uint256) private balances; // 用户余额

    uint256 public constant FEE_RATE = 2; // 手续费率
    bytes32 public immutable DOMAIN_SEPARATOR; // EIP-712 域分隔符
}

事件声明

  • 用于定义日志信息。
  • 一般用于记录合约中的关键操作(如转账、状态变更)。
  • 使用 indexed 关键字支持事件过滤。

示例:

event Transfer(address indexed from, address indexed to, uint256 amount);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

构造函数

  • 用于初始化合约状态变量。
  • 仅在合约部署时调用一次。
  • 通常在构造函数中设置管理员(owner)。

示例:

constructor(uint256 _initialSupply) {
    owner = msg.sender; // 部署者为管理员
    totalSupply = _initialSupply;
}

修饰符

  • 修饰符是对函数调用的访问控制或预检查。
  • 使用 modifier 定义,_ 表示修饰符内插入函数代码。

示例:

modifier onlyOwner() {
    require(msg.sender == owner, "Caller is not the owner");
    _;
}

modifier validAddress(address _addr) {
    require(_addr != address(0), "Invalid address");
    _;
}

函数定义

函数分为以下几类,按顺序排列更清晰:

  1. 公共函数( external public
    • 直接与外部交互的接口。
    • 通常是合约的主要功能。
  1. 内部函数( internal private
    • 用于辅助逻辑,其他函数可调用。
  1. 视图函数( view pure
    • 不修改状态,用于查询。
  1. 支付函数( payable
    • 允许接收 ETH
  1. 回退函数( fallback receive
    • 用于处理直接发送 ETH 的情况。

示例:

function transfer(address to, uint256 amount) external onlyOwner {
    require(to != address(0), "Invalid address");
    require(amount &lt;= totalSupply, "Insufficient balance");

    totalSupply -= amount;
    balances[to] += amount;
    emit Transfer(msg.sender, to, amount);
}

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

fallback() external payable {
    // 回退函数逻辑
}

可复用逻辑

  • 使用库或内部函数封装逻辑。
  • 尽量避免代码重复。

示例:

function _safeTransfer(address to, uint256 amount) internal {
    require(to != address(0), "Invalid address");
    balances[to] += amount;
}

文件末尾

确保文件末尾有明确结束:

  • 如果合约较长,可以加注释标记结束。

示例:

// End of MyContract
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。
该文章收录于 Solidity从入门到进阶
5 订阅 23 篇文章

0 条评论

请先 登录 后评论
Louis
Louis
web3 developer,技术交流或者有工作机会可加VX: magicalLouis