当前位置: 首页 > ai >正文

Solidity 编程进阶

Solidity 全局变量

什么是全局变量?

Solidity 提供了一些内置的 全局变量(Global Variables),用于访问区块链状态、交易信息等数据。这些变量不需要额外声明,可以在智能合约中直接调用。

主要全局变量分类

Solidity 全局变量主要分为以下几类:

区块信息(Block Information)

示例:获取当前区块号和时间戳

contract BlockInfo {function getBlockDetails() public view returns (uint, uint) {return (block.number, block.timestamp);}
}

2.2 交易信息(Transaction Information)

⚠️ 注意:tx.origin 可能导致安全漏洞,避免用于身份验证! 示例:检查交易发起者 contract TxInfo { function getTxOrigin() public view returns (address) { return tx.origin; } }

消息信息(Message Information)

示例:获取调用者地址和发送的 ETH 数量

contract MessageInfo {function getMessageDetails() public payable returns (address, uint) {return (msg.sender, msg.value);}
}

以太坊环境信息(Ethereum Environment Information)

示例:获取当前合约的地址和余额

contract ContractInfo {function getContractDetails() public view returns (address, uint) {return (address(this), address(this).balance);}
}

this 关键字

this 关键字用于引用当前合约的实例。它可以用于:

  1. 获取当前合约的地址。
  2. 在合约内部调用自身的函数(但会创建新的交易)。
  3. 访问当前合约的 balance。 示例:使用 this 关键字
contract ThisExample {function getContractAddress() public view returns (address) {return address(this);}function getContractBalance() public view returns (uint) {return address(this).balance;}
}

注意事项:

  • this 关键字调用合约自身的函数时,会触发新的交易,因此会消耗额外的 gas。
  • this.balance 获取的是合约的 ETH 余额,而 msg.sender.balance 可以用于获取调用者的余额。

其他特殊全局变量

示例:使用 keccak256 计算哈希值

contract HashExample {function getHash(string memory input) public pure returns (bytes32) {return keccak256(abi.encodePacked(input));}
}
  1. 应用场景 ✅ 随机数生成(⚠️ 仅限低安全性用途)
function getRandomNumber() public view returns (uint) {return uint(keccak256(abi.encodePacked(block.timestamp, msg.sender))) % 100;
}

✅ 支付合约

contract Payment {function pay() public payable {}function getBalance() public view returns (uint) {return address(this).balance;}
}

✅ 安全身份验证(避免 tx.origin)

