以太坊智能合约开发完整指南:从Solidity到安全审计 Solidity基础 Hello World 数据类型 值类型 引用类型 函数修饰符 高级特性 事件日志 错误处理 继承与接口 ERC标准 ERC20代币 ERC721 NFT 安全最佳实践 重入攻击防护 整数溢出防护 Solidity 0.8+内置检查,早期版本需要: 访问控制 Gas优化 存储优化 循环优化 批量操作 测试 Hardhat测试框架 部署 Remix IDE 最简单的在线开发环境,适合初学者。
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract HelloWorld { string public greeting = "Hello, World!"; function setGreeting(string memory _greeting) public { greeting = _greeting; } function sayHello() public view returns (string memory) { return greeting; } }
值类型
bool public isActive = true; uint256 public count = 100; int256 public temperature = -5; address public owner = msg.sender; bytes32 public hash = keccak256("test");
引用类型
// 数组 uint256[] public numbers; string[] public names; // 映射 mapping(address => uint256) public balances; // 结构体 struct User { string name; uint256 age; }
contract AccessControl { address public owner; constructor() { owner = msg.sender; } modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; } function sensitiveFunction() public onlyOwner { // 只有owner可以调用 } }
contract Token { event Transfer(address indexed from, address indexed to, uint256 value); function transfer(address to, uint256 amount) public { _transfer(msg.sender, to, amount); emit Transfer(msg.sender, to, amount); } }
contract ErrorHandling { error InsufficientBalance(uint256 requested, uint256 available); function withdraw(uint256 amount) public { uint256 balance = balances[msg.sender]; if (amount > balance) { revert InsufficientBalance(amount, balance); } balances[msg.sender] -= amount; payable(msg.sender).transfer(amount); } }
interface IERC20 { function transfer(address to, uint256 amount) external returns (bool); } contract MyToken is IERC20 { function transfer(address to, uint256 amount) public override returns (bool) { // 实现 return true; } }
contract MyToken is IERC20 { string public name = "My Token"; string public symbol = "MTK"; uint8 public decimals = 18; uint256 public override totalSupply; mapping(address => uint256) public override balanceOf; mapping(address => mapping(address => uint256)) public override allowance; function transfer(address to, uint256 amount) public override returns (bool) { require(balanceOf[msg.sender] >= amount, "Insufficient balance"); balanceOf[msg.sender] -= amount; balanceOf[to] += amount; emit Transfer(msg.sender, to, amount); return true; } function approve(address spender, uint256 amount) public override returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transferFrom(address from, address to, uint256 amount) public override returns (bool) { require(allowance[from][msg.sender] >= amount, "Allowance exceeded"); allowance[from][msg.sender] -= amount; balanceOf[from] -= amount; balanceOf[to] += amount; emit Transfer(from, to, amount); return true; } }
import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract MyNFT is ERC721, Ownable { using Counters for Counters.Counter; Counters.Counter private _tokenIdCounter; constructor() ERC721("MyNFT", "NFT") {} function mintNFT(address recipient, string memory tokenURI) public onlyOwner returns (uint256) { _tokenIdCounter.increment(); uint256 newItemId = _tokenIdCounter.current(); _mint(recipient, newItemId); _setTokenURI(newItemId, tokenURI); return newItemId; } }
contract SecureContract { mapping(address => uint256) public balances; bool private locked; modifier noReentrant() { require(!locked, "Reentrant call"); locked = true; _; locked = false; } function withdraw() public noReentrant { uint256 amount = balances[msg.sender]; require(amount > 0, "No balance"); balances[msg.sender] = 0; (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed"); } }
Solidity 0.8+内置检查,早期版本需要:
import "@openzeppelin/contracts/utils/math/SafeMath.sol"; contract MathContract { using SafeMath for uint256; function add(uint256 a, uint256 b) public pure returns (uint256) { return a.add(b); // 自动检查溢出 } }
import "@openzeppelin/contracts/access/AccessControl.sol"; contract MyContract is AccessControl { bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); bytes32 public constant USER_ROLE = keccak256("USER_ROLE"); constructor() { _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); _grantRole(ADMIN_ROLE, msg.sender); } function adminFunction() public onlyRole(ADMIN_ROLE) { // 只有管理员可以调用 } }
contract GasOptimized { // 使用更小的类型 uint8 public smallNumber; // 而非uint256 bool public flag; address public owner; // 打包相关变量 struct PackedData { uint128 a; uint64 b; uint32 c; uint32 d; } }
contract LoopOptimization { uint256[] public numbers; // ❌ 低效 function sumBad() public view returns (uint256) { uint256 total; for (uint256 i = 0; i < numbers.length; i++) { total += numbers[i]; } return total; } // ✅ 高效 function sumGood() public view returns (uint256) { uint256 total; uint256 length = numbers.length; for (uint256 i = 0; i < length; i++) { total += numbers[i]; } return total; } }
contract BatchOperations { function batchTransfer(address[] memory recipients, uint256[] memory amounts) public { require(recipients.length == amounts.length, "Length mismatch"); for (uint256 i = 0; i < recipients.length; i++) { transfer(recipients[i], amounts[i]); } } }
const { expect } = require("chai"); describe("Token Contract", function () { it("Should transfer tokens between accounts", async function () { const [owner, addr1] = await ethers.getSigners(); const Token = await ethers.getContractFactory("Token"); const token = await Token.deploy(); await token.transfer(addr1.address, 50); expect(await token.balanceOf(addr1.address)).to.equal(50); }); });
最简单的在线开发环境,适合初学者。
module.exports = { networks: { mainnet: { url: process.env.MAINNET_RPC_URL, accounts: [process.env.PRIVATE_KEY] }, goerli: { url: process.env.GOERLI_RPC_URL, accounts: [process.env.PRIVATE_KEY] } } };
npx hardhat verify --network mainnet DEPLOYED_ADDRESS "Constructor Arg 1" "Constructor Arg 2"