乙醚提供者-发光机器人-捆绑
此寄存器包含 FlashbotsBundleProvider 乙醚.js 供应商,额外 Provider 改为 ethers.js 使高级别人员能够进入 eth_sendBundle 财务报告和审定财务报表 eth_callBundle 结束点 中继。 。 。 . mev-relay 是主机服务; 不需要运行 mev-relay 或者说 mev-geth 以这个例子为例.
Flashbots启用的中继器和矿工暴露出两个新的jsonrpc端点: eth_sendBundle 财务报告和审定财务报表 eth_callBundle。由于这些是非标准终点,ethers.js和其他库不支持这些请求(如: getTransactionCount) (中文(简体) ). 为了与这些端点互动,您需要访问另一个全天候(非Flashbots)端点进行非计算,气体估计和交易状态.
图书馆提供的一个关键特征是 有效载荷签名,要求将 Flashbot 捆绑提交 mev-relay 服务。 这个库通过 authSigner 传入建筑师. 有关中继签名的更多信息, 请查看这里
此库不是一个完全功能化的醚. js 执行,只是一个简单的提供者类,旨在与现有的 醚.js v6 安装。 。 。 .
示例
安装 ethers.js 和 Flashbots ethers 捆绑供应商 .
npm install --save ethers
npm install --save @flashbots/ethers-provider-bundle
打开一个新的 TypeScript 文件( 如果您愿意, 这也与 JavaScript 一起工作)
import { JsonRpcProvider, Wallet } from "ethers";
import { FlashbotsBundleProvider } from "@flashbots/ethers-provider-bundle";
// Standard json rpc provider directly from ethers.js (NOT Flashbots)
const provider = new JsonRpcProvider(ETHEREUM_RPC_URL, 1)
// `authSigner` is an Ethereum private key that does NOT store funds and is NOT your bot's primary key.
// This is an identifying key for signing payloads to establish reputation and whitelisting
// In production, this should be used across multiple bundles to build relationship. In this example, we generate a new wallet each time
const authSigner = Wallet.createRandom();
// Flashbots provider requires passing in a standard provider
const flashbotsProvider = await FlashbotsBundleProvider.create(
provider, // a normal ethers.js provider, to perform gas estimiations and nonce lookups
authSigner // ethers.js signer wallet, only for signing request payloads, not transactions
)
从这里,你有 flashbotsProvider 对象设置,现在可以执行其中之一 eth_callBundle (通过) simulate())或(或) eth_sendBundle (通过) sendBundle) (中文(简体) ). 其中每一项职能都以下列一系列方式发挥作用: Bundle Transactions
捆绑交易
两者 simulate 财务报告和审定财务报表 sendBundle 运行于一系列严格有序的交易。 虽然采矿者需要签名交易,但提供者库将接受预先签名交易和 TransactionRequest + Signer 交易(它将估算、非计算和在发送到 mev-relay(中文(简体) )
const wallet = new Wallet(PRIVATE_KEY)
const transaction = {
to: CONTRACT_ADDRESS,
data: CALL_DATA
}
const transactionBundle = [
{
signedTransaction: SIGNED_ORACLE_UPDATE_FROM_PENDING_POOL // serialized signed transaction hex
},
{
signer: wallet, // ethers signer
transaction: transaction // ethers populated transaction object
}
]
锁定目标
最后需要的东西 sendBundle() 正在锁定目标 每个捆绑都具体提到一个块。 如果您的捆绑对多个块有效(包括所有块直到被布雷), sendBundle() 每一个区块都必须呼叫,最好是在前面的一个区块上。 这让你有机会重新评价你抓住的机会, 并重新签下一个更高的非ce,必要时.
街区应该永远是一个 未来 block, 绝不是当前 .
const targetBlockNumber = (await provider.getBlockNumber()) + 1
天然气价格和EIP-1559
EIP-1559启用前,搜索者提交交易的最常用方式是使用 gasPrice=0时,向 block.coinbase 以交易成功为条件。 所有交易都必须支付 baseFee 现在,一个街区的属性。 例如,如何确保您正在使用这个 baseFee,参见 demo.ts 在此仓库中.
const block = await provider.getBlock(blockNumber)
const maxBaseFeeInFutureBlock = FlashbotsBundleProvider.getMaxBaseFeeInFutureBlock(block.baseFeePerGas, BLOCKS_IN_THE_FUTURE)
const eip1559Transaction = {
to: wallet.address,
type: 2,
maxFeePerGas: PRIORITY_FEE.add(maxBaseFeeInFutureBlock),
maxPriorityFeePerGas: PRIORITY_FEE,
gasLimit: 21000,
data: '0x',
chainId: CHAIN_ID
}
FlashbotsBundleProvider.getMaxBaseFeeInFutureBlock 计算可能的最大基准Fee BLOCKS_IN_THE_FUTURE 区块,每个区块的最大扩展。 你将不支付这个费用, 只要它被指定为 maxFeePerGas,你只支付街区的 baseFee。 。 。 .
另外,如果你拥有 baseFeePerGas, (中文(简体) ), gasUsed,以及 gasLimit 从当前区块开始,未来只针对一个区块,您可以使用 FlashbotsBundleProvider.getBaseFeeInNextBlock。该方法执行基于 EIP1559定义
模拟和发送
现在我们有了:
- Flashbots 供应商
flashbotsProvider - 交易包
transactionBundle - 块数
targetBlockNumber
我们可以运行模拟 直接提交矿工,通过 mev-relay。 。 。 .
模拟 :
const signedTransactions = await flashbotsProvider.signBundle(transactionBundle)
const simulation = await flashbotsProvider.simulate(signedTransactions, targetBlockNumber)
console.log(JSON.stringify(simulation, null, 2))
发送 :
const flashbotsTransactionResponse = await flashbotsProvider.sendBundle(
transactionBundle,
targetBlockNumber,
)
Flashbots 交易响应
打电话后 sendBundle,该提供者将返回与您提交的捆绑相关的辅助对象的保证.
这些函数返回在交易提交时间可用的元数据,以及以下可以等待,跟踪并模拟捆绑行为的函数.
bundleTransactions()- 发送到中继器的一系列交易描述,包括散列、非ce和原始交易.receipts()- 返回与作为捆绑的一部分转发的交易散列相应的一系列交易收据的承诺。 将不等待块被布雷;可能返回不完整的信息wait()- 返回承诺,等待达到目标区块号 或 其中一项交易由于非现金问题而变得无效(包括但不限于从您的捆绑中提取的一笔交易太早)。 返回等待解析为状态表simulate()- 一旦达到适当的区块高度,就返回交易模拟的承诺。 3⁄4 ̄ ̧漯B
可选的 eth sendBundle 参数
在目标块数之外,对象可以带有可选属性:
{
minTimestamp, // optional minimum timestamp at which this bundle is valid (inclusive)
maxTimestamp, // optional maximum timestamp at which this bundle is valid (inclusive)
revertingTxHashes: [tx1, tx2] // optional list of transaction hashes allowed to revert. Without specifying here, any revert invalidates the entire bundle.
}
分钟时间戳/ 最大时间戳
虽然每个捆绑的目标只有一个块,但可以根据块的时间戳来添加一个过滤器以获取有效性. 这个可以 没有 允许根据时间戳瞄准任何区块编号,或指示矿工使用何种时间戳,它只是作为二级过滤器.
如果您的捆绑在某一时间之前无效, 或者包含一个已到期的机会, 设置这些值可以让矿工在阶段的早些时候跳过捆绑处理 .
此外,今后你可以瞄准几个区块,但使用严格的最大时标,确保考虑在一定时间内将你的捆绑纳入其中,无论在那个时间范围内布设了多快的区块.
恢复交易
如果交易包包括,则不考虑列入 任意 返回或失败的交易。 虽然这通常是可取的,但有些先进的使用情况是,查询人可能希望将失败的交易带入链条。 这通常对非现金管理是可取的。 考虑:
Nonce # 1 = 失败( 无关) 令牌转让 第2号交易=DEX交易
如果搜索者想将 #2 带入链中,则必须先包含 #1,其失败与所期望的交易 #2无关. 这在高气压时期特别常见.
可选参数 revertingTxHashes 允许查询者指定可以(但不需要)返回的一系列交易.
付你的钱
除了支付装有天然气价格的捆绑外,捆绑还可以通过下列方式有条件地支付矿工的费用:
block.coinbase.transfer(_minerReward)
或者说
block.coinbase.call{value: _minerReward}("");
(假定 微分奖励是一种固态) uint256 将微值直接转让给矿工)
捆绑的全部价值在结尾处相加,所以并不是每个交易都需要有气价或 block.coinbase 支付,只要至少一个支付,并支付足够支持非支付交易中使用的气体.
注意 : 如果交易尚未存在于混合体中, 燃气费将只会对您的捆绑有益 。 当将待决交易包含在捆绑中时,类似于天然气价格为 0; 捆绑中的其他交易需要支付更多的气体.
包和用户统计
Flashbots 中继器也可以返回关于您作为一个用户(仅由您的签名地址识别)和任何先前提交的捆绑的统计数据.
-
getUserStats()返回关于过去性能的汇总度量,包括如果您的签名密钥目前符合“高优先级”队列的条件. 更多关于搜索者的名声 -
getBundleStats(bundleHash, targetBlockNumber)返回单个捆绑提交的具体数据,包括捆绑到矿工之前各个阶段的详细时间戳。 你可以从模拟中获得捆绑的哈什:const simulation = await flashbotsProvider.simulate(signedTransactions, targetBlockNumber) console.log(simulation.bundleHash)
调查损失
当您的捆绑未能降落在指定的区块中时, 有很多原因可以发生这种情况 。 列出原因,请检查 Flashbots Docs:为什么我的交易没有被包括在内?. 为了帮助排除出问题,这个库提供了一种方法,可以模拟一个捆绑在多个位置上,以识别被连锁捆绑的相竞捆绑(如果有的话)并计算相关的定价.
使用说明请查看 demo-research.ts。 。 。 .
如何运行演示( T)
包括一个简单的演示,说明如何用认证签名器验证Flashbots Provider并提交一个[不起作用]捆绑。 这将不产生任何 mev,而是作为样本初始化,帮助您整合到自己的功能搜索器中.
发送私人交易
送一个 单人 交易,无需作为捆绑发送,请使用 sendPrivateTransaction 函数。 这种方法使我们能够比常规捆绑更快,更高效地处理交易. 当你使用这种方法发送交易时,我们将尝试在接下来的25个区块上(最多但不会超过目标区块号)发送给矿工.
const tx = {
from: wallet.address,
to: wallet.address,
value: "0x42",
gasPrice: 99n * BigInt(1e9), // 99 gwei
gasLimit: 21000n,
}
const privateTx = {
transaction: tx,
signer: wallet,
}
const res = await flashbotsProvider.sendPrivateTransaction(privateTx)
可选的,您可以设置以下参数来微调您的提交 :
// highest block number your tx can be included in
const maxBlockNumber = (await provider.getBlockNumber()) + 10;
// timestamp for simulations
const minTimestamp = 1645753192;
const res = await flashbotsProvider.sendPrivateTransaction(
privateTx,
{maxBlockNumber, minTimestamp}
)
Goerli 上的闪光机器人
为了在去主网前测试Flashbots,可以使用Goerli Flashbots中继器,它与Flashbots启用的Goerli验证器配合工作. Goerli上的闪光机器人需要两个简单的改变:
- 确保您的通用Provider通过 FlashbotsBundle Provider 构建器与 Goerli 连接( 气体估算和非ce 请求需要对应正确的链) :
import { getDefaultProvider } from 'ethers'
const provider = getDefaultProvider('goerli')
- 将中继端点设置为
https://relay-goerli.flashbots.net/
const flashbotsProvider = await FlashbotsBundleProvider.create(
provider,
authSigner,
'https://relay-goerli.flashbots.net/',
'goerli')