Skip to content

Commit

Permalink
feat: added multiple builders
Browse files Browse the repository at this point in the history
Co-authored-by: 0xng <[email protected]>
  • Loading branch information
0xGorilla and 0xng committed Feb 23, 2024
1 parent f695af3 commit f2debbf
Show file tree
Hide file tree
Showing 13 changed files with 141 additions and 193 deletions.
20 changes: 10 additions & 10 deletions __tests__/unit/transactions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,17 @@ describe('transactions', () => {
const expectedError = new Error('Missing property baseFeePerGas on block');

try {
gasModule.getMainnetGasType2Parameters({ block: emptyBlock, burstSize: 2, priorityFeeInWei });
gasModule.getMainnetGasType2Parameters({ block: emptyBlock, blocksAhead: 2, priorityFeeInWei });
} catch (err) {
expect(err).toStrictEqual(expectedError);
}
});

it('should call getBaseFeeInNextBlock when blocksAhead is 0', async () => {
const burstSize = 0;
const blocksAhead = 0;
when(FlashbotsBundleProvider.getBaseFeeInNextBlock).mockReturnValue(mockFlashbotsResponse);

gasModule.getMainnetGasType2Parameters({ block: FAKE_BLOCK, burstSize, priorityFeeInWei });
gasModule.getMainnetGasType2Parameters({ block: FAKE_BLOCK, blocksAhead, priorityFeeInWei });

expect(FlashbotsBundleProvider.getBaseFeeInNextBlock).toHaveBeenCalledWith(
FAKE_BLOCK.baseFeePerGas,
Expand All @@ -57,10 +57,10 @@ describe('transactions', () => {
});

it('should call getBaseFeeInNextBlock when blocksAhead is 1', async () => {
const burstSize = 1;
const blocksAhead = 1;
when(FlashbotsBundleProvider.getBaseFeeInNextBlock).mockReturnValue(mockFlashbotsResponse);

gasModule.getMainnetGasType2Parameters({ block: FAKE_BLOCK, burstSize, priorityFeeInWei });
gasModule.getMainnetGasType2Parameters({ block: FAKE_BLOCK, blocksAhead, priorityFeeInWei });

expect(FlashbotsBundleProvider.getBaseFeeInNextBlock).toHaveBeenCalledWith(
FAKE_BLOCK.baseFeePerGas,
Expand All @@ -70,12 +70,12 @@ describe('transactions', () => {
});

it('should call getMaxBaseFeeInFutureBlock when blocksAhead is more than 1', async () => {
const burstSize = 2;
const blocksAhead = 2;
when(FlashbotsBundleProvider.getMaxBaseFeeInFutureBlock).mockReturnValue(mockFlashbotsResponse);

gasModule.getMainnetGasType2Parameters({ block: FAKE_BLOCK, burstSize, priorityFeeInWei });
gasModule.getMainnetGasType2Parameters({ block: FAKE_BLOCK, blocksAhead, priorityFeeInWei });

expect(FlashbotsBundleProvider.getMaxBaseFeeInFutureBlock).toHaveBeenCalledWith(FAKE_BLOCK.baseFeePerGas, burstSize);
expect(FlashbotsBundleProvider.getMaxBaseFeeInFutureBlock).toHaveBeenCalledWith(FAKE_BLOCK.baseFeePerGas, blocksAhead);
});

it('should return the right maxFeePerGas', async () => {
Expand All @@ -85,7 +85,7 @@ describe('transactions', () => {

const maxFeePerGas = BigNumber.from(priorityFeeInWei).add(mockFlashbotsResponse);

const fnCall = gasModule.getMainnetGasType2Parameters({ block: FAKE_BLOCK, burstSize: 2, priorityFeeInWei });
const fnCall = gasModule.getMainnetGasType2Parameters({ block: FAKE_BLOCK, blocksAhead: 2, priorityFeeInWei });
expect(fnCall.maxFeePerGas).toEqual(maxFeePerGas);
});

Expand All @@ -95,7 +95,7 @@ describe('transactions', () => {
when(FlashbotsBundleProvider.getMaxBaseFeeInFutureBlock).mockReturnValue(mockFlashbotsResponse);

const maxFeePerGas = BigNumber.from(priorityFeeInWei).add(mockFlashbotsResponse);
expect(gasModule.getMainnetGasType2Parameters({ block: FAKE_BLOCK, burstSize: 2, priorityFeeInWei })).toEqual({
expect(gasModule.getMainnetGasType2Parameters({ block: FAKE_BLOCK, blocksAhead: 2, priorityFeeInWei })).toEqual({
priorityFee: BigNumber.from(priorityFeeInWei),
maxFeePerGas,
});
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@keep3r-network/keeper-scripting-utils",
"version": "1.1.0",
"version": "1.1.1",
"description": "A library containing helper functions that facilitate scripting for keepers of the Keep3r Network",
"keywords": [
"ethereum",
Expand Down
73 changes: 0 additions & 73 deletions src/broadcastors/flashbotsBroadcastor.ts

This file was deleted.

3 changes: 1 addition & 2 deletions src/broadcastors/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from './flashbotsBroadcastor';
export * from './privateBroadcastor';
export * from './mempoolBroadcastor';
export * from './stealthBroadcastor';
export * from './privateBroadcastor';
2 changes: 1 addition & 1 deletion src/broadcastors/mempoolBroadcastor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class MempoolBroadcastor {
public doStaticCall = true
) {}

tryToWorkOnMempool = async (props: BroadcastorProps): Promise<void> => {
tryToWork = async (props: BroadcastorProps): Promise<void> => {
const { jobContract, workMethod, workArguments, block } = props;

const txSigner = jobContract.signer as Wallet;
Expand Down
43 changes: 21 additions & 22 deletions src/broadcastors/privateBroadcastor.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { getStealthHash } from '../flashbots';
import { getGasParametersNextBlock, populateTx, sendPrivateTransaction } from '../transactions';
import { getMainnetGasType2Parameters, populateTx, sendBundle } from '../transactions';
import type { TransactionRequest } from '@ethersproject/abstract-provider';
import { Contract, Overrides, Wallet, ethers } from 'ethers';
import { Wallet, Overrides, ethers } from 'ethers';
import { BroadcastorProps } from 'types';

/**
* @notice Creates and populate a private transaction to work a specific job
*
* @param endpoint The endpoint url
* @param stealthRelayer The address of the StealthRelayer contract.
* @param endpoints The endpoint urls
* @param priorityFeeInWei The priority fee in wei
* @param gasLimit The gas limit determines the maximum gas that can be spent in the transaction
* @param doStaticCall Flag to determinate whether to perform a callStatic to work or not. Defaults to true.
Expand All @@ -17,23 +15,19 @@ import { BroadcastorProps } from 'types';
*/
export class PrivateBroadcastor {
constructor(
public endpoint: string,
public stealthRelayer: Contract,
public endpoints: string[],
public priorityFeeInWei: number,
public gasLimit: number,
public doStaticCall = true,
public chainId: number
) {}

async tryToWorkOnStealthRelayer(props: BroadcastorProps): Promise<void> {
async tryToWork(props: BroadcastorProps): Promise<void> {
const { jobContract, workMethod, workArguments, block } = props;

const stealthHash = getStealthHash();
const workData = jobContract.interface.encodeFunctionData(workMethod, [...workArguments]);

if (this.doStaticCall) {
try {
await this.stealthRelayer.callStatic.execute(jobContract.address, workData, stealthHash, block.number);
await jobContract.callStatic[workMethod](...workArguments);
} catch (error: unknown) {
if (error instanceof Error) {
console.log(`Static call failed with ${error.message}`);
Expand All @@ -42,9 +36,14 @@ export class PrivateBroadcastor {
}
}

console.log(`Attempting to work strategy statically succeeded. Preparing real transaction...`);
const blocksAhead = 2;
const targetBlock = block.number + blocksAhead;

const { priorityFee, maxFeePerGas } = getGasParametersNextBlock({ block, priorityFeeInWei: this.priorityFeeInWei });
const { priorityFee, maxFeePerGas } = getMainnetGasType2Parameters({
block,
priorityFeeInWei: this.priorityFeeInWei,
blocksAhead,
});

const txSigner = jobContract.signer as Wallet;

Expand All @@ -57,22 +56,22 @@ export class PrivateBroadcastor {
maxPriorityFeePerGas: priorityFee,
type: 2,
};

const tx: TransactionRequest = await populateTx({
contract: this.stealthRelayer,
functionName: 'execute',
functionArgs: [jobContract.address, workData, stealthHash, block.number],
contract: jobContract,
functionName: workMethod,
functionArgs: [...workArguments],
options,
chainId: this.chainId,
});

const privateTx = await txSigner.signTransaction(tx);
console.log(`Bundle populated successfully. Sending private bundle: ${workArguments}`);

console.log(`Transaction populated successfully. Sending private transaction for strategy: ${workArguments}`);

await sendPrivateTransaction({
endpoint: this.endpoint,
await sendBundle({
endpoints: this.endpoints,
privateTx,
maxBlockNumber: ethers.utils.hexlify(block.number).toString(),
targetBlock: ethers.utils.hexlify(targetBlock).toString(),
});
}
}
70 changes: 32 additions & 38 deletions src/broadcastors/stealthBroadcastor.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,31 @@
import { calculateTargetBlocks, getStealthHash } from '../flashbots';
import { getStealthHash } from '../flashbots';
import { getMainnetGasType2Parameters, populateTx, sendBundle } from '../transactions';
import type { TransactionRequest } from '@ethersproject/abstract-provider';
import type { FlashbotsBundleTransaction, FlashbotsBundleProvider } from '@flashbots/ethers-provider-bundle';
import type { Wallet, Overrides, Contract } from 'ethers';
import { Contract, Overrides, Wallet, ethers } from 'ethers';
import { BroadcastorProps } from 'types';

/**
* @notice Creates and populate a transaction for work in a determinated job using flashbots
* @notice Creates and populate a private transaction to work a specific job
*
* @param flashbotsProvider The flashbots provider. It contains a JSON or WSS provider
* @param flashbots The flashbot that will send the bundle
* @param burstSize The amount of transactions for future blocks to be broadcast each time
* @param endpoints The endpoint urls
* @param stealthRelayer The address of the StealthRelayer contract.
* @param priorityFeeInWei The priority fee in wei
* @param gasLimit The gas limit determines the maximum gas that can be spent in the transaction
* @param doStaticCall Flag to determinate whether to perform a callStatic to work or not. Defaults to true.
* @param gasLimit The gas limit determines the maximum gas that can be spent in the transaction
* @param doStaticCall Flag to determinate whether to perform a callStatic to work or not. Defaults to true.
* @param chainId The chainId.
*
*/
export class StealthBroadcastor {
public chainId: number;

constructor(
public flashbotsProvider: FlashbotsBundleProvider,
public endpoints: string[],
public stealthRelayer: Contract,
public priorityFeeInWei: number,
public gasLimit: number,
public burstSize: number,
public doStaticCall = true
) {
this.chainId = flashbotsProvider.network.chainId;
}
public doStaticCall = true,
public chainId: number
) {}

async tryToWorkOnStealthRelayer(props: BroadcastorProps): Promise<void> {
async tryToWork(props: BroadcastorProps): Promise<void> {
const { jobContract, workMethod, workArguments, block } = props;

const stealthHash = getStealthHash();
Expand All @@ -47,16 +42,15 @@ export class StealthBroadcastor {
}
}

console.log(`Attempting to work strategy statically succeeded. Preparing real transaction...`);
console.log(`Attempting to work strategy statically succeeded. Preparing real bundle...`);

const nextBlock = ++block.number;

const targetBlocks = calculateTargetBlocks(this.burstSize, nextBlock);
const blocksAhead = 2;
const targetBlock = block.number + blocksAhead;

const { priorityFee, maxFeePerGas } = getMainnetGasType2Parameters({
block,
priorityFeeInWei: this.priorityFeeInWei,
burstSize: this.burstSize,
blocksAhead,
});

const txSigner = jobContract.signer as Wallet;
Expand All @@ -70,23 +64,23 @@ export class StealthBroadcastor {
maxPriorityFeePerGas: priorityFee,
type: 2,
};
for (const targetBlock of targetBlocks) {
const tx: TransactionRequest = await populateTx({
contract: this.stealthRelayer,
functionName: 'execute',
functionArgs: [jobContract.address, workData, stealthHash, targetBlock],
options,
chainId: this.chainId,
});

const privateTx: FlashbotsBundleTransaction = {
transaction: tx,
signer: txSigner,
};
const tx: TransactionRequest = await populateTx({
contract: this.stealthRelayer,
functionName: 'execute',
functionArgs: [jobContract.address, workData, stealthHash, targetBlock],
options,
chainId: this.chainId,
});

console.log('Transaction populated successfully. Sending bundle...');
const privateTx = await txSigner.signTransaction(tx);

await sendBundle({ flashbotsProvider: this.flashbotsProvider, privateTxs: [privateTx], targetBlockNumber: targetBlock });
}
console.log(`Bundle populated successfully. Sending private bundle for strategy: ${workArguments}`);

await sendBundle({
endpoints: this.endpoints,
privateTx,
targetBlock: ethers.utils.hexlify(targetBlock).toString(),
});
}
}
Loading

0 comments on commit f2debbf

Please sign in to comment.