Skip to content

Commit

Permalink
feat: send bundle (#7)
Browse files Browse the repository at this point in the history
* fix: target block bug (bump flashbots package)

* feat: rollback package upgrade

* feat: adding sendBundle method to stealthBroadcast

* fix: removing yarn lock update

* fix: adding await

* style: plus one syntax

* fix: import

* feat: adding privateBroadcastor

* fix: removing debug line

* feat: added multiple builders

Co-authored-by: 0xng <[email protected]>

* fix: undo destroy method

* feat: improved error log

* ci: upgraded to node 18

---------

Co-authored-by: 0xng <[email protected]>
Co-authored-by: 0xGorilla <[email protected]>
Co-authored-by: 0xng <[email protected]>
Co-authored-by: 0xGorilla <[email protected]>
  • Loading branch information
5 people authored Aug 6, 2024
1 parent 7fcb464 commit 50efd89
Show file tree
Hide file tree
Showing 17 changed files with 321 additions and 136 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
registry-url: https://registry.npmjs.org/
- run: yarn
- run: yarn build
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
# https://github.com/actions/setup-node
- uses: actions/setup-node@v2-beta
with:
node-version: '16'
node-version: 18
cache: 'yarn'

- run: yarn install
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
registry-url: https://registry.npmjs.org/
- run: yarn
- run: yarn build
Expand Down
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
3 changes: 2 additions & 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 Expand Up @@ -62,6 +62,7 @@
},
"dependencies": {
"@flashbots/ethers-provider-bundle": "0.5.0",
"axios": "^1.6.7",
"chalk": "4.1.1",
"dotenv": "16.0.1",
"ethers": "5.6.9",
Expand Down
73 changes: 0 additions & 73 deletions src/broadcastors/flashbotsBroadcastor.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/broadcastors/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './flashbotsBroadcastor';
export * from './privateBroadcastor';
export * from './mempoolBroadcastor';
export * from './stealthBroadcastor';
8 changes: 6 additions & 2 deletions 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 Expand Up @@ -58,7 +58,11 @@ export class MempoolBroadcastor {
await jobContract.callStatic[workMethod](...workArguments);
} catch (error: unknown) {
if (error instanceof Error) {
console.log(`Static call failed with ${error.message}`);
console.log(
`Static call failed. Job contract: ${jobContract.address}. Work method: ${workMethod}. Work arguments: ${[
...workArguments,
].join(', ')}. Error message: ${error.message}`
);
}
return;
}
Expand Down
81 changes: 81 additions & 0 deletions src/broadcastors/privateBroadcastor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { getMainnetGasType2Parameters, populateTx, sendBundle } from '../transactions';
import type { TransactionRequest } from '@ethersproject/abstract-provider';
import { Wallet, Overrides, ethers } from 'ethers';
import { BroadcastorProps } from 'types';

/**
* @notice Creates and populate a private transaction to work a specific job
*
* @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.
* @param chainId The chainId.
*
*/
export class PrivateBroadcastor {
constructor(
public endpoints: string[],
public priorityFeeInWei: number,
public gasLimit: number,
public doStaticCall = true,
public chainId: number
) {}

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

if (this.doStaticCall) {
try {
await jobContract.callStatic[workMethod](...workArguments);
} catch (error: unknown) {
if (error instanceof Error) {
console.log(
`Static call failed. Job contract: ${jobContract.address}. Work method: ${workMethod}. Work arguments: ${[
...workArguments,
].join(', ')}. Error message: ${error.message}`
);
}
return;
}
}

const blocksAhead = 2;
const targetBlock = block.number + blocksAhead;

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

const txSigner = jobContract.signer as Wallet;

const currentNonce = await txSigner.getTransactionCount();

const options: Overrides = {
gasLimit: this.gasLimit,
nonce: currentNonce,
maxFeePerGas,
maxPriorityFeePerGas: priorityFee,
type: 2,
};

const tx: TransactionRequest = await populateTx({
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}`);

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

0 comments on commit 50efd89

Please sign in to comment.