contract Auth {address public owner;constructor() { owner = msg.sender; }function isOwner() public view returns (bool) {return msg.sender == owner;}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;contract ABI{function encodeData(string memory text,uint256 number) public pure  returns (bytes memory,bytes memory){//编码return (//0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000045a6f6f6400000000000000000000000000000000000000000000000000000000abi.encode(text,number),//返回会带0//0x5a6f6f640000000000000000000000000000000000000000000000000000000000000012abi.encodePacked(text,number//返回不是带0,压缩了,这个没办法解码,需要补齐才可以));} //解码function decodeData(bytes memory encodedData) public pure returns (string memory text, uint256 number) {return abi.decode(encodedData, (string, uint256));
}//当前函数签名function getSelector () public pure returns (bytes4){return msg.sig;}function computeSelector(string memory fun) public pure returns (bytes4){return bytes4(keccak256(bytes(fun)));}//0xa9059cbb0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc40000000000000000000000000000000000000000000000000000000000000064function transfer (address addr, uint256 amount) public  pure  returns (bytes memory){return msg.data;}//调研函数生成 msg.data//0xa9059cbb0000000000000000000000005b38da6a701c568545dcfcb03fcb875f56beddc40000000000000000000000000000000000000000000000000000000000000064function encodeFunctionCall () public pure  returns (bytes memory){return  abi.encodeWithSignature("transfer(address,uint256)",0x5B38Da6a701c568545dCfcB03FcB875f56beddC4, 100);//encodeWithSignature时,注意去掉空格和变量,否则,transfer和此方法的返回不一致}//哈希运算function hashFunctions(string memory input) public  pure returns (bytes32,bytes32,bytes32){bytes memory data = abi.encodePacked(input);return (keccak256(data),sha256(data),ripemd160(data));}//数学运算function modulaMath(uint256 x,uint256 y,uint256 z) public  pure  returns (uint256 ,uint256){return (addmod(x, y, z),mulmod(x, y, z));}//椭圆曲线恢复公钥(ecrecover)function recoverAddress(bytes32 hash,uint8 v,bytes32 r,bytes32 s)public  pure returns (address){return ecrecover(hash, v, r, s);}
}

错误处理

1. require 语句

require 用于在执行函数之前检查条件是否满足。如果条件不满足,require 会抛出异常并回滚所有状态更改。它通常用于验证用户输入或外部调用的合法性。

语法
require(条件, "Error code");
示例
function transfer(address to, uint256 amount) public {require(to != address(0), "无效的接收地址");require(amount > 0, "转账金额必须大于0");// ... 转账逻辑 ...
}
特点
  • 用于输入验证
  • 抛出异常时会回滚状态
  • 可以附带错误信息

2. assert 语句

assert 用于检查内部逻辑的正确性,通常用于验证合约内部状态是否一致。如果条件不满足,assert 会抛出异常并回滚所有状态更改。它通常用于调试和确保合约逻辑的正确性。

语法
assert(条件);
示例
function divide(uint256 a, uint256 b) public pure returns (uint256) {uint256 result = a / b;assert(b != 0); // 确保除数不为0return result;
}
特点
  • 用于内部逻辑验证
  • 抛出异常时会回滚状态
  • 通常不附带错误信息

revert 关键字

revert 是 Solidity 中用于 回退交易(transaction)并恢复状态 的一个关键字。它经常用于错误处理,当某些条件不满足时立即终止执行并返还剩余的 gas。


基本语法
revert();
revert("错误信息");

工作机制

当调用 revert() 时:

  • 当前函数执行被中止;

  • 所有状态更改(包括调用之前的更改)都会被回滚;

  • 未使用的 gas 会被退还给调用者;

  • 如果有错误信息,会被返回给调用者(前端或调用合约可以读取这个信息)。


使用场景
  1. 条件检查失败
if (msg.value < price) {revert("Insufficient funds");
}
  1. 配合自定义错误使用(gas 更省):
error NotOwner();function onlyOwner() public view {if (msg.sender != owner) {revert NotOwner();}
}
自定义错误
// 简单错误(不带参数)
error Unauthorized();// 带参数的错误
error InsufficientBalance(uint256 available, uint256 required);// 复杂参数的错误
error TransferFailed(address from, address to, uint256 amount, string reason);

触发自定义错误

function withdraw(uint256 amount) public {if (amount > balances[msg.sender]) {revert InsufficientBalance(balances[msg.sender], amount);}// 其他逻辑...
}
优势

  1. Gas 效率:比 require/revert 节省约 50-70% Gas

    • 普通 require: ~45 Gas

    • 自定义错误: ~20 Gas

  2. 参数化错误信息:可以携带任意数量和类型的参数

  3. 类型安全:编译器会检查错误类型和参数匹配

  4. ABI 编码:自动生成错误签名,方便客户端解析


💡 例子

pragma solidity ^0.8.0;contract Shop {address public owner;uint256 public price = 1 ether;constructor() {owner = msg.sender;}function buy() public payable {if (msg.value < price) {revert("Not enough ETH sent");}// 购买逻辑}
}

 三者对比

特性requirerevertassert
✅ 功能条件检查,不满足则终止执行手动触发终止执行,带/不带错误信息检查程序内部逻辑是否永远成立
🎯 用途验证输入、权限、调用状态更灵活地中止执行用来发现bug不变量失效
🔄 状态回滚✅ 会回滚✅ 会回滚✅ 会回滚
💸 Gas 返还✅ 未消耗的 gas 会返还✅ 会返还❌ 不返还(panic,重大错误)
📢 可带信息✅ "错误信息"✅ "错误信息" 或 error Type()❌ 无信息(只有 Panic code)
🧱 推荐使用外部输入条件、require权限检查等业务逻辑失败、自定义 error 抛错测试 / 内部断言,永远不该失败

 try-catch 语句

Solidity 从 0.6.0 版本开始引入了 try-catch 语句,用于处理外部函数调用和合约创建中的错误。这是 Solidity 中错误处理的重要机制之一。

适用场景

try-catch 只能用于以下两种情况:

  1. 外部函数调用(使用 address 调用或合约实例调用)

  2. 合约创建(使用 new 关键字)

完整的 try-catch 结构
try externalContract.someFunction(arg1, arg2) returns (uint256 result) {// 调用成功时执行的代码// 可以使用返回值 result
} catch Error(string memory reason) {// 当 revert(reasonString) 或 require(false, reasonString) 被调用时// 可以访问 reason
} catch Panic(uint errorCode) {// 当发生 panic 错误时(如除以零、数组越界等)// errorCode 表示错误类型
} catch (bytes memory lowLevelData) {// 当错误不符合上述任何类型时// 包含低级错误数据
}
错误类型详解
  1. Error(string memory reason)
  • 对应 revert("description") 或 require(false, "description")

  • 可以获取错误描述字符串

2. Panic(uint errorCode)

  • 对应 Solidity 的 "panic" 错误(类似断言失败)

  • 常见错误码:

    • 0x01: 断言失败

    • 0x11: 算术运算溢出

    • 0x12: 除以零

    • 0x21: 无效的数组索引

    • 0x31: 分配过多内存

    • 0x32: 调用未初始化的内部函数类型变量

  1. 默认的 catch 块
  • 捕获所有其他类型的错误

  • 包含原始错误数据(bytes 类型)

自定义修饰符

自定义修饰符(Modifier)用于在函数执行前或执行后添加额外的检查或逻辑。它可以复用代码,并且常用于权限控制或状态检查。

语法

modifier 修饰符名 {// 前置逻辑_; // 执行函数体// 后置逻辑
}

示例

contract Owner {address public owner;constructor() {owner = msg.sender;}// 自定义修饰符,仅允许合约所有者调用modifier onlyOwner {require(msg.sender == owner, "仅合约所有者可调用");_;}function changeOwner(address newOwner) public onlyOwner {owner = newOwner;}
}

特点

  • 可以复用代码
  • 常用于权限控制
  • 可以在函数执行前后添加逻辑

综合示例

contract Bank {mapping(address => uint256) public balances;// 自定义修饰符,检查余额是否足够modifier hasSufficientBalance(uint256 amount) {require(balances[msg.sender] >= amount, "余额不足");_;}// 存款函数function deposit() public payable {require(msg.value > 0, "存款金额必须大于0");balances[msg.sender] += msg.value;}// 取款函数function withdraw(uint256 amount) public hasSufficientBalance(amount) {balances[msg.sender] -= amount;payable(msg.sender).transfer(amount);assert(balances[msg.sender] >= 0); // 确保余额不为负}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;contract code {function requireError(bool condition) public pure returns (uint256){require(condition,"input is not true");return 1;}function assertError (bool condition) public pure returns (uint256){assert(condition);return 2;}function revertError(bool condition) public pure  returns (uint256){if(!condition){revert("123");}return 3;}}

地址类型

在 Solidity 中,address 类型用于存储以太坊地址。地址可以是外部账户(EOA)或合约账户。地址类型是 Solidity 中最基本的数据类型之一,常用于处理以太坊账户之间的交互。

地址类型的基本操作

// 声明一个地址变量
address public myAddress;// 获取当前调用者的地址
address public caller = msg.sender;// 地址类型之间的比较
function compareAddress(address addr1, address addr2) public pure returns (bool) {return addr1 == addr2;
}// 地址类型的转换
function toBytes(address addr) public pure returns (bytes memory) {return abi.encodePacked(addr);
}

地址类型的成员变量和方法

// 获取地址的余额
function getBalance(address addr) public view returns (uint256) {return addr.balance;
}// 向地址转账
function sendEther(address payable recipient) public payable {recipient.transfer(msg.value);
}// 调用地址的代码(低级别调用)
function callContract(address addr, bytes memory data) public returns (bool, bytes memory) {(bool success, bytes memory result) = addr.call(data);return (success, result);
}

接口

接口(Interface)是 Solidity 中用于定义合约之间交互的一种方式。通过接口,一个合约可以调用另一个合约的函数,而无需知道其具体实现。

定义接口

// 定义一个简单的接口
interface IERC20 {function transfer(address to, uint256 amount) external returns (bool);function balanceOf(address account) external view returns (uint256);
}

使用接口

contract MyContract {// 使用接口与另一个合约交互function transferToken(address tokenAddress, address to, uint256 amount) public returns (bool) {IERC20 token = IERC20(tokenAddress);return token.transfer(to, amount);}// 获取代币余额function getTokenBalance(address tokenAddress, address account) public view returns (uint256) {IERC20 token = IERC20(tokenAddress);return token.balanceOf(account);}
}

接口的特点

  • 接口只能声明函数,不能实现函数。
  • 接口中的函数必须标记为 external
  • 接口不能定义状态变量或构造函数。
  • 接口可以继承其他接口。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;// 定义一个简单的银行接口
interface IBank {function deposit() external payable;function withdraw(uint256 amount) external;function getBalance() external view returns (uint256);
}// 实现银行接口的合约
contract Bank is IBank {mapping(address => uint256) public balances;function deposit() external payable override {require(msg.value > 0, "save amount must than o");balances[msg.sender] += msg.value;}function withdraw(uint256 amount) external override {require(balances[msg.sender] >= amount, "amout not enough");balances[msg.sender] -= amount;payable(msg.sender).transfer(amount);}function getBalance() external view override returns (uint256) {return balances[msg.sender];}
}// 使用银行接口的合约
contract BankUser {function depositToBank(address bankAddress) external payable {IBank bank = IBank(bankAddress);bank.deposit{value: msg.value}();}function withdrawFromBank(address bankAddress, uint256 amount) external {IBank bank = IBank(bankAddress);bank.withdraw(amount);}receive() external payable { }  fallback() external payable { }function getBankBalance(address bankAddress) external view returns (uint256) {IBank bank = IBank(bankAddress);return bank.getBalance();}
}

import 导包方式

在 Solidity 中,import 用于引入其他合约或库文件,以便在当前合约中使用它们的功能。import 语句可以用于引入本地文件或远程文件。

语法

// 引入本地文件
import "./MyContract.sol";// 引入远程文件
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol";// 引入整个目录
import "./utils/*";// 引入并重命名
import { MyContract as MC } from "./MyContract.sol";

// 引入 OpenZeppelin 的 ERC20 合约
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";contract MyToken is ERC20 {constructor() ERC20("MyToken", "MTK") {}
}

继承

Solidity 支持合约之间的继承,允许一个合约继承另一个合约的功能。继承可以复用代码,并且可以通过 override 关键字来重写父合约的函数。

contract Parent {function foo() public virtual returns (string memory) {return "Parent";}
}contract Child is Parent {function foo() public virtual override returns (string memory) {return "Child";}
}

示例

// SPDX-License-Identifier: MIT
pragma solidity ^0.8;// 父合约
contract Ownable {address public owner;constructor() {owner = msg.sender;}modifier onlyOwner() {require(msg.sender == owner, "Only owner can call this function");_;}function test () public  pure virtual  returns (uint256){return 2;}
}// 子合约
contract MyContract is Ownable {function changeOwner(address newOwner) public onlyOwner {owner = newOwner;}function test () public  pure override  returns (uint256){return 4;}
}

OpenZeppelin

OpenZeppelin 是一个广泛使用的 Solidity 库,提供了许多经过审计的智能合约模板,如 ERC20、ERC721、Ownable 等。使用 OpenZeppelin 可以大大减少开发时间并提高合约的安全性。

安装 OpenZeppelin

hardhat 会用到

npm install @openzeppelin/contracts

使用 OpenZeppelin 的 ERC20 合约

// 引入 OpenZeppelin 的 ERC20 合约
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";contract MyToken is ERC20 {constructor() ERC20("MyToken", "MTK") {_mint(msg.sender, 1000000 * 10 ** decimals());}
}

使用 OpenZeppelin 的 Ownable 合约

// 引入 OpenZeppelin 的 Ownable 合约
import "@openzeppelin/contracts/access/Ownable.sol";contract MyContract is Ownable {function specialFunction() public onlyOwner {// 只有合约所有者可以调用此函数}
}

综合示例

// 引入 OpenZeppelin 的 ERC20 和 Ownable 合约
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";contract MyToken is ERC20, Ownable {constructor() ERC20("MyToken", "MTK") {_mint(msg.sender, 1000000 * 10 ** decimals());}function mint(address to, uint256 amount) public onlyOwner {_mint(to, amount);}
}

http://www.xdnf.cn/news/17100.html

相关文章:

  • 8.6 JavaWeb(请求响应 P67-P74)
  • PyTorch入门引导
  • Go语言“fmt”包详解
  • 【Linux内核系列】:信号(上)
  • Docker的安装,服务器与客户端之间的通信
  • LeetCode每日一题,8-6
  • springboot项目justAuth扩展第二个小程序
  • Unity轻量观察相机
  • 功能安全和网络安全的综合保障流程
  • 云端软件工程智能代理:任务委托与自动化实践全解
  • CDP集群中通过Hive外部表迁移HBase数据的操作记录
  • 昇思+昇腾开发板+DeepSeek模型推理和性能优化
  • 自己本地搭建的服务器怎么接公网?公网IP直连服务器方法,和只有内网IP直接映射到互联网
  • 线性代数中矩阵的基本运算运算
  • 哲学中的主体性:历史演进、理论范式与当代重构
  • FLAN-T5:大规模指令微调的统一语言模型框架
  • python-自定义抠图
  • OpenSpeedy绿色免费版下载,提升下载速度,网盘下载速度等游戏变速工具
  • Datawhale AI夏令营 第三期 task2 稍微改进
  • MyBatis实现SQL
  • Python日志记录库——logaid
  • Centos-Stream 10 安装教程(2025版图文教程)
  • ASP3605I同步降压调节器的高频化设计与多相扩展技术优化方案
  • Python 函数详解
  • 重生之我在暑假学习微服务第十天《网关篇》
  • 微软Dragon Ambient eXperience (DAX) 深度解析
  • 《UE教程》第一章第六回——迁移独立项目(资源)
  • 【学习嵌入式day-17-数据结构-单向链表/双向链表】
  • 【计算机网络】6应用层
  • 深度学习·基础知识