特殊函数 #
一、构造函数 constructor #
1.1 基本概念 #
构造函数在合约部署时自动执行一次,用于初始化合约状态。
solidity
contract ConstructorDemo {
address public owner;
uint256 public initialValue;
// 构造函数
constructor(uint256 _value) {
owner = msg.sender;
initialValue = _value;
}
}
1.2 构造函数特点 #
solidity
contract ConstructorFeatures {
address public owner;
string public name;
// 1. 只执行一次
constructor(string memory _name) {
owner = msg.sender;
name = _name;
}
// 2. 不能有返回值
// constructor() returns (uint256) {} // 错误
// 3. 可以有参数
// 4. 可以重载(但不推荐)
}
1.3 构造函数可见性 #
solidity
contract ConstructorVisibility {
address public owner;
// 构造函数默认是internal(如果省略可见性)
// 但推荐显式声明为public或省略
constructor() {
owner = msg.sender;
}
}
1.4 构造函数示例 #
solidity
contract Token {
string public name;
string public symbol;
uint8 public decimals;
uint256 public totalSupply;
address public owner;
mapping(address => uint256) public balanceOf;
// 带参数的构造函数
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals,
uint256 _totalSupply
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
totalSupply = _totalSupply;
owner = msg.sender;
balanceOf[msg.sender] = _totalSupply;
}
}
// 部署时传入参数
// new Token("MyToken", "MTK", 18, 1000000 * 10**18)
1.5 继承中的构造函数 #
solidity
contract Parent {
uint256 public value;
constructor(uint256 _value) {
value = _value;
}
}
contract Child is Parent {
string public name;
// 子合约需要调用父合约构造函数
constructor(uint256 _value, string memory _name) Parent(_value) {
name = _name;
}
}
contract AnotherChild is Parent {
constructor() Parent(100) {
// 调用父合约构造函数,传入固定值
}
}
二、接收函数 receive #
2.1 基本概念 #
receive函数在合约接收纯ETH转账时被调用。
solidity
contract ReceiveDemo {
event Received(address indexed from, uint256 amount);
// receive函数
receive() external payable {
emit Received(msg.sender, msg.value);
}
}
2.2 receive特点 #
solidity
contract ReceiveFeatures {
// 1. 必须是external payable
// 2. 不能有参数
// 3. 不能有返回值
// 4. 一个合约只能有一个receive函数
receive() external payable {
// 处理ETH接收
}
}
2.3 receive使用示例 #
solidity
contract Vault {
mapping(address => uint256) public balances;
event Deposit(address indexed from, uint256 amount);
// 通过receive接收ETH
receive() external payable {
balances[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}
// 查询余额
function getBalance() public view returns (uint256) {
return address(this).balance;
}
// 提取
function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
payable(msg.sender).transfer(amount);
}
}
2.4 receive触发条件 #
solidity
contract ReceiveTrigger {
event Received(address indexed from, uint256 amount);
receive() external payable {
emit Received(msg.sender, msg.value);
}
// receive在以下情况被调用:
// 1. 向合约发送ETH,不调用任何函数
// 2. 使用transfer()或send()发送ETH
// 3. 使用call{value: ...}("")发送空数据
}
contract TriggerExample {
ReceiveTrigger public target;
constructor(address _target) {
target = ReceiveTrigger(_target);
}
// 触发receive
function triggerReceive() public payable {
payable(address(target)).transfer(msg.value);
}
// 不触发receive(调用函数)
function notTriggerReceive() public payable {
// 调用特定函数,不触发receive
}
}
三、回退函数 fallback #
3.1 基本概念 #
fallback函数在调用不存在的函数或发送ETH但receive不存在时被调用。
solidity
contract FallbackDemo {
event FallbackCalled(address indexed from, uint256 amount, bytes data);
// fallback函数
fallback() external payable {
emit FallbackCalled(msg.sender, msg.value, msg.data);
}
}
3.2 fallback特点 #
solidity
contract FallbackFeatures {
// 1. 必须是external
// 2. 可以是payable(接收ETH)
// 3. 不能有参数
// 4. 不能有返回值
// 5. 一个合约只能有一个fallback函数
fallback() external payable {
// 处理未知调用
}
}
3.3 fallback使用示例 #
solidity
contract Proxy {
address public implementation;
event ProxyCall(address indexed sender, bytes data);
constructor(address _implementation) {
implementation = _implementation;
}
// 代理合约使用fallback转发调用
fallback() external payable {
address impl = implementation;
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
receive() external payable {
// 接收ETH
}
}
3.4 receive与fallback的区别 #
solidity
contract ReceiveVsFallback {
event ReceiveCalled(address indexed from, uint256 amount);
event FallbackCalled(address indexed from, uint256 amount, bytes data);
// receive:只处理纯ETH转账
receive() external payable {
emit ReceiveCalled(msg.sender, msg.value);
}
// fallback:处理其他情况
fallback() external payable {
emit FallbackCalled(msg.sender, msg.value, msg.data);
}
// 调用规则:
// 1. 发送ETH + 空数据 -> receive
// 2. 发送ETH + 非空数据 -> fallback
// 3. 调用不存在的函数 -> fallback
// 4. 如果没有receive,发送ETH -> fallback
}
3.5 调用流程图 #
text
调用合约
│
├── 调用存在的函数
│ └── 执行该函数
│
└── 调用不存在的函数或发送ETH
│
├── 发送ETH + 空数据
│ │
│ ├── 有receive函数 -> 执行receive
│ │
│ └── 无receive函数 -> 执行fallback
│
└── 发送ETH + 非空数据 或 不发送ETH
│
└── 执行fallback
四、综合示例 #
4.1 支付合约 #
solidity
contract PaymentContract {
address public owner;
mapping(address => uint256) public balances;
event Deposit(address indexed from, uint256 amount);
event Withdrawal(address indexed to, uint256 amount);
event FallbackTriggered(address indexed from, uint256 amount, bytes data);
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
// 显式存款函数
function deposit() public payable {
require(msg.value > 0, "Must send ETH");
balances[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}
// receive:接收纯ETH转账
receive() external payable {
balances[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}
// fallback:处理其他情况
fallback() external payable {
if (msg.value > 0) {
balances[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}
emit FallbackTriggered(msg.sender, msg.value, msg.data);
}
// 提款
function withdraw(uint256 amount) public {
require(balances[msg.sender] >= amount, "Insufficient balance");
balances[msg.sender] -= amount;
payable(msg.sender).transfer(amount);
emit Withdrawal(msg.sender, amount);
}
// 查询合约余额
function getContractBalance() public view returns (uint256) {
return address(this).balance;
}
// 紧急提取(仅owner)
function emergencyWithdraw() public onlyOwner {
uint256 balance = address(this).balance;
payable(owner).transfer(balance);
}
}
4.2 代理合约 #
solidity
contract ProxyContract {
address public implementation;
address public admin;
event Upgraded(address indexed implementation);
event AdminChanged(address indexed oldAdmin, address indexed newAdmin);
constructor(address _implementation) {
implementation = _implementation;
admin = msg.sender;
}
modifier onlyAdmin() {
require(msg.sender == admin, "Not admin");
_;
}
// 升级实现合约
function upgrade(address _newImplementation) public onlyAdmin {
implementation = _newImplementation;
emit Upgraded(_newImplementation);
}
// 更换管理员
function changeAdmin(address _newAdmin) public onlyAdmin {
address oldAdmin = admin;
admin = _newAdmin;
emit AdminChanged(oldAdmin, _newAdmin);
}
// fallback:转发所有调用到实现合约
fallback() external payable {
_delegate(implementation);
}
// receive:接收ETH
receive() external payable {
// 可以选择转发或直接接收
}
function _delegate(address _impl) internal {
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), _impl, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
}
4.3 多功能钱包 #
solidity
contract MultiFunctionWallet {
address public owner;
mapping(bytes4 => address) public methods;
event Deposit(address indexed from, uint256 amount);
event MethodRegistered(bytes4 indexed selector, address indexed implementation);
event MethodUnregistered(bytes4 indexed selector);
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
// 注册方法
function registerMethod(bytes4 selector, address implementation) public onlyOwner {
methods[selector] = implementation;
emit MethodRegistered(selector, implementation);
}
// 注销方法
function unregisterMethod(bytes4 selector) public onlyOwner {
delete methods[selector];
emit MethodUnregistered(selector);
}
// receive:接收ETH
receive() external payable {
emit Deposit(msg.sender, msg.value);
}
// fallback:动态方法分发
fallback() external payable {
address impl = methods[msg.sig];
require(impl != address(0), "Method not found");
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
}
五、最佳实践 #
5.1 构造函数最佳实践 #
solidity
contract ConstructorBestPractices {
address public owner;
uint256 public value;
// 推荐:参数使用下划线前缀
constructor(uint256 _value) {
owner = msg.sender;
value = _value;
// 推荐:在构造函数中进行验证
require(_value > 0, "Value must be positive");
}
// 推荐:使用modifier简化权限检查
modifier onlyOwner() {
require(msg.sender == owner, "Not owner");
_;
}
}
5.2 receive/fallback最佳实践 #
solidity
contract ReceiveFallbackBestPractices {
event Deposit(address indexed from, uint256 amount);
event FallbackCalled(address indexed from, bytes data);
// 推荐:receive只处理纯ETH转账
receive() external payable {
emit Deposit(msg.sender, msg.value);
}
// 推荐:fallback处理其他情况
fallback() external payable {
// 记录调用信息
emit FallbackCalled(msg.sender, msg.data);
// 如果发送了ETH,记录
if (msg.value > 0) {
emit Deposit(msg.sender, msg.value);
}
}
}
5.3 安全考虑 #
solidity
contract SecurityConsiderations {
address public owner;
constructor() {
owner = msg.sender;
}
// 注意:receive和fallback可以接收ETH
// 确保合约逻辑正确处理ETH
receive() external payable {
// 可以添加限制
require(msg.value <= 10 ether, "Amount too large");
}
// 注意:fallback可能被意外触发
fallback() external payable {
// 记录所有调用
// emit FallbackCalled(msg.sender, msg.value, msg.data);
}
}
六、总结 #
特殊函数要点:
| 函数 | 说明 | 触发条件 |
|---|---|---|
| constructor | 初始化合约 | 部署时执行一次 |
| receive | 接收ETH | 纯ETH转账 |
| fallback | 回退处理 | 调用不存在函数或发送ETH+数据 |
使用建议:
- constructor用于初始化状态
- receive处理纯ETH转账
- fallback处理未知调用
- 注意安全考虑
接下来,让我们学习合约基础!
最后更新:2026-03-27