Solidity全局变量与安全实践指南
1. 区块与交易属性
function getBlockData() public view returns (uint number, uint timestamp, uint gasLimit,address miner
) {return (block.number, // 当前区块号block.timestamp, // 区块时间戳(Unix秒)block.gaslimit, // 当前区块的gas上限block.coinbase // 矿工地址);
}function getTxData() public view returns (uint gasPrice,address origin
) {return (tx.gasprice, // 当前交易的gas价格tx.origin // 原始交易发送者(慎用!));
}
2. 消息属性 (msg)
function processPayment() public payable {// 获取调用信息address sender = msg.sender;uint value = msg.value;bytes4 selector = msg.sig;// 验证至少转账1 ETHrequire(value >= 1 ether, "Minimum 1 ETH required");// 记录交易payments[sender] += value;
}
3. 地址操作
// 安全ETH转账
function sendEther(address payable to, uint amount) public {// 检查合约余额require(address(this).balance >= amount, "Insufficient balance");// 使用call代替transfer/send (推荐)(bool success, ) = to.call{value: amount}("");require(success, "Transfer failed");
}// 调用其他合约
function callExternalContract(address contractAddr, string memory funcSig,uint param
) public {bytes memory data = abi.encodeWithSignature(funcSig, param);(bool success, bytes memory result) = contractAddr.call(data);require(success, "External call failed");// 处理返回结果...
}// 检查合约余额
function checkBalance(address addr) public view returns (uint) {return addr.balance; // 地址的ETH余额(wei)
}
4. ABI编码/解码
// 创建函数调用数据
function createTransferData(address recipient, uint amount
) public pure returns (bytes memory) {return abi.encodeWithSignature("transfer(address,uint256)", recipient, amount);
}// 解析数据
function decodeData(bytes memory data
) public pure returns (address, uint) {(address recipient, uint amount) = abi.decode(abi.decode(data[4:]), // 跳过函数选择器(address, uint));return (recipient, amount);
}
5. 数学与加密
// 创建唯一标识符
function createTokenId(string memory name, address owner
) public pure returns (bytes32) {return keccak256(abi.encodePacked(name, owner));
}// 签名验证
function verify(bytes32 messageHash,uint8 v,bytes32 r,bytes32 s
) public pure returns (address) {return ecrecover(messageHash, v, r, s);
}// 安全数学运算
function safeModAdd(uint a,uint b,uint mod
) public pure returns (uint) {return addmod(a, b, mod);
}
6. 错误处理
function withdraw(uint amount) public {// 检查条件require(balances[msg.sender] >= amount, "Insufficient balance");// 更新状态balances[msg.sender] -= amount;// 执行转账(bool success, ) = msg.sender.call{value: amount}("");// 自定义错误处理if (!success) {revert WithdrawFailed(msg.sender, amount);}
}// 自定义错误类型
error WithdrawFailed(address sender, uint amount);// 内部一致性检查
function validateState() public view {assert(totalSupply == calculateTotalSupply());
}
7. 合约操作
// 获取合约信息
function getContractInfo() public view returns (address contractAddress,uint balance
) {return (address(this), // 当前合约地址address(this).balance // 合约ETH余额);
}// 安全销毁合约
function destroyContract() public onlyOwner {// 保存所有者地址address owner = owner;// 清除状态delete owner;// 销毁合约并发送余额selfdestruct(payable(owner));
}
8. Gas 操作
function optimizeGas() public {uint startGas = gasleft();// 执行复杂操作...for(uint i = 0; i < 100; i++) {// 某些操作}uint gasUsed = startGas - gasleft();emit GasConsumed(gasUsed);
}
9. 类型信息
// 获取接口ID (ERC-165)
function getERC721InterfaceId() public pure returns (bytes4) {return type(IERC721).interfaceId;
}// 检查合约支持的接口
function supportsInterface(address contractAddr,bytes4 interfaceId
) public view returns (bool) {(bool success, bytes memory result) = contractAddr.staticcall(abi.encodeWithSignature("supportsInterface(bytes4)", interfaceId));return success && abi.decode(result, (bool));
}
关键实践建议
- 安全转账模式:
// 安全转账模板
function safeTransferETH(address to, uint value) internal {(bool success, ) = to.call{value: value}(new bytes(0));require(success, "ETH transfer failed");
}
- call vs delegatecall:
// 普通调用:在目标合约上下文中执行
contractA.call(abi.encodeWithSignature("func()"));// 委托调用:在当前合约上下文中执行目标合约代码
contractB.delegatecall(abi.encodeWithSignature("func()"));
- 时间戳使用警告:
// 不安全的随机数生成(容易被矿工操纵)
uint badRandom = uint(keccak256(abi.encodePacked(block.timestamp)));// 更安全的方案(但仍不完全安全)
uint betterRandom = uint(keccak256(abi.encodePacked(block.timestamp, block.difficulty,msg.sender
)));
- ABI编码最佳实践:
// 安全:明确指定类型防止哈希碰撞
bytes32 safeHash = keccak256(abi.encode("Transfer(address,uint256)",recipient,amount
));// 危险:紧密打包可能导致不同类型数据碰撞
bytes32 unsafeHash = keccak256(abi.encodePacked("Transfer",recipient,amount
));
这些全局属性和方法是 Solidity 智能合约开发的基石,正确理解和使用它们对于编写安全、高效的合约至关重要。在实际开发中,始终优先考虑安全性,特别是处理资金转账和外部调用时。