传统的gas定价模型(Txn Type===0)
import Web3 from 'web3';import { AbiItem } from 'web3-utils/types/index.d';import erc20 from '../abis/erc20.json';import { FeeMarketEIP1559Transaction as Tx } from '@ethereumjs/tx';import { Common, Chain, Hardfork } from '@ethereumjs/common';import config from '../constants/service.config';const webSocketProvider = (rpcUrl: string) => { return new Web3.providers.HttpProvider(rpcUrl);};const provider = (rpcUrl: string = config.alchemyEthBaseUrl) => { return new Web3(webSocketProvider(rpcUrl));};const transfer = async ( { ...rest }: any) => { const { privateKey, contractAddress, tokenAddress, recipientAddress, rpcUrl, senderAddress, amount, ...args } = rest; const web3 = provider(); const { toHex, toWei, hexToNumber } = web3.utils; const gasPrice = await web3.eth.getGasPrice(); try { const nounce = await web3.eth.getTransactionCount(senderAddress, 'pending'); let rawTx = {}; if (['eth', 'matic'].includes(tokenAddress.toLowerCase())) { // 以太坊主币 rawTx = { to: recipientAddress, value: toHex(toWei(String(amount))), }; } else { // 合乎erc20标准的代币 const contract = new web3.eth.Contract(erc20 as AbiItem[], tokenAddress, { from: senderAddress, }); const transferEvent = contract.methods.transfer( recipientAddress, toHex(toWei(String(amount))) ); const limit = await transferEvent.estimateGas(); // gas limit rawTx = { to: tokenAddress, gasLimit: toHex(Math.ceil(limit * 1.2)), data: transferEvent.encodeABI(), }; } /** * 组装交易数据: * to: * 如果是以太坊主币,to是收款人地址 * 如果是erc20代码,to是协定地址,收款人地址在合约办法:contrack.methods.transfer中定义 * value: * 如果是以太坊主币,value是转账金额 * 如果是erc20代码,value是`toHex(0)`,转账金额在合约办法:contrack.methods.transfer中定义 * maxPriorityFeePerGas * Txn Type=2所必须,进步优先级,给予矿工的小费 * maxFeePerGas * Txn Type=2所必须,进步优先级,给予矿工小费的最大值 */ rawTx = { from: senderAddress, nonce: toHex(nounce), gasLimit: toHex(21000), //"21000", gasPrice: toHex(gasPrice), value: '0x00', //"10000000",//'0x00', ...rawTx, }; /** * 以太坊资源的通用实现类; * 创立以太坊链和分叉的实例,实现EIP1559性能须要抉择London硬分叉 * new Common({ chain: Chain.Mainnet, eips: [1559] }) */ const common = new Common({ chain: Chain.Goerli, hardfork: Hardfork.London, }); const unsignedTx = Tx.fromTxData(rawTx, { common }); const secretKey = Buffer.from(privateKey.slice(2), 'hex'); const signedTx = unsignedTx.sign(secretKey); const serializedTx = signedTx.serialize(); const signedTransactionData = '0x' + serializedTx.toString('hex'); web3.eth .sendSignedTransaction(signedTransactionData, async function (err, hash) { if (!err) { /** * gasfee = `${web3.utils.fromWei( (Number((rawTx as any).gasLimit) * Number(gasPrice)).toString() )}${wallet.unit}`; */ console.log('-----hash-------', hash); const url = `https://goerli.etherscan.io/tx/${hash}`; const accepted = `https://etherscan.io/api?module=localchk&action=txexist&txhash=${hash}`; console.log('TX Link', url); console.log('Accepted', accepted); } else { console.log('-----error', err); } }) .on('receipt', (receipt) => { console.log('TX receipt', receipt); // blockHash: "" // blockNumber: // contractAddress: null // cumulativeGasUsed: // effectiveGasPrice: // from: "" // gasUsed: 21000 // logs: [] // logsBloom: "" // status: true // to: "" // transactionHash: "" // transactionIndex: // type: "0x0" const { gasUsed, status } = receipt; /** * gasfee = `${web3.utils.fromWei( (gasUsed * Number(gasPrice)).toString() )}${wallet.unit}`; */ }); } catch (e) { console.log(e); }};
伦敦硬分叉EIP1559(Txn Type===2)
import Web3 from 'web3';import { AbiItem } from 'web3-utils/types/index.d';import erc20 from '../abis/erc20.json';import { FeeMarketEIP1559Transaction as Tx } from '@ethereumjs/tx';import { Common, Chain, Hardfork } from '@ethereumjs/common';import config from '../constants/service.config';const webSocketProvider = (rpcUrl: string) => { return new Web3.providers.HttpProvider(rpcUrl);};const provider = (rpcUrl: string = config.alchemyEthBaseUrl) => { return new Web3(webSocketProvider(rpcUrl));};const transfer = async ( { ...rest }: any) => { const { privateKey, contractAddress, tokenAddress, recipientAddress, rpcUrl, senderAddress, amount, ...args } = rest; const web3 = provider(); const { toHex, toWei, hexToNumber } = web3.utils; const gasPrice = await web3.eth.getGasPrice(); try { const nounce = await web3.eth.getTransactionCount(senderAddress, 'pending'); let rawTx = {}; if (['eth', 'matic'].includes(tokenAddress.toLowerCase())) { // 以太坊主币 rawTx = { to: recipientAddress, value: toHex(toWei(String(amount))), }; } else { // 合乎erc20标准的代币 const contract = new web3.eth.Contract(erc20 as AbiItem[], tokenAddress, { from: senderAddress, }); const transferEvent = contract.methods.transfer( recipientAddress, toHex(toWei(String(amount))) ); const limit = await transferEvent.estimateGas(); // gas limit rawTx = { to: tokenAddress, gasLimit: toHex(Math.ceil(limit * 1.2)), data: transferEvent.encodeABI(), }; } // maxPriorityFeePerGas: Txn Type === 2所须要的字段 const maxPriorityFeePerGasRes = request.post(config.alchemyEthBaseUrl, { id: 1, jsonrpc: '2.0', method: 'eth_maxPriorityFeePerGas', }); const maxPriorityFeePerGas = ((await maxPriorityFeePerGasRes) as any) .result; const getBlockRes = web3.eth.getBlock('latest'); const baseFeePerGas = ((await getBlockRes) as any).baseFeePerGas - 1; /** * 组装交易数据: * to: * 如果是以太坊主币,to是收款人地址 * 如果是erc20代码,to是协定地址,收款人地址在合约办法:contrack.methods.transfer中定义 * value: * 如果是以太坊主币,value是转账金额 * 如果是erc20代码,value是`toHex(0)`,转账金额在合约办法:contrack.methods.transfer中定义 * maxPriorityFeePerGas * Txn Type=2所必须,进步优先级,给予矿工的小费 * maxFeePerGas * Txn Type=2所必须,进步优先级,给予矿工小费的最大值 */ rawTx = { from: senderAddress, nonce: toHex(nounce), gasLimit: toHex(21000), //"21000", maxPriorityFeePerGas: maxPriorityFeePerGas, maxFeePerGas: toHex( 2 * baseFeePerGas + hexToNumber(maxPriorityFeePerGas) ), value: '0x00', //"10000000",//'0x00', ...rawTx, }; /** * 以太坊资源的通用实现类; * 创立以太坊链和分叉的实例,实现EIP1559性能须要抉择London硬分叉 * new Common({ chain: Chain.Mainnet, eips: [1559] }) */ const common = new Common({ chain: Chain.Goerli, hardfork: Hardfork.London, }); const unsignedTx = Tx.fromTxData(rawTx, { common }); const secretKey = Buffer.from(privateKey.slice(2), 'hex'); const signedTx = unsignedTx.sign(secretKey); const serializedTx = signedTx.serialize(); const signedTransactionData = '0x' + serializedTx.toString('hex'); web3.eth .sendSignedTransaction(signedTransactionData, async function (err, hash) { if (!err) { /** * gasfee = `${web3.utils.fromWei( (Number((rawTx as any).gasLimit) * Number(gasPrice)).toString() )}${wallet.unit}`; */ console.log('-----hash-------', hash); const url = `https://goerli.etherscan.io/tx/${hash}`; const accepted = `https://etherscan.io/api?module=localchk&action=txexist&txhash=${hash}`; console.log('TX Link', url); console.log('Accepted', accepted); } else { console.log('-----error', err); } }) .on('receipt', (receipt) => { console.log('TX receipt', receipt); // blockHash: "" // blockNumber: // contractAddress: null // cumulativeGasUsed: // effectiveGasPrice: // from: "" // gasUsed: 21000 // logs: [] // logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" // status: true // to: "" // transactionHash: "" // transactionIndex: // type: "0x0" const { gasUsed, status } = receipt; /** * gasfee = `${web3.utils.fromWei( (gasUsed * Number(gasPrice)).toString() )}${wallet.unit}`; */ }); } catch (e) { console.log(e); }};
内部签名
import Web3 from 'web3';import { bufArrToArr, toBuffer, bigIntToHex, addHexPrefix } from '@ethereumjs/util'...// signatures是内部服务签名const { toHex, toWei, toDecimal } = web3.utils;const { r_hex: r, s_hex: s, recovery_id_hex } = signaturesconst chainId = unsignedTx.common.chainId()const v = chainId === undefined ? BigInt(toDecimal(addHexPrefix(recovery_id_hex)) + 27) : BigInt(toDecimal(addHexPrefix(recovery_id_hex)) + 35) + BigInt(chainId) * BigInt(2)const toSignTx = { nonce: unsignedTx.nonce, gasPrice: unsignedTx.gasPrice, gasLimit: unsignedTx.gasLimit, to: unsignedTx.to, value: unsignedTx.value, data: unsignedTx.data, v, r: addHexPrefix(r), s: addHexPrefix(s),}const signedTx = Tx.fromTxData(toSignTx, { common })const serializeSignedTx = signedTx.serialize()const signedTxHex = addHexPrefix(serializeSignedTx.toString('hex'))web3.eth.sendSignedTransaction(signedTxHex, async function (err, hash) { if (!err) { console.log('-----hash-------', hash); const url = `https://goerli.etherscan.io/tx/${hash}`; const accepted = `https://etherscan.io/api?module=localchk&action=txexist&txhash=${hash}`; console.log('TX Link', url); console.log('Accepted', accepted); } else { console.log('-----error', err); }})