目录
-
- 摘要
- [1. 引言:为什么需要区块链?](#1. 引言:为什么需要区块链?)
-
- [1.1 区块链的核心价值](#1.1 区块链的核心价值)
- [1.2 区块链应用场景](#1.2 区块链应用场景)
- [2. 智能合约:区块链的可编程核心](#2. 智能合约:区块链的可编程核心)
-
- [2.1 什么是智能合约?](#2.1 什么是智能合约?)
- [2.2 智能合约的工作原理](#2.2 智能合约的工作原理)
- [2.3 以太坊虚拟机(EVM)](#2.3 以太坊虚拟机(EVM))
- [3. Solidity智能合约开发](#3. Solidity智能合约开发)
-
- [3.1 Solidity语言介绍](#3.1 Solidity语言介绍)
- [3.2 Solidity发展历程](#3.2 Solidity发展历程)
- [3.3 第一个智能合约](#3.3 第一个智能合约)
- [3.4 ERC20代币合约](#3.4 ERC20代币合约)
- [4. 去中心化应用(DApp)架构](#4. 去中心化应用(DApp)架构)
-
- [4.1 DApp与传统应用对比](#4.1 DApp与传统应用对比)
- [4.2 DApp技术栈](#4.2 DApp技术栈)
- [4.3 DApp开发流程](#4.3 DApp开发流程)
- [5. Web3.py:Python与以太坊交互](#5. Web3.py:Python与以太坊交互)
-
- [5.1 Web3.py介绍](#5.1 Web3.py介绍)
- [5.2 安装与配置](#5.2 安装与配置)
- [5.3 连接以太坊节点](#5.3 连接以太坊节点)
- [5.4 账户管理](#5.4 账户管理)
- [5.5 查询区块链数据](#5.5 查询区块链数据)
- [6. 智能合约部署与交互](#6. 智能合约部署与交互)
-
- [6.1 编译智能合约](#6.1 编译智能合约)
- [6.2 部署智能合约](#6.2 部署智能合约)
- [6.3 调用智能合约](#6.3 调用智能合约)
- [6.4 完整的DApp后端示例](#6.4 完整的DApp后端示例)
- [7. DApp前端集成](#7. DApp前端集成)
-
- [7.1 前端架构设计](#7.1 前端架构设计)
- [7.2 连接MetaMask钱包](#7.2 连接MetaMask钱包)
- [7.3 前端调用智能合约](#7.3 前端调用智能合约)
- [8. 安全最佳实践](#8. 安全最佳实践)
-
- [8.1 智能合约安全清单](#8.1 智能合约安全清单)
- [8.2 常见漏洞与防护](#8.2 常见漏洞与防护)
- [8.3 安全代码示例](#8.3 安全代码示例)
- [9. 测试与部署](#9. 测试与部署)
-
- [9.1 单元测试](#9.1 单元测试)
- [9.2 部署脚本](#9.2 部署脚本)
- [10. 总结](#10. 总结)
- 参考资料
摘要
区块链技术正在重塑互联网的信任机制。本文从智能合约基础概念出发,深入讲解以太坊开发的核心技术,并以Web3.py为工具,手把手带你完成从智能合约编写到去中心化应用(DApp)开发的完整流程。通过本文,你将掌握Solidity智能合约开发、Web3.py与以太坊交互、DApp前端集成等核心技能。无论你是区块链新手还是希望系统学习DApp开发的工程师,本文都将为你打开Web3世界的大门。
1. 引言:为什么需要区块链?
传统互联网应用依赖中心化服务器,数据由单一机构控制,存在单点故障、数据篡改、隐私泄露等风险。区块链通过分布式账本和共识机制,实现了去中心化的信任体系。
1.1 区块链的核心价值
区块链系统
用户
节点1
节点2
节点3
分布式账本
优势: 去中心化、不可篡改
传统中心化系统
用户
中心服务器
数据库
风险: 单点故障
1.2 区块链应用场景
| 应用领域 | 典型案例 | 核心价值 |
|---|---|---|
| DeFi | Uniswap、Aave | 去中心化金融,无需中介 |
| NFT | OpenSea、无聊猿 | 数字资产确权 |
| DAO | MakerDAO | 去中心化治理 |
| 供应链 | VeChain | 溯源防伪 |
| 游戏 | Axie Infinity | Play-to-Earn |
2. 智能合约:区块链的可编程核心
2.1 什么是智能合约?
智能合约是部署在区块链上的可执行代码,具有以下特点:
- 自动执行:满足条件时自动触发,无需人工干预
- 不可篡改:一旦部署,代码无法修改
- 透明公开:所有代码和交易记录公开可查
- 去信任化:代码即法律,无需信任第三方
2.2 智能合约的工作原理
智能合约 区块链 钱包 用户 智能合约 区块链 钱包 用户 发起交易 广播交易 验证签名 执行合约代码 更新状态 打包区块 返回交易回执 显示结果
2.3 以太坊虚拟机(EVM)
以太坊虚拟机是智能合约的运行环境:
| 特性 | 说明 |
|---|---|
| 图灵完备 | 支持循环、条件等复杂逻辑 |
| 沙箱隔离 | 合约之间相互隔离 |
| Gas机制 | 防止无限循环,计算资源有偿使用 |
| 确定性执行 | 相同输入必然产生相同输出 |
3. Solidity智能合约开发
3.1 Solidity语言介绍
Solidity是以太坊最主流的智能合约编程语言:
- 语法类似JavaScript:降低学习门槛
- 静态类型:编译时类型检查
- 面向对象:支持继承、多态
- 安全特性:内置溢出检查
3.2 Solidity发展历程
| 年份 | 版本 | 重要特性 |
|---|---|---|
| 2014 | 0.1.x | 首次发布 |
| 2017 | 0.4.x | ERC20标准确立 |
| 2018 | 0.5.x | 语法重大更新 |
| 2020 | 0.6.x | try/catch异常处理 |
| 2021 | 0.8.x | 内置溢出检查 |
| 2023 | 0.8.20 | PUSH0指令优化 |
3.3 第一个智能合约
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title SimpleStorage
* @dev 一个简单的存储合约,演示Solidity基础语法
* @author 区块链开发者
*/
contract SimpleStorage {
// 状态变量:存储在区块链上
uint256 private storedData;
// 事件:用于日志记录
event ValueChanged(uint256 oldValue, uint256 newValue);
// 错误:自定义错误类型(Gas优化)
error ValueNotChanged(uint256 currentValue);
/**
* @dev 存储新值
* @param newValue 要存储的新值
*/
function set(uint256 newValue) public {
// 检查新值是否与当前值相同
if (storedData == newValue) {
revert ValueNotChanged(storedData);
}
uint256 oldValue = storedData;
storedData = newValue;
// 触发事件
emit ValueChanged(oldValue, newValue);
}
/**
* @dev 获取当前存储的值
* @return 当前存储的值
*/
function get() public view returns (uint256) {
return storedData;
}
}
代码解释:
pragma solidity ^0.8.20指定编译器版本uint256 private storedData声明一个私有状态变量event ValueChanged定义事件,用于记录状态变化error ValueNotChanged定义自定义错误,节省Gasfunction set是一个公开函数,可被外部调用emit关键字触发事件,记录到区块链日志
3.4 ERC20代币合约
ERC20是以太坊最广泛使用的代币标准:
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @title MyToken
* @dev 实现ERC20标准的代币合约
*/
contract MyToken {
// 代币基本信息
string public name;
string public symbol;
uint8 public decimals;
uint256 public totalSupply;
// 余额映射
mapping(address => uint256) public balanceOf;
// 授权额度映射
mapping(address => mapping(address => uint256)) public allowance;
// ERC20事件
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev 构造函数:初始化代币
* @param _name 代币名称
* @param _symbol 代币符号
* @param _initialSupply 初始供应量
*/
constructor(
string memory _name,
string memory _symbol,
uint256 _initialSupply
) {
name = _name;
symbol = _symbol;
decimals = 18; // 标准精度
// 将初始代币分配给部署者
totalSupply = _initialSupply * 10 ** uint256(decimals);
balanceOf[msg.sender] = totalSupply;
emit Transfer(address(0), msg.sender, totalSupply);
}
/**
* @dev 转账
* @param to 接收地址
* @param value 转账金额
*/
function transfer(address to, uint256 value) public returns (bool success) {
require(balanceOf[msg.sender] >= value, "Insufficient balance");
require(to != address(0), "Invalid recipient");
balanceOf[msg.sender] -= value;
balanceOf[to] += value;
emit Transfer(msg.sender, to, value);
return true;
}
/**
* @dev 授权第三方转账
* @param spender 被授权地址
* @param value 授权金额
*/
function approve(address spender, uint256 value) public returns (bool success) {
allowance[msg.sender][spender] = value;
emit Approval(msg.sender, spender, value);
return true;
}
/**
* @dev 授权转账
* @param from 发送地址
* @param to 接收地址
* @param value 转账金额
*/
function transferFrom(
address from,
address to,
uint256 value
) public returns (bool success) {
require(value <= balanceOf[from], "Insufficient balance");
require(value <= allowance[from][msg.sender], "Insufficient allowance");
require(to != address(0), "Invalid recipient");
balanceOf[from] -= value;
balanceOf[to] += value;
allowance[from][msg.sender] -= value;
emit Transfer(from, to, value);
return true;
}
}
这个合约实现了ERC20标准的核心功能:
- transfer:直接转账
- approve:授权第三方使用代币
- transferFrom:授权转账(DeFi协议常用)
4. 去中心化应用(DApp)架构
4.1 DApp与传统应用对比
DApp应用
前端
Web3.js/Web3.py
智能合约
区块链
IPFS/去中心化存储
传统Web应用
前端
后端API
数据库
中心化风险
4.2 DApp技术栈
| 层级 | 技术 | 说明 |
|---|---|---|
| 前端 | React/Vue + ethers.js | 用户界面 |
| 中间件 | Web3.py / Web3.js | 区块链交互 |
| 智能合约 | Solidity | 业务逻辑 |
| 区块链 | Ethereum / Layer2 | 执行环境 |
| 存储 | IPFS / Arweave | 去中心化存储 |
| 索引 | The Graph | 数据查询 |
4.3 DApp开发流程
需求分析
合约设计
合约开发
合约测试
合约部署
前端开发
集成测试
上线发布
5. Web3.py:Python与以太坊交互
5.1 Web3.py介绍
Web3.py是Python生态中最流行的以太坊交互库:
- 完整的JSON-RPC支持:覆盖所有以太坊API
- 智能合约交互:编译、部署、调用
- 账户管理:签名、交易构建
- 类型安全:支持类型提示
5.2 安装与配置
bash
# 创建虚拟环境
python -m venv web3-env
source web3-env/bin/activate
# 安装Web3.py
pip install web3
# 安装开发工具
pip install eth-account py-solc-x
5.3 连接以太坊节点
python
from web3 import Web3
from web3.providers.eth_tester import EthereumTesterProvider
# 方式1: 连接本地节点(Ganache/Anvil)
w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))
# 方式2: 连接Infura节点
INFURA_URL = "https://mainnet.infura.io/v3/YOUR_PROJECT_ID"
w3 = Web3(Web3.HTTPProvider(INFURA_URL))
# 方式3: 使用测试提供者(开发测试)
w3 = Web3(EthereumTesterProvider())
# 检查连接状态
if w3.is_connected():
print("✅ 已连接到以太坊节点")
print(f"当前区块高度: {w3.eth.block_number}")
print(f"链ID: {w3.eth.chain_id}")
else:
print("❌ 连接失败")
代码解释:
HTTPProvider用于连接HTTP RPC节点is_connected()检查节点连接状态eth.block_number获取当前区块高度- Infura是以太坊主流的节点服务提供商
5.4 账户管理
python
from web3 import Web3
from eth_account import Account
import secrets
# 初始化Web3
w3 = Web3()
# 生成新账户
def create_account():
"""生成新的以太坊账户"""
private_key = "0x" + secrets.token_hex(32)
account = Account.from_key(private_key)
return {
"address": account.address,
"private_key": private_key
}
# 从私钥恢复账户
def restore_account(private_key):
"""从私钥恢复账户"""
account = Account.from_key(private_key)
return account.address
# 签名消息
def sign_message(private_key, message):
"""签名消息"""
account = Account.from_key(private_key)
message_hash = w3.keccak(text=message)
signed = account.sign_message(message_hash)
return signed.signature.hex()
# 示例
print("=" * 50)
print("账户管理示例")
print("=" * 50)
# 创建新账户
new_account = create_account()
print(f"\n新账户地址: {new_account['address']}")
print(f"私钥: {new_account['private_key']}")
# 签名消息
message = "Hello, Blockchain!"
signature = sign_message(new_account['private_key'], message)
print(f"\n消息签名: {signature}")
5.5 查询区块链数据
python
from web3 import Web3
# 连接到以太坊主网(使用公共节点)
w3 = Web3(Web3.HTTPProvider('https://eth.llamarpc.com'))
def query_blockchain():
"""查询区块链基本信息"""
print("=" * 50)
print("区块链信息查询")
print("=" * 50)
# 最新区块信息
latest_block = w3.eth.get_block('latest')
print(f"\n最新区块:")
print(f" 区块号: {latest_block['number']}")
print(f" 时间戳: {latest_block['timestamp']}")
print(f" 交易数: {len(latest_block['transactions'])}")
print(f" Gas限制: {latest_block['gasLimit']}")
print(f" Gas使用: {latest_block['gasUsed']}")
# 查询账户余额
vitalik_address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" # Vitalik的地址
balance_wei = w3.eth.get_balance(vitalik_address)
balance_eth = w3.from_wei(balance_wei, 'ether')
print(f"\nVitalik账户余额:")
print(f" {balance_eth:.4f} ETH")
# 查询交易
# 使用一笔历史交易作为示例
tx_hash = "0x5c504ed432cb51138bcf09aa5e8a410dd4a1e204ef84bfed1be16dfba1b22060" # 第一笔以太坊交易
tx = w3.eth.get_transaction(tx_hash)
print(f"\n历史交易信息:")
print(f" 发送者: {tx['from']}")
print(f" 接收者: {tx['to']}")
print(f" 金额: {w3.from_wei(tx['value'], 'ether')} ETH")
print(f" Gas价格: {w3.from_wei(tx['gasPrice'], 'gwei')} Gwei")
if w3.is_connected():
query_blockchain()
else:
print("无法连接到以太坊节点")
代码解释:
get_block('latest')获取最新区块信息get_balance(address)查询账户余额(单位为Wei)from_wei()将Wei转换为ETH(1 ETH = 10^18 Wei)get_transaction(hash)根据交易哈希查询交易详情
6. 智能合约部署与交互
6.1 编译智能合约
python
from solcx import compile_source, install_solc
import json
# 安装指定版本的Solidity编译器
install_solc('0.8.20')
# Solidity源码
CONTRACT_SOURCE = """
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract SimpleStorage {
uint256 private storedData;
event ValueChanged(uint256 oldValue, uint256 newValue);
function set(uint256 newValue) public {
uint256 oldValue = storedData;
storedData = newValue;
emit ValueChanged(oldValue, newValue);
}
function get() public view returns (uint256) {
return storedData;
}
}
"""
def compile_contract():
"""编译Solidity合约"""
compiled = compile_source(
CONTRACT_SOURCE,
output_values=["abi", "bin"]
)
# 获取合约接口
contract_id, contract_interface = next(iter(compiled.items()))
return {
"abi": contract_interface["abi"],
"bytecode": contract_interface["bin"]
}
# 编译合约
compiled_contract = compile_contract()
print("合约编译成功!")
print(f"ABI: {json.dumps(compiled_contract['abi'], indent=2)}")
6.2 部署智能合约
python
from web3 import Web3
from eth_account import Account
from solcx import compile_source, install_solc
import time
# 安装编译器
install_solc('0.8.20')
# 连接到本地测试链(如Ganache)
w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))
# 测试账户私钥(Ganache默认账户)
PRIVATE_KEY = "0x你的私钥" # 替换为实际私钥
account = Account.from_key(PRIVATE_KEY)
# 合约源码
CONTRACT_SOURCE = """
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract SimpleStorage {
uint256 private storedData;
event ValueChanged(uint256 oldValue, uint256 newValue);
function set(uint256 newValue) public {
uint256 oldValue = storedData;
storedData = newValue;
emit ValueChanged(oldValue, newValue);
}
function get() public view returns (uint256) {
return storedData;
}
}
"""
def deploy_contract():
"""部署智能合约"""
print("=" * 50)
print("部署智能合约")
print("=" * 50)
# 编译合约
compiled = compile_source(
CONTRACT_SOURCE,
output_values=["abi", "bin"]
)
_, contract_interface = next(iter(compiled.items()))
# 创建合约对象
SimpleStorage = w3.eth.contract(
abi=contract_interface["abi"],
bytecode=contract_interface["bin"]
)
# 构建部署交易
nonce = w3.eth.get_transaction_count(account.address)
transaction = SimpleStorage.constructor().build_transaction({
"from": account.address,
"nonce": nonce,
"gas": 2000000,
"gasPrice": w3.eth.gas_price
})
# 签名交易
signed_txn = account.sign_transaction(transaction)
# 发送交易
tx_hash = w3.eth.send_raw_transaction(signed_txn.raw_transaction)
print(f"交易哈希: {tx_hash.hex()}")
# 等待确认
print("等待交易确认...")
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"\n✅ 合约部署成功!")
print(f"合约地址: {receipt.contractAddress}")
print(f"Gas使用: {receipt.gasUsed}")
return receipt.contractAddress, contract_interface["abi"]
# 执行部署
if w3.is_connected():
contract_address, abi = deploy_contract()
else:
print("请确保本地测试链正在运行")
代码解释:
compile_source()编译Solidity源码,获取ABI和字节码w3.eth.contract()创建合约对象constructor().build_transaction()构建部署交易sign_transaction()使用私钥签名交易send_raw_transaction()发送已签名的交易wait_for_transaction_receipt()等待交易确认
6.3 调用智能合约
python
from web3 import Web3
from eth_account import Account
import json
# 连接
w3 = Web3(Web3.HTTPProvider('http://127.0.0.1:8545'))
PRIVATE_KEY = "0x你的私钥"
account = Account.from_key(PRIVATE_KEY)
# 合约信息(部署后获得)
CONTRACT_ADDRESS = "0x合约地址" # 替换为实际地址
CONTRACT_ABI = [...] # 替换为实际ABI
def interact_with_contract():
"""与智能合约交互"""
print("=" * 50)
print("智能合约交互")
print("=" * 50)
# 创建合约实例
contract = w3.eth.contract(
address=CONTRACT_ADDRESS,
abi=CONTRACT_ABI
)
# 读取数据(call - 不消耗Gas)
current_value = contract.functions.get().call()
print(f"\n当前存储值: {current_value}")
# 写入数据(transact - 消耗Gas)
new_value = 42
nonce = w3.eth.get_transaction_count(account.address)
# 构建交易
transaction = contract.functions.set(new_value).build_transaction({
"from": account.address,
"nonce": nonce,
"gas": 100000,
"gasPrice": w3.eth.gas_price
})
# 签名并发送
signed_txn = account.sign_transaction(transaction)
tx_hash = w3.eth.send_raw_transaction(signed_txn.raw_transaction)
print(f"\n设置新值: {new_value}")
print(f"交易哈希: {tx_hash.hex()}")
# 等待确认
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"Gas使用: {receipt.gasUsed}")
# 验证结果
updated_value = contract.functions.get().call()
print(f"\n更新后的值: {updated_value}")
# 监听事件
print("\n监听ValueChanged事件:")
event_filter = contract.events.ValueChanged.create_filter(from_block='latest')
for event in event_filter.get_all_entries():
print(f" 旧值: {event['args']['oldValue']}")
print(f" 新值: {event['args']['newValue']}")
# 执行交互
if w3.is_connected():
interact_with_contract()
6.4 完整的DApp后端示例
python
from web3 import Web3
from eth_account import Account
from solcx import compile_source, install_solc
import json
import os
class DAppBackend:
"""DApp后端服务类"""
def __init__(self, rpc_url, private_key):
"""初始化"""
self.w3 = Web3(Web3.HTTPProvider(rpc_url))
self.account = Account.from_key(private_key)
install_solc('0.8.20')
# 存储已部署的合约
self.contracts = {}
def is_connected(self):
"""检查连接状态"""
return self.w3.is_connected()
def deploy_token(self, name, symbol, initial_supply):
"""部署ERC20代币合约"""
token_source = """
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract SimpleToken {
string public name;
string public symbol;
uint8 public decimals = 18;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
constructor(string memory _name, string memory _symbol, uint256 _supply) {
name = _name;
symbol = _symbol;
totalSupply = _supply * 10 ** uint256(decimals);
balanceOf[msg.sender] = totalSupply;
emit Transfer(address(0), msg.sender, totalSupply);
}
function transfer(address to, uint256 value) public returns (bool) {
require(balanceOf[msg.sender] >= value, "Insufficient balance");
balanceOf[msg.sender] -= value;
balanceOf[to] += value;
emit Transfer(msg.sender, to, value);
return true;
}
function approve(address spender, uint256 value) public returns (bool) {
allowance[msg.sender][spender] = value;
emit Approval(msg.sender, spender, value);
return true;
}
function transferFrom(address from, address to, uint256 value) public returns (bool) {
require(value <= balanceOf[from], "Insufficient balance");
require(value <= allowance[from][msg.sender], "Insufficient allowance");
balanceOf[from] -= value;
balanceOf[to] += value;
allowance[from][msg.sender] -= value;
emit Transfer(from, to, value);
return true;
}
}
"""
# 编译
compiled = compile_source(token_source, output_values=["abi", "bin"])
_, interface = next(iter(compiled.items()))
# 创建合约
Token = self.w3.eth.contract(
abi=interface["abi"],
bytecode=interface["bin"]
)
# 构建交易
nonce = self.w3.eth.get_transaction_count(self.account.address)
tx = Token.constructor(name, symbol, initial_supply).build_transaction({
"from": self.account.address,
"nonce": nonce,
"gas": 3000000,
"gasPrice": self.w3.eth.gas_price
})
# 签名并发送
signed = self.account.sign_transaction(tx)
tx_hash = self.w3.eth.send_raw_transaction(signed.raw_transaction)
receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash)
# 存储合约信息
self.contracts[symbol] = {
"address": receipt.contractAddress,
"abi": interface["abi"]
}
return receipt.contractAddress
def get_balance(self, token_symbol, address):
"""查询代币余额"""
if token_symbol not in self.contracts:
raise ValueError(f"Token {token_symbol} not deployed")
contract = self.w3.eth.contract(
address=self.contracts[token_symbol]["address"],
abi=self.contracts[token_symbol]["abi"]
)
balance = contract.functions.balanceOf(address).call()
return self.w3.from_wei(balance, 'ether')
def transfer_token(self, token_symbol, to_address, amount):
"""转账代币"""
if token_symbol not in self.contracts:
raise ValueError(f"Token {token_symbol} not deployed")
contract = self.w3.eth.contract(
address=self.contracts[token_symbol]["address"],
abi=self.contracts[token_symbol]["abi"]
)
amount_wei = self.w3.to_wei(amount, 'ether')
nonce = self.w3.eth.get_transaction_count(self.account.address)
tx = contract.functions.transfer(to_address, amount_wei).build_transaction({
"from": self.account.address,
"nonce": nonce,
"gas": 100000,
"gasPrice": self.w3.eth.gas_price
})
signed = self.account.sign_transaction(tx)
tx_hash = self.w3.eth.send_raw_transaction(signed.raw_transaction)
receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash)
return tx_hash.hex(), receipt.gasUsed
# 使用示例
if __name__ == "__main__":
# 配置
RPC_URL = "http://127.0.0.1:8545"
PRIVATE_KEY = "0x你的私钥"
# 创建DApp后端
dapp = DAppBackend(RPC_URL, PRIVATE_KEY)
if dapp.is_connected():
print("✅ 已连接到区块链")
# 部署代币
token_address = dapp.deploy_token("MyToken", "MTK", 1000000)
print(f"✅ 代币已部署: {token_address}")
# 查询余额
balance = dapp.get_balance("MTK", dapp.account.address)
print(f"✅ 代币余额: {balance} MTK")
else:
print("❌ 连接失败")
7. DApp前端集成
7.1 前端架构设计
区块链层
前端应用
React/Vue组件
状态管理
Redux/Pinia
Web3 Provider
MetaMask钱包
智能合约
以太坊节点
7.2 连接MetaMask钱包
javascript
// 连接MetaMask钱包的JavaScript代码
// 可集成到React/Vue前端
async function connectWallet() {
// 检查MetaMask是否安装
if (typeof window.ethereum === 'undefined') {
alert('请安装MetaMask钱包!');
return null;
}
try {
// 请求连接
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
console.log('已连接账户:', accounts[0]);
return accounts[0];
} catch (error) {
console.error('连接失败:', error);
return null;
}
}
// 监听账户变化
window.ethereum.on('accountsChanged', (accounts) => {
console.log('账户已切换:', accounts[0]);
// 更新UI
});
// 监听链变化
window.ethereum.on('chainChanged', (chainId) => {
console.log('链已切换:', chainId);
// 刷新页面
window.location.reload();
});
7.3 前端调用智能合约
javascript
// 使用ethers.js调用智能合约
import { ethers } from 'ethers';
// 合约ABI(从编译结果获取)
const CONTRACT_ABI = [...];
const CONTRACT_ADDRESS = '0x...';
async function callContract() {
// 连接MetaMask
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
// 创建合约实例
const contract = new ethers.Contract(
CONTRACT_ADDRESS,
CONTRACT_ABI,
signer
);
// 读取数据(免费)
const value = await contract.get();
console.log('当前值:', value.toString());
// 写入数据(需要Gas)
const tx = await contract.set(42);
console.log('交易哈希:', tx.hash);
// 等待确认
const receipt = await tx.wait();
console.log('交易已确认:', receipt);
}
8. 安全最佳实践
8.1 智能合约安全清单
智能合约安全
重入攻击防护
整数溢出检查
访问控制
外部调用安全
使用ReentrancyGuard
先改状态再转账
Solidity 0.8+内置检查
使用SafeMath库
onlyOwner修饰符
角色权限管理
限制Gas消耗
检查返回值
8.2 常见漏洞与防护
| 漏洞类型 | 描述 | 防护措施 |
|---|---|---|
| 重入攻击 | 恶意合约在回调中重复调用 | 使用ReentrancyGuard,先改状态再转账 |
| 整数溢出 | 数值超出类型范围 | Solidity 0.8+自动检查 |
| 访问控制 | 函数权限设置不当 | 使用modifier限制访问 |
| 随机数预测 | 链上随机数可预测 | 使用Chainlink VRF |
| 前端跑路 | 合约可被开发者修改 | 使用透明代理或不可升级合约 |
8.3 安全代码示例
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title SecureVault
* @dev 安全的保险柜合约,演示安全最佳实践
*/
contract SecureVault is ReentrancyGuard, Ownable {
mapping(address => uint256) private balances;
event Deposit(address indexed user, uint256 amount);
event Withdrawal(address indexed user, uint256 amount);
// 存款
function deposit() external payable {
require(msg.value > 0, "Amount must be greater than 0");
balances[msg.sender] += msg.value;
emit Deposit(msg.sender, msg.value);
}
// 取款(防重入)
function withdraw(uint256 amount) external nonReentrant {
require(balances[msg.sender] >= amount, "Insufficient balance");
// 先更新状态,再转账(Checks-Effects-Interactions模式)
balances[msg.sender] -= amount;
(bool success, ) = payable(msg.sender).call{value: amount}("");
require(success, "Transfer failed");
emit Withdrawal(msg.sender, amount);
}
// 查询余额
function getBalance() external view returns (uint256) {
return balances[msg.sender];
}
// 紧急暂停(仅管理员)
bool private paused;
modifier whenNotPaused() {
require(!paused, "Contract is paused");
_;
}
function setPaused(bool _paused) external onlyOwner {
paused = _paused;
}
}
9. 测试与部署
9.1 单元测试
python
import pytest
from web3 import Web3
from web3.providers.eth_tester import EthereumTesterProvider
from solcx import compile_source, install_solc
# 安装编译器
install_solc('0.8.20')
# 测试合约源码
CONTRACT_SOURCE = """
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Counter {
uint256 public count;
function increment() public {
count += 1;
}
function decrement() public {
require(count > 0, "Count cannot be negative");
count -= 1;
}
}
"""
@pytest.fixture
def w3():
"""Web3测试实例"""
return Web3(EthereumTesterProvider())
@pytest.fixture
def contract(w3):
"""部署测试合约"""
compiled = compile_source(CONTRACT_SOURCE, output_values=["abi", "bin"])
_, interface = next(iter(compiled.items()))
Counter = w3.eth.contract(
abi=interface["abi"],
bytecode=interface["bin"]
)
tx_hash = Counter.constructor().transact()
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
return w3.eth.contract(
address=receipt.contractAddress,
abi=interface["abi"]
)
def test_initial_count(contract):
"""测试初始计数"""
assert contract.functions.count().call() == 0
def test_increment(contract):
"""测试增加计数"""
contract.functions.increment().transact()
assert contract.functions.count().call() == 1
def test_decrement(contract):
"""测试减少计数"""
contract.functions.increment().transact()
contract.functions.decrement().transact()
assert contract.functions.count().call() == 0
def test_decrement_below_zero(contract):
"""测试计数不能为负"""
with pytest.raises(Exception):
contract.functions.decrement().transact()
9.2 部署脚本
python
from web3 import Web3
from eth_account import Account
from solcx import compile_source, install_solc
import json
import os
class ContractDeployer:
"""合约部署器"""
def __init__(self, network_config):
"""
初始化部署器
network_config: {
"rpc_url": "https://...",
"private_key": "0x...",
"chain_id": 1
}
"""
self.w3 = Web3(Web3.HTTPProvider(network_config["rpc_url"]))
self.account = Account.from_key(network_config["private_key"])
self.chain_id = network_config["chain_id"]
install_solc('0.8.20')
def deploy(self, contract_source, constructor_args=None):
"""部署合约"""
# 编译
compiled = compile_source(contract_source, output_values=["abi", "bin"])
_, interface = next(iter(compiled.items()))
# 创建合约
Contract = self.w3.eth.contract(
abi=interface["abi"],
bytecode=interface["bin"]
)
# 构建交易
nonce = self.w3.eth.get_transaction_count(self.account.address)
if constructor_args:
tx = Contract.constructor(*constructor_args).build_transaction({
"from": self.account.address,
"nonce": nonce,
"chainId": self.chain_id,
"gas": 3000000,
"gasPrice": self.w3.eth.gas_price
})
else:
tx = Contract.constructor().build_transaction({
"from": self.account.address,
"nonce": nonce,
"chainId": self.chain_id,
"gas": 3000000,
"gasPrice": self.w3.eth.gas_price
})
# 签名并发送
signed = self.account.sign_transaction(tx)
tx_hash = self.w3.eth.send_raw_transaction(signed.raw_transaction)
print(f"部署交易: {tx_hash.hex()}")
# 等待确认
receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"合约地址: {receipt.contractAddress}")
print(f"Gas使用: {receipt.gasUsed}")
# 保存部署信息
deployment_info = {
"address": receipt.contractAddress,
"abi": interface["abi"],
"tx_hash": tx_hash.hex(),
"gas_used": receipt.gasUsed
}
with open("deployment.json", "w") as f:
json.dump(deployment_info, f, indent=2)
return deployment_info
# 网络配置
NETWORKS = {
"sepolia": {
"rpc_url": "https://sepolia.infura.io/v3/YOUR_PROJECT_ID",
"chain_id": 11155111
},
"mainnet": {
"rpc_url": "https://mainnet.infura.io/v3/YOUR_PROJECT_ID",
"chain_id": 1
}
}
# 部署示例
if __name__ == "__main__":
import sys
network = sys.argv[1] if len(sys.argv) > 1 else "sepolia"
private_key = os.environ.get("PRIVATE_KEY")
config = NETWORKS[network].copy()
config["private_key"] = private_key
deployer = ContractDeployer(config)
# 部署合约
# deployer.deploy(CONTRACT_SOURCE, constructor_args=["MyToken", "MTK", 1000000])
10. 总结
本文从智能合约基础概念出发,系统介绍了区块链DApp开发的完整流程。
核心要点回顾
- 智能合约:部署在区块链上的可执行代码,自动执行、不可篡改
- Solidity:以太坊主流智能合约语言,语法类似JavaScript
- Web3.py:Python与以太坊交互的核心库,支持合约部署和调用
- DApp架构:前端 + Web3 + 智能合约 + 区块链的完整技术栈
学习路径建议
Solidity基础
本地开发测试
Web3.py交互
前端集成
测试网部署
主网上线
思考题
- 智能合约的"不可篡改"特性既是优势也是劣势,如何处理合约升级需求?
- DApp与传统Web应用相比,有哪些用户体验上的挑战?如何解决?
- 除了以太坊,还有哪些智能合约平台?它们各有什么特点?