Skip to content

Commit

Permalink
Merge pull request #106 from DarkFlorist/updates
Browse files Browse the repository at this point in the history
add timeout (60 secs), reduce get simulation stack spamming to inteceptor, add more information to user
  • Loading branch information
KillariDev authored Dec 23, 2024
2 parents 1d4114d + 63281f7 commit bcda65e
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 36 deletions.
2 changes: 1 addition & 1 deletion app/ts/components/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ export const SettingsModal = ({ display, bouquetNetwork, bouquetSettings }: { di
{ relayMode.value.value === 'mempool' ? <>
<SingleNotice variant = 'warn' title = 'Mempool mode is dangerous' description = { `When mempool mode is enabled. The transactions are sent as individual transactions to the below RPC URL. This means it's possible that only one of the transactions might end up on the chain. Use this mode only if a relay is not available for the network.`} />
<div key = {'mempoolSimulationRpcEndpoint'} className={`flex flex-col justify-center border h-16 outline-none px-4 focus-within:bg-white/5 bg-transparent ${!mempoolSimulationRpcEndpoint.value.valid ? 'border-red-400' : 'border-white/50 focus-within:border-white/80'}`}>
<span className='text-sm text-gray-500'>Mempool Simulation RPC URL (a RPC with eth_simulateV1 support)</span>
<span className='text-sm text-gray-500'>Mempool Simulation RPC URL (an RPC with eth_simulateV1 support)</span>
<input onInput={(e: JSX.TargetedEvent<HTMLInputElement>) => validateMempoolSimulationRpcEndpointInput(e.currentTarget.value)} value={mempoolSimulationRpcEndpoint.value.value} type='text' className='bg-transparent outline-none placeholder:text-gray-600' placeholder='https://' />
</div>
<div key = {'mempoolSubmitRpcEndpoint'} className={`flex flex-col justify-center border h-16 outline-none px-4 focus-within:bg-white/5 bg-transparent ${!mempoolSubmitRpcEndpoint.value.valid ? 'border-red-400' : 'border-white/50 focus-within:border-white/80'}`}>
Expand Down
1 change: 1 addition & 0 deletions app/ts/components/Submit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ export const Submit = ({
</div>
<p><span className='font-bold'>Gas:</span> {formatUnits(getMaxBaseFeeInFutureBlock(blockInfo.value.baseFee, bouquetNetwork.value.blocksInFuture), 'gwei')} gwei + {formatUnits(bouquetNetwork.value.priorityFee.toString(), 'gwei')} gwei priority</p>
<p><span className='font-bold'>Transaction Submit RPC:</span> { bouquetNetwork.value.mempoolSubmitRpcEndpoint }</p>
<p><span className='font-bold'>Transaction Simulation RPC:</span> { bouquetNetwork.value.mempoolSimulationRpcEndpoint }</p>
</> : <>
<p><span className='font-bold'>Gas:</span> {formatUnits(getMaxBaseFeeInFutureBlock(blockInfo.value.baseFee, bouquetNetwork.value.blocksInFuture), 'gwei')} gwei + {formatUnits(bouquetNetwork.value.priorityFee.toString(), 'gwei')} gwei priority</p>
<p><span className='font-bold'>Relays:</span> simulation:{bouquetNetwork.value.simulationRelayEndpoint}, submit:{bouquetNetwork.value.submissionRelayEndpoint} (Block {blockInfo.value.blockNumber.toString()})</p>
Expand Down
8 changes: 4 additions & 4 deletions app/ts/components/Transactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export const Transactions = ({
try {
const uniqueAddresses = [...new Set(bundle.value.transactions.map((tx) => tx.to ? addressString(tx.to) : null ).filter(addr => addr))] as string[]
const abis: (string | undefined)[] = []

const network = getNetwork(bouquetSettings.value, provider.value?.chainId || 1n)
const requests = await Promise.all(
uniqueAddresses.map((address) =>
Expand Down Expand Up @@ -93,7 +93,6 @@ export const Transactions = ({
useSignalEffect(() => {
if (interfaces.value && bundle.value) {
parseTransactions()
compareWithInterceptor()
}
if (provider.value && provider.value.isInterceptor && !interceptorComparison.value.intervalId) createCompareInterval()
})
Expand Down Expand Up @@ -141,9 +140,10 @@ export const Transactions = ({
}

async function createCompareInterval() {
if (!provider.value || !provider.value.isInterceptor) return;
if (!provider.value || !provider.value.isInterceptor) return
const different = await compare()
interceptorComparison.value = { different, intervalId: setInterval(compareWithInterceptor, 20000)}
clearInterval(interceptorComparison.value.intervalId)
interceptorComparison.value = { different, intervalId: setInterval(compareWithInterceptor, 10000) }
}

return (
Expand Down
8 changes: 4 additions & 4 deletions app/ts/library/bundleUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,14 @@ export const getRawTransactionsAndCalculateFeesAndNonces = async (bundle: Flashb
return transactions
}

export const createBundleTransactions = async (
export const createBundleTransactions = (
bundle: Bundle,
signers: Signers,
blockInfo: BlockInfo,
blocksInFuture: bigint,
fundingAmountMin: bigint,
): Promise<FlashbotsBundleTransaction[]> => {
return await Promise.all(bundle.transactions.map(async ({ from, to, gasLimit, value, input, chainId }) => {
): FlashbotsBundleTransaction[] => {
return bundle.transactions.map(({ from, to, gasLimit, value, input, chainId }) => {
const gasOpts = {
maxPriorityFeePerGas: blockInfo.priorityFee,
type: 2,
Expand Down Expand Up @@ -99,5 +99,5 @@ export const createBundleTransactions = async (
...gasOpts,
},
}
}))
})
}
59 changes: 32 additions & 27 deletions app/ts/library/flashbots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export async function simulateBundle(
): Promise<SimulationResponse> {
if (network.blocksInFuture <= 0n) throw new Error('Blocks in future is negative or zero')
const maxBaseFee = getMaxBaseFeeInFutureBlock(blockInfo.baseFee, network.blocksInFuture)
const bundleTransactions = await createBundleTransactions(bundle, signers, blockInfo, network.blocksInFuture, fundingAmountMin)
const bundleTransactions = createBundleTransactions(bundle, signers, blockInfo, network.blocksInFuture, fundingAmountMin)
const txs = await getRawTransactionsAndCalculateFeesAndNonces(bundleTransactions, provider.provider, blockInfo, maxBaseFee)

const bigIntify = (ethersValue: ethers.BigNumberish | null | undefined | AddressLike) => ethersValue ? BigInt(ethersValue.toString()) : undefined
Expand All @@ -94,32 +94,37 @@ export async function simulateBundle(
})) } ], traceTransfers: false, validation: true }, 'latest' ]
} as const
const serialized = serialize(EthSimulateV1Params, data)
const request = await fetch(network.mempoolSimulationRpcEndpoint, { method: 'POST', body: JSON.stringify({ jsonrpc: '2.0', id: 0, ...serialized }), headers: { 'Content-Type': 'application/json' } })
const response = JsonRpcResponse.parse(await request.json())
if ('error' in response) {
console.log(response)
throw new Error(response.error.message)
}
const parsed = EthSimulateV1Result.parse(response.result)
const calls = parsed[0].calls
try {
const request = await fetch(network.mempoolSimulationRpcEndpoint, { method: 'POST', body: JSON.stringify({ jsonrpc: '2.0', id: 0, ...serialized }), headers: { 'Content-Type': 'application/json' }, signal: AbortSignal.timeout(60000) })
const response = JsonRpcResponse.parse(await request.json())
if ('error' in response) {
console.log(response)
throw new Error(response.error.message)
}
const parsed = EthSimulateV1Result.parse(response.result)
const calls = parsed[0].calls

return {
totalGasUsed: calls.reduce((a, b) => a + b.gasUsed, 0n),
firstRevert: calls.map((call, index) => {
const to = bigIntify(txs[index].transaction.to)
if (to === undefined) throw new Error('to is undefined')
const from = bigIntify(txs[index].transaction.from)
return {
...call,
toAddress: addressString(to),
fromAddress: from !== undefined ? addressString(from) : undefined,
}
}).find((txSim) => txSim.status === 'failure'),
results: calls,
gasFees: txs.reduce((totalFee, tx, currentIndex) => {
if (tx.transaction.gasPrice) return totalFee + BigInt(tx.transaction.gasPrice) * calls[currentIndex].gasUsed
return totalFee + min(parsed[0].baseFeePerGas + BigInt(tx.transaction.maxPriorityFeePerGas || 0n), BigInt(tx.transaction.maxFeePerGas || 0n))
}, 0n),
return {
totalGasUsed: calls.reduce((a, b) => a + b.gasUsed, 0n),
firstRevert: calls.map((call, index) => {
const to = bigIntify(txs[index].transaction.to)
if (to === undefined) throw new Error('to is undefined')
const from = bigIntify(txs[index].transaction.from)
return {
...call,
toAddress: addressString(to),
fromAddress: from !== undefined ? addressString(from) : undefined,
}
}).find((txSim) => txSim.status === 'failure'),
results: calls,
gasFees: txs.reduce((totalFee, tx, currentIndex) => {
if (tx.transaction.gasPrice) return totalFee + BigInt(tx.transaction.gasPrice) * calls[currentIndex].gasUsed
return totalFee + min(parsed[0].baseFeePerGas + BigInt(tx.transaction.maxPriorityFeePerGas || 0n), BigInt(tx.transaction.maxFeePerGas || 0n))
}, 0n),
}
} catch (error) {
if (error instanceof DOMException && error.name === 'TimeoutError') throw new Error(`Simulation timed out to RPC: ${network.mempoolSimulationRpcEndpoint}`)
throw error
}
}
case 'relay': {
Expand Down Expand Up @@ -161,7 +166,7 @@ export async function sendBundle(bundle: Bundle, targetBlock: bigint, fundingAmo
if (network.blocksInFuture <= 0n) throw new Error('Blocks in future is negative or zero')
const maxBaseFee = getMaxBaseFeeInFutureBlock(blockInfo.baseFee, network.blocksInFuture)
const transactions = (await getRawTransactionsAndCalculateFeesAndNonces(
await createBundleTransactions(bundle, signers, blockInfo, network.blocksInFuture, fundingAmountMin),
createBundleTransactions(bundle, signers, blockInfo, network.blocksInFuture, fundingAmountMin),
provider.provider,
blockInfo,
maxBaseFee,
Expand Down

0 comments on commit bcda65e

Please sign in to comment.