web3.js 官方文档 :web3js.readthedocs.io/en/v1.8.0/
Metamask 官方文档: docs.metamask.io/guide/
一、引入web3.js
方式一: npm install web3
方式二:引入 dist/web3.min.js 文件
二、常用方法汇总
1、Web3 浏览器检测
大多数支持以太坊的浏览器(例如 MetaMask)在 window.ethereum 上都有一个符合 EIP-1193 的提供程
if (typeof window.ethereum !== 'undefined') {
console.log('MetaMask is installed!');
// 未安装可以提示用户并新页面打开Metamask 安装网址
window.open('https://metamask.io/download/'); // 打开Metamask 安装页面
}
2、创建Web3实例并设置Provider
const Web3 = require('web3')
const web3 = new Web3(Web3.givenProvider || "ws://localhost:8545");
// Web3.givenProvider 当前环境的原生 provider 会被浏览器设置
// web3.givenProvider 将返回浏览器设置的原生 provider 集,返回 null 再连接本地或远程的节点
// 连接到远程节点
var web3 = new Web3("https://eth-mainnet.alchemyapi.io/v2/your-api-key");
更改Provider
// change provider
web3.setProvider('ws://localhost:8546');
// or
web3.setProvider(new Web3.providers.WebsocketProvider('ws://localhost:8546'));
3、检查网络状况
const Web3 = require('web3')
const web3 = new Web3(Web3.givenProvider || "ws://localhost:8545");
// 判断网络是否连接
let isConnecting = web3.eth.net.isListening(); // 返回结果为布尔值
// 判断当前连接网络类型
let netId = web3.eth.net.getId(); // 返回当前链接网络链id
4、添加网络(wallet_addEthereumChain)
window.ethereum &&
window.ethereum
.request({
method: 'wallet_addEthereumChain',
params: [
{
chainId: 97,
chainName: 'Binance Smart Chain Testnet',
nativeCurrency: {
name: 'BNB',
symbol: 'BNB',
decimals: 18,
},
rpcUrls:'https://bsc-dataseed1.binance.org/',
blockExplorerUrls: 'https://bscscan.com/',
},
],
})
5、切换网络(wallet_switchEthereumChain)
window.ethereum && window.ethereum
.request({
method: 'wallet_switchEthereumChain',
params: [
{
chainId: 97
},
],
})
6、链接钱包 requestAccounts
此方法将从当前环境请求/启用帐户。 此方法仅在您使用来自 Metamask、Status 或 TrustWallet 等应用程序的注入提供程序时才有效。 如果您连接到具有默认 Web3.js 提供程序(WebsocketProvider、HttpProvider 和 IpcProvider)的节点,则它不起作用。
let accounts = web3.eth.requestAccounts();
// 入参 Function -(可选)可选回调,返回错误对象作为第一个参数,结果作为第二个参数。
// 返回启用账户的数组
/* 等同于*/
const onClickConnect = async () => {
try {
let account = await ethereum.request({ method: 'eth_requestAccounts' });
} catch (error) {
console.error(error);
}
};
连接到用户后,可以通过检查 window.ethereum.selectedAddress 重新检查当前帐户.
切换账户监听事件
连接钱包账户切换后触发的事件;
ethereum.on('accountsChanged', (accounts) => {
console.log("accounts",accounts)
});
切换网络监听事件
ethereum.on('chainChanged', (chainId) => {
// 正确处理链更改之后的业务流程可能很复杂。官方建议链更改只有重新加载页面
console.log("chainId",chainId)
window.location.reload();
});
断开连接监听事件
ethereum.on('disconnect', async function (result, error) {
console.log("result",result)
console.log("error",error)
});
7、获取当前链ID getChainId
可以用于检测用户连接到哪个以太坊网络
“已连接”这个词,指的是 web3 站点是否可以访问用户的帐户。 然而,在提供者接口中,“已连接”和“已断开”是指提供者是否可以向当前链发出 RPC 请求。
let eth_chainId = web3.eth.getChainId();
console.log("eth_chainId",eth_chainId)
// 示例
let accounts = await this.web3.eth.requestAccounts();
let eth_chainId = await this.web3.eth.getChainId();
console.log("查询eth_chainId", eth_chainId)
if (eth_chainId !== configs.chainId) { // 与当前
message.warning('Please switch to the Binance Smart Chain Mainnet')
// 此处可调用切换网络方法,切换到正确网络
return;
}
8、签名方法 汇总
- eth_sign (insecure and unadvised to use) 不安全不建议使用
- personal_sign
- signTypedData (currently identical to signTypedData_v1)(与 signTypedData_v1 相同)
- signTypedData_v1
- signTypedData_v3
- signTypedData_v4 (最新EIP-712 规范的版本)
personal_sign
web3.eth.personal.sign(dataToSign, address, password [, callback])
入参:
1.字符串 - 要签名的数据。 如果是字符串,它将使用 web3.utils.utf8ToHex 进行转换。
-
字符串 - 用于签署数据的地址。
-
字符串 - 用于签署数据的帐户的密码。可以不传
-
Function - (可选)可选回调,第一个参数返回错误对象,第二个参数返回结果。
返回:字符串类型的签名凭证
web3.eth.personal.sign(web3.utils.utf8ToHex("Hello world"), "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", "test password!")
.then(console.log);
> "0x30755ed65396facf86c53e6217c52b4daebe72aa4941d89635409de4c9c7f9466d4e9aaec7977f05e923889b33c0d0dd27d7226b6e6f56ce737465c5cfd04be400"
/* 等同于 */
let selectedAccount = '0x4265c9e28C4305789232813c80cfD8d0757a8149'
let msg = 'hello Web3 !'
const signature = web3.eth.personal.sign(msg, selectedAccount);
console.log("signature",signature)
signTypedData_v4
符合最新EIP712标准的签名方式,交易相关签名建议使用这种方式
let msgParams = createSignMessage(msg);
let params = [from, msgParams];
let method = "eth_signTypedData_v4";
console.log("msgParams===============", JSON.parse(msgParams));
web3.currentProvider.sendAsync(
{
method,
params,
from: from,
},
function (err, result) {
if (err) {
console.log("err", err);
message.error(err.message);
resolve(false);
}
if (result.error) return console.error("ERROR", result);
console.log("TYPED SIGNED:" + JSON.stringify(result.result));
let signData = JSON.stringify(result.result);
resolve(signData);
}
);
详细参考文档 见:
9、余额查询 getBalance
getBalance 方法可以获取给定区块的地址余额。
web3.eth.getBalance(address [, defaultBlock] [, callback])
// defaultBlock 参数可选,如果传递此参数,它将不使用 web3.eth.defaultBlock 设置的默认块。
// 也可以使用预定义的块编号,如“最早”、“最新”、“待定”、“安全”或“最终”。
// 示例
web3.eth.getBalance("0x407d73d8a49eeb85d32cf465507dd71d507100c1")
.then(console.log);
> "1000000000000"
10、交易查询 getTransaction
web3.eth.getTransaction(transactionHash [, callback])
参数:
- transactionHash:String - 交易的哈希值
- callback:Function - 可选的回调函数,其第一个参数为错误对象,第二个参数为返回结果。
返回值:
一个Promise对象,其解析值为具有给定哈希值的交易对象,该对象具有如下字段:
- hash 32 Bytes - String: 交易的哈希值
- nonce - Number: 交易发送方在此交易之前产生的交易数量
- blockHash 32 Bytes - String: 交易所在块的哈希值。如果交易处于pending状态,则该值为null
- blockNumber - Number: 交易所在块的编号,如果交易处于pending状态,则该值为null
- transactionIndex - Number: 交易在块中的索引位置,如果交易处于pending状态,则该值为null
- from - String: 交易发送方的地址
- to - String: 交易接收方的地址。对于创建合约的交易,该值为null
- value - String: 以wei为单位的转账金额
- gasPrice - String: 发送方承诺的gas价格,以wei为单位
- gas - Number: 发送方提供的gas用量
- input - String: 随交易发送的数据
web3.eth.getTransaction('0x9fc76417374aa880d4449a1f7f31ec597f00b1f6f3dd2d66f4c9c6c445836d8b§234')
.then(console.log);
> {
"hash": "0x9fc76417374aa880d4449a1f7f31ec597f00b1f6f3dd2d66f4c9c6c445836d8b",
"nonce": 2,
"blockHash": "0xef95f2f1ed3ca60b048b4bf67cde2195961e0bba6f70bcbea9a2c4e133e34b46",
"blockNumber": 3,
"transactionIndex": 0,
"from": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"to": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
"value": '123450000000000000',
"gas": 314159,
"gasPrice": '2000000000000',
"input": "0x57cb2fc4"
}
11、发送交易 sendTransaction
const transactionParameters = {
nonce: '0x00', // ignored by MetaMask
gasPrice: '0x09184e72a000', // customizable by user during MetaMask confirmation.
gas: '0x2710', // customizable by user during MetaMask confirmation.
to: '0x0000000000000000000000000000000000000000', // Required except during contract publications.
from: ethereum.selectedAddress, // must match user's active address.
value: '0x00', // Only required to send ether to the recipient from the initiating external account.
data:
'0x7f7465737432000000000000000000000000000000000000000000000000000000600057', // Optional, but used for defining smart contract creation and interaction.
chainId: '0x3', // Used to prevent transaction reuse across blockchains. Auto-filled by MetaMask.
};
// txHash is a hex string
// As with any RPC call, it may throw an error
const txHash = await ethereum.request({
method: 'eth_sendTransaction',
params: [transactionParameters],
});
三、合约相关方法
1、初始化合约对象
new web3.eth.Contract(jsonInterface[, address][, options])
创建一个新的合约实例,其所有方法和事件都在其 json 接口对象中定义。
入参:
- jsonInterface - Object:合约实例化的json接口
- address - 字符串(可选):要调用的智能合约的地址。
- options - 对象(可选):合约的选项。 有些用作调用和事务的后备:
- from - 字符串:应从地址进行交易。
- gasPrice - 字符串:用于交易的以 wei 为单位的 gas 价格。
- gas - Number:为交易提供的最大 gas(gas 限制)。
- data - String:合约的字节码。 在部署合约时使用。
返回:
- 对象:合约实例及其所有方法和事件。
var MyContract = new web3.eth.Contract(abi,address)
// 具体使用示例
const ProxyRegistryAddress = "0x392431428a1CEAcC4022C8E86eBb73Ed4bf2EFa6"; // 用户注册合约地址
const registerAbi = [...省略json 接口文件] // 合约实例化的ABI json接口
// 初始化合约对象
const initAllContact = () => {
let web3 = Web3Manager.getWeb3Ins(); // 获取web 实例对象
let ProxyRegistryContact = new web3.eth.Contract(
RegisterAbi,
ProxyRegistryAddress
);
};
2、Call 调用合约方法
myContract.methods.myMethod([param1[, param2[, ...]]]).call(options [, defaultBlock] [, callback])
- myMethod 为合约内部定义的具体方法名;
- [param1[, param2[, ...]]] 为合约定义的方法所需要的参数;
参数:
options - Object(可选):调用的参数选项
- from - 字符串(可选):调用事件的地址,是可选的,但强烈建议设置它,否则默认为 address(0)
- gasPrice - 字符串(可选):交易的以 wei 为单位的 gas 价格
- gas - Number(可选):交易提供的最大 gas(gas 限制)
defaultBlock - Number|String|BN|BigNumber(可选):
如果您传递此参数,它将不使用通过 contract.defaultBlock 设置的默认块。也可以使用预定义的块编号,如"earliest", "latest", "pending", "safe" or "finalized" 作为参数。用于从过去的区块中请求数据或重放交易。
callback - 函数(可选):此回调的第一个参数为err,第二个参数为智能合约方法执行的结果result
// 判断用户是否注册 || 查询用户的代理合约地址
let walletAddress = '0x4265c9e28C4305789232813c80cfD8d0757a8149'
const isRegister = () => {
let { ProxyRegistryContact } = initAllContact(); // 初始化合约
return new Promise((resolve, reject) => {
ProxyRegistryContact.methods
.proxies(walletAddress)
.call((err, result) => {
if (result) {
resolve(result);
}
if (err) {
message.error(err);
reject(err)
}
});
})
};
3、 Send 发送合约方法交易
myContract.methods.myMethod([param1[, param2[, ...]]]).send(options[, callback])
- myMethod: 合约内部定义的具体方法名;
- [param1[, param2[, ...]]] :合约内部方法所需要的参数;
入参:
options - Object:发送交易的参数选项。
- from - 字符串:交易的发送地址。
- gasPrice - 字符串(可选):用于此交易的以 wei 为单位的 gas 价格。
- gas - Number(可选):为此交易提供的最大 gas(gas 限制)。
- value - Number|String|BN|BigNumber(可选):为交易传输的价值,以 wei 为单位。
- nonce - Number(可选):交易的随机数
callback - 函数(可选):返回transactionHash 交易哈希,或者错误对象回调;
返回:
回调函数中将返回32字节长的交易哈希值。
PromiEvent: 一个Promise对象,当交易收据有效时或者发送交易时解析为新的合约实例。 它同时也是一个事件发生器,声明有以下事件:
- "transactionHash" 返回 String: 交易发送后得到有效交易哈希值时触发
- "receipt" 返回 Object: 交易收据有效时触发。
- "confirmation" 返回 Number, Object: 收到确认时触发
- "error" 返回 Error: 交易发送过程中如果出现错误则触发此事件。对于out of gas错误,其第二个参数为交易收据
myContract.methods.myMethod(123).send({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'})
.on('transactionHash', function(hash){
console.log("hash",hash)
// 返回string 类型的交易hash,交易发送后得到有效交易哈希值时触发
})
.on('confirmation', function(confirmationNumber, receipt,latestBlockHash){
// 发送的交易被确认时触发,返回 confirmationNumber 和 回执对象,latestBlockHash 最新区块hash
})
.on('receipt', function(receipt){ // 交易收据有效时触发,返回一个Object对象
// receipt example
console.log(receipt); // 返回的回执示例
> {
"transactionHash": "0x9fc76417374aa880d4449a1f7f31ec597f00b1f6f3dd2d66f4c9c6c445836d8b",
"transactionIndex": 0,
"blockHash": "0xef95f2f1ed3ca60b048b4bf67cde2195961e0bba6f70bcbea9a2c4e133e34b46",
"blockNumber": 3,
"contractAddress": "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe",
"cumulativeGasUsed": 314159,
"gasUsed": 30234,
"events": {
"MyEvent": {
returnValues: {
myIndexedParam: 20,
myOtherIndexedParam: '0x123456789...',
myNonIndexParam: 'My String'
},
raw: {
data: '0x7f9fade1c0d57a7af66ab4ead79fade1c0d57a7af66ab4ead7c2c2eb7b11a91385',
topics: ['0xfd43ade1c09fade1c0d57a7af66ab4ead7c2c2eb7b11a91ffdd57a7af66ab4ead7', '0x7f9fade1c0d57a7af66ab4ead79fade1c0d57a7af66ab4ead7c2c2eb7b11a91385']
},
event: 'MyEvent',
signature: '0xfd43ade1c09fade1c0d57a7af66ab4ead7c2c2eb7b11a91ffdd57a7af66ab4ead7',
logIndex: 0,
transactionIndex: 0,
transactionHash: '0x7f9fade1c0d57a7af66ab4ead79fade1c0d57a7af66ab4ead7c2c2eb7b11a91385',
blockHash: '0xfd43ade1c09fade1c0d57a7af66ab4ead7c2c2eb7b11a91ffdd57a7af66ab4ead7',
blockNumber: 1234,
address: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'
},
"MyOtherEvent": {
...
},
"MyMultipleEvent":[{...}, {...}]
// 如果有多个相同的事件,它们将在一个数组中
}
}
})
.on('error', function(error, receipt) {
// 如果交易被网络拒绝并带有收据,则第一个参数为error回调,第二个参数才是收据回执。
});
// 用户代币授权
// TokenContact 为代币合约 walletAddress 为用户钱包地址 defaultVal 为默认授权额度
// approve 为合约内部定义的授权方法,TokenTransferProxyAddress与defaultVal 为授权方法所需要的参数
const getAuthorization = (TokenContact, walletAddress) => {
return new Promise((resolve, reject) => {
let defaultVal = web3.utils.toWei("10000000000", "ether");
TokenContact.methods
.approve(TokenTransferProxyAddress, defaultVal)
.send({ from: walletAddress })
.on('receipt', function (receipt) {
resolve(receipt.transactionHash)
})
.on('error', function (error, receipt) {
message.error(error.message);
resolve(false)
})
})
};
4、estimateGas - 估算合约方法gas用量
通过在EVM中执行方法来估算链上执行是需要的gas用量。得到的估算值可能与之后实际发送 交易的gas用量有差异,因为合约的状态可能在两个时刻存在差异。
调用:
myContract.methods.myMethod([param1[, param2[, ...]]]).estimateGas(options[, callback])
参数:
- options - Object : 选项,包括以下字段:
-
- from - String : 可选,交易发送方地址
- gas - Number : 可选,本次交易gas用量上限
- value - Number|String|BN|BigNumber: 可选,交易转账金额,单位:wei
- callback - Function : 可选的回调函数,触发时其第二个参数为gas估算量,第一个参数为错误对象。
返回值:
一个Promise对象,其解析值为估算的gas用量。
// 使用回调函数
myContract.methods.myMethod(123).estimateGas({gas: 5000000}, function(error, gasAmount){
if(gasAmount == 5000000)
console.log('Method ran out of gas');
});
// 使用promise
myContract.methods.myMethod(123).estimateGas({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'})
.then(function(gasAmount){
...
})
.catch(function(error){
...
});