Web3.js开发指南
Web3.js是以太坊生态系统中最流行的JavaScript库之一,用于与以太坊节点交互。本章将介绍Web3.js的核心功能和实践应用。
环境配置
1. 安装Web3.js
| # 使用npm安装
npm install web3
# 使用yarn安装
yarn add web3
|
2. 初始化Web3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | // 使用MetaMask提供的provider
const web3 = new Web3(window.ethereum);
// 使用Infura
const web3 = new Web3(new Web3.providers.HttpProvider(
'https://mainnet.infura.io/v3/YOUR-PROJECT-ID'
));
// 使用WebSocket
const web3 = new Web3(new Web3.providers.WebsocketProvider(
'wss://mainnet.infura.io/ws/v3/YOUR-PROJECT-ID'
));
// 使用本地节点
const web3 = new Web3('http://localhost:8545');
|
基础功能
1. 账户操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | // 创建账户
const account = web3.eth.accounts.create();
console.log('地址:', account.address);
console.log('私钥:', account.privateKey);
// 导入私钥
const privateKey = '0x...';
const importedAccount = web3.eth.accounts.privateKeyToAccount(privateKey);
// 获取账户余额
async function getBalance(address) {
const balance = await web3.eth.getBalance(address);
return web3.utils.fromWei(balance, 'ether');
}
// 获取账户交易数量
async function getNonce(address) {
return await web3.eth.getTransactionCount(address);
}
|
2. 交易处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 | // 发送交易
async function sendTransaction(from, to, value) {
const tx = {
from: from,
to: to,
value: web3.utils.toWei(value, 'ether'),
gas: 21000,
gasPrice: await web3.eth.getGasPrice()
};
return await web3.eth.sendTransaction(tx);
}
// 签名交易
async function signTransaction(privateKey, to, value) {
const tx = {
to: to,
value: web3.utils.toWei(value, 'ether'),
gas: 21000,
gasPrice: await web3.eth.getGasPrice(),
nonce: await web3.eth.getTransactionCount(account.address)
};
const signedTx = await web3.eth.accounts.signTransaction(tx, privateKey);
return await web3.eth.sendSignedTransaction(signedTx.rawTransaction);
}
// 监听交易确认
function waitForConfirmation(txHash) {
return new Promise((resolve, reject) => {
web3.eth.getTransactionReceipt(txHash)
.then(receipt => {
if (receipt) {
resolve(receipt);
} else {
setTimeout(() => waitForConfirmation(txHash), 1000);
}
})
.catch(reject);
});
}
|
智能合约交互
1. 合约部署
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | // 部署合约
async function deployContract(abi, bytecode, from, args = []) {
const contract = new web3.eth.Contract(abi);
const deploy = contract.deploy({
data: bytecode,
arguments: args
});
const gas = await deploy.estimateGas();
const tx = await deploy.send({
from: from,
gas: gas
});
return tx.options.address;
}
// 部署示例
const MyContract = {
abi: [...],
bytecode: '0x...'
};
const contractAddress = await deployContract(
MyContract.abi,
MyContract.bytecode,
account.address,
['Constructor Arg']
);
|
2. 合约方法调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 | // 初始化合约实例
const contract = new web3.eth.Contract(abi, contractAddress);
// 调用view/pure函数
async function readContract() {
const result = await contract.methods.getValue().call();
console.log('Value:', result);
}
// 调用状态修改函数
async function writeContract(from) {
const tx = await contract.methods.setValue(123).send({
from: from,
gas: await contract.methods.setValue(123).estimateGas()
});
console.log('Transaction:', tx.transactionHash);
}
// 批量调用
async function batchCall() {
const batch = new web3.BatchRequest();
const promises = [
new Promise((resolve, reject) => {
batch.add(contract.methods.getValue().call.request(null, (error, result) => {
if (error) reject(error);
else resolve(result);
}));
}),
new Promise((resolve, reject) => {
batch.add(contract.methods.getOwner().call.request(null, (error, result) => {
if (error) reject(error);
else resolve(result);
}));
})
];
batch.execute();
return Promise.all(promises);
}
|
3. 事件监听
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 | // 监听所有事件
contract.events.allEvents()
.on('data', event => console.log('Event:', event))
.on('error', error => console.error('Error:', error));
// 监听特定事件
contract.events.Transfer({
filter: {
from: account.address
},
fromBlock: 'latest'
})
.on('data', event => {
console.log('Transfer:', {
from: event.returnValues.from,
to: event.returnValues.to,
value: event.returnValues.value
});
})
.on('error', console.error);
// 获取历史事件
async function getPastEvents() {
const events = await contract.getPastEvents('Transfer', {
filter: {
from: account.address
},
fromBlock: 0,
toBlock: 'latest'
});
return events.map(event => ({
from: event.returnValues.from,
to: event.returnValues.to,
value: event.returnValues.value,
blockNumber: event.blockNumber
}));
}
|
实用工具
1. 数据转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | // Wei转换
const toWei = web3.utils.toWei('1', 'ether');
const fromWei = web3.utils.fromWei('1000000000000000000', 'ether');
// 十六进制转换
const toHex = web3.utils.toHex('123');
const fromHex = web3.utils.hexToNumber('0x7b');
// UTF8转换
const toUtf8 = web3.utils.toUtf8('Hello');
const fromUtf8 = web3.utils.hexToUtf8('0x48656c6c6f');
// 单位转换
const units = {
wei: '1',
kwei: '1000',
mwei: '1000000',
gwei: '1000000000',
szabo: '1000000000000',
finney: '1000000000000000',
ether: '1000000000000000000'
};
|
2. 数据编码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | // ABI编码
const encoded = web3.eth.abi.encodeFunctionCall({
name: 'transfer',
type: 'function',
inputs: [{
type: 'address',
name: 'to'
}, {
type: 'uint256',
name: 'value'
}]
}, ['0x123...', '1000000000000000000']);
// 参数编码
const params = web3.eth.abi.encodeParameters(
['string', 'uint256'],
['Hello', 123]
);
// 事件编码
const eventSignature = web3.eth.abi.encodeEventSignature(
'Transfer(address,address,uint256)'
);
|
高级特性
1. 订阅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | // 订阅新区块
const subscription = web3.eth.subscribe('newBlockHeaders')
.on('data', block => {
console.log('New block:', block.number);
})
.on('error', error => {
console.error('Error:', error);
});
// 订阅待定交易
web3.eth.subscribe('pendingTransactions')
.on('data', txHash => {
console.log('Pending transaction:', txHash);
});
// 订阅日志
web3.eth.subscribe('logs', {
address: contractAddress,
topics: [web3.utils.sha3('Transfer(address,address,uint256)')]
})
.on('data', log => {
console.log('Log:', log);
});
|
2. ENS集成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | // 解析ENS名称
async function resolveENS(name) {
const address = await web3.eth.ens.getAddress(name);
return address;
}
// 反向解析
async function reverseResolve(address) {
const name = await web3.eth.ens.reverse(address).name();
return name;
}
// 获取ENS属性
async function getENSAttributes(name) {
const resolver = await web3.eth.ens.getResolver(name);
const content = await resolver.methods.contenthash().call();
const addr = await resolver.methods.addr().call();
return { content, addr };
}
|
错误处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 | // 错误处理示例
async function safeContractCall() {
try {
const result = await contract.methods.riskyFunction().call();
return result;
} catch (error) {
if (error.message.includes('revert')) {
console.error('Contract reverted:', error.reason);
} else if (error.message.includes('gas')) {
console.error('Out of gas');
} else {
console.error('Unknown error:', error);
}
throw error;
}
}
// 交易错误处理
async function safeTransaction(tx) {
try {
const receipt = await tx.send({
from: account.address,
gas: await tx.estimateGas()
});
return receipt;
} catch (error) {
if (error.code === 4001) {
console.error('Transaction rejected by user');
} else if (error.message.includes('insufficient funds')) {
console.error('Insufficient funds');
} else {
console.error('Transaction failed:', error);
}
throw error;
}
}
|
练习题
- 实现一个完整的代币转账功能:
| // 练习:实现ERC20代币转账
async function transferToken(tokenAddress, to, amount) {
// 你的代码
}
|
- 创建一个事件监听器:
| // 练习:实现事件监听
function setupEventListener(contract) {
// 你的代码
}
|
- 实现批量查询余额:
| // 练习:实现批量余额查询
async function getBatchBalances(addresses) {
// 你的代码
}
|
- 创建一个交易确认检查器:
| // 练习:实现交易确认检查
async function checkTransactionConfirmation(txHash) {
// 你的代码
}
|
- 实现ENS域名解析:
| // 练习:实现ENS解析
async function resolveENSName(name) {
// 你的代码
}
|
参考资源
- Web3.js官方文档
- 以太坊JSON-RPC API
- Web3.js GitHub
- 以太坊开发工具
- ENS文档