Skip to content

Commit

Permalink
feat: oval discovery improvements
Browse files Browse the repository at this point in the history
Signed-off-by: Pablo Maldonado <[email protected]>
  • Loading branch information
md0x committed Jul 17, 2024
1 parent 4131b76 commit 9dfd12e
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 161 deletions.
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"devDependencies": {
"@typechain/ethers-v6": "^0.5.1",
"@types/bluebird": "^3.5.42",
"@types/chai": "^4.3.16",
"@types/eventsource": "^1.1.11",
"@types/mocha": "^10.0.7",
Expand All @@ -38,13 +39,15 @@
"chai": "^4.4.1",
"dotenv": "^16.3.1",
"mocha": "^10.6.0",
"sinon": "^18.0.0"
"sinon": "^18.0.0",
"typechain": "^8.3.2"
},
"scripts": {
"dev": "nodemon",
"build": "tsc",
"test": " mocha",
"start": "node ./out/index.js",
"lint": "prettier './**/*.ts' --write"
"lint": "prettier './**/*.ts' --write",
"generate-contract-types": "rm -rf src/contract-types && mkdir -p src/contract-types && typechain --target ethers-v6 --out-dir src/contract-types 'src/abi/*.json'"
}
}
}
8 changes: 7 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import {
isEthSendBundleParams,
sendBundle,
verifyBundleSignature,
getOvalRefundConfig,
OvalDiscovery
} from "./lib";

const app = express();
Expand All @@ -51,6 +53,10 @@ const { ovalConfigs, ovalConfigsShared } = env;
const walletManager = WalletManager.getInstance(provider);
walletManager.initialize(ovalConfigs, ovalConfigsShared);

// Initialize Oval discovery
const ovalDiscovery = OvalDiscovery.getInstance();
ovalDiscovery.initialize(provider);

// Start restful API server to listen for root inbound post requests.
app.post("/", async (req, res, next) => {
try {
Expand Down Expand Up @@ -167,7 +173,7 @@ app.post("/", async (req, res, next) => {
// Construct the inner bundle with call to Oval to unlock the latest value.
const unlockBundle = createUnlockLatestValueBundle(
unlock.signedUnlockTx,
ovalConfigs[unlock.ovalAddress].refundAddress,
getOvalRefundConfig(unlock.ovalAddress).refundAddress,
targetBlock,
);

Expand Down
9 changes: 6 additions & 3 deletions src/lib/bundleUtils.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Interface, Transaction, TransactionRequest, Wallet } from "ethers";
import express from "express";
import { FlashbotsBundleProvider } from "flashbots-ethers-v6-provider-bundle";
import { getBaseFee, getMaxBlockByChainId, getProvider } from "./helpers";
import { getBaseFee, getMaxBlockByChainId, getOvalRefundConfig, getProvider } from "./helpers";
import { WalletManager } from "./walletManager";

import MevShareClient, { BundleParams } from "@flashbots/mev-share-client";
import { JSONRPCID, createJSONRPCSuccessResponse } from "json-rpc-2.0";

import { ovalAbi } from "../abi";
import { OvalDiscovery } from "./";
import { env } from "./env";
import { Logger } from "./logging";
import { Refund } from "./types";
Expand Down Expand Up @@ -97,7 +98,7 @@ export const getUnlockBundlesFromOvalAddresses = async (
// Construct the inner bundle with call to Oval to unlock the latest value.
const unlockBundle = createUnlockLatestValueBundle(
unlock.signedUnlockTx,
ovalConfigs[ovalAddress].refundAddress,
getOvalRefundConfig(ovalAddress).refundAddress,
targetBlock,
);

Expand All @@ -116,8 +117,10 @@ export const findUnlock = async (
targetBlock: number,
req: express.Request,
) => {
const factoryInstances = OvalDiscovery.getInstance().getOvalFactoryInstances();

const unlocks = await Promise.all(
Object.keys(ovalConfigs).map(async (ovalAddress) =>
[...factoryInstances, ...Object.keys(ovalConfigs)].map(async (ovalAddress) =>
prepareUnlockTransaction(flashbotsBundleProvider, backrunTxs, targetBlock, ovalAddress, req),
),
);
Expand Down
2 changes: 2 additions & 0 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,5 @@ export const flashbotsSupportedNetworks: {
export const FLASHBOTS_SIGNATURE_HEADER = "x-flashbots-signature";

export const OVAL_ADDRESSES_HEADER = "x-oval-addresses";

export const FACTORIES_GENESIS_BLOCK = 20268518;
14 changes: 14 additions & 0 deletions src/lib/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ type EnvironmentVariables = {
[key: number]: number;
};
sharedWalletUsageCleanupInterval: number;
standardCoinbaseFactory: string;
standardChainlinkFactory: string;
standardChronicleFactory: string;
standardPythFactory: string;
defaultRefundAddress: string;
defaultRefundPercent: number;
ovalDiscoveryInterval: number;
};

export const env: EnvironmentVariables = {
Expand All @@ -73,4 +80,11 @@ export const env: EnvironmentVariables = {
[SEPOLIA_CHAIN_ID]: getInt(getEnvVar("SEPOLIA_BLOCK_OFFSET", "24")),
},
sharedWalletUsageCleanupInterval: getInt(getEnvVar("SHARED_WALLET_USAGE_CLEANUP_INTERVAL", "60")),
ovalDiscoveryInterval: getInt(getEnvVar("OVAL_DISCOVERY_INTERVAL", "180")),
standardCoinbaseFactory: getAddress(getEnvVar("STANDARD_COINBASE_FACTORY", "0x0e3d2b8220C0f74A287B85690a8cfeE5b45C2D44")),
standardChainlinkFactory: getAddress(getEnvVar("STANDARD_CHAINLINK_FACTORY", "0x6d0cbebdeBc5060E6264fcC497d5A277B5748Cf9")),
standardChronicleFactory: getAddress(getEnvVar("STANDARD_CHRONICLE_FACTORY", "0xE0225B5224512868814D9b10A14F705d99Ba0EdF")),
standardPythFactory: getAddress(getEnvVar("STANDARD_PYTH_FACTORY", "0x53A2a7C0cBb76B20782C6842A25876C5377B64e8")),
defaultRefundAddress: getAddress(getEnvVar("DEFAULT_REFUND_ADDRESS", "0x9Cc5b1bc0E1970D44B5Adc7ba51d76a5DD375434")),
defaultRefundPercent: getFloat(getEnvVar("DEFAULT_REFUND_PERCENT", fallback.refundPercent)),
};
22 changes: 19 additions & 3 deletions src/lib/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ import {
import { Request } from "express";
import { FlashbotsBundleProvider } from "flashbots-ethers-v6-provider-bundle";
import { JSONRPCRequest } from "json-rpc-2.0";
import { OvalDiscovery } from "./";
import { flashbotsSupportedNetworks, supportedNetworks } from "./constants";
import { env } from "./env";
import { Logger } from "./logging";
import { OvalAddressConfigList, OvalConfig, OvalConfigShared, OvalConfigs, OvalConfigsShared } from "./types";
import { OvalAddressConfigList, OvalConfig, OvalConfigShared, OvalConfigs, OvalConfigsShared, RefundConfig } from "./types";

export function getProvider() {
const network = new Network(supportedNetworks[env.chainId], env.chainId);
Expand Down Expand Up @@ -278,6 +279,21 @@ export function getOvalConfigsShared(input: string): OvalConfigsShared {
throw new Error(`Value "${input}" is valid JSON but is not OvalConfigsShared records`);
}

export const getOvalAddresses = (): string[] => {
const factoryInstances = OvalDiscovery.getInstance().getOvalFactoryInstances();
return [...factoryInstances, ...Object.keys(env.ovalConfigs)].map(getAddress);
};

export const getOvalRefundConfig = (ovalAddress: string): RefundConfig => {
if (env.ovalConfigs[ovalAddress]) {
return env.ovalConfigs[ovalAddress];
}
return {
refundAddress: getAddress(env.defaultRefundAddress),
refundPercent: env.defaultRefundPercent,
};
};

// Get OvalAddressConfigList from the header string or throw an error if the header is invalid.
export const getOvalHeaderConfigs = (
header: string | string[] | undefined,
Expand All @@ -292,10 +308,10 @@ export const getOvalHeaderConfigs = (
}
// Normalise addresses and check if they are valid Oval instances.
const normalisedAddresses = ovalAddresses.map(getAddress);
if (normalisedAddresses.some((ovalAddress) => !ovalConfigs[ovalAddress])) {
if (normalisedAddresses.some((ovalAddress) => !getOvalAddresses().includes(ovalAddress))) {
throw new Error(`Some addresses in "${header}" are not valid Oval instances`);
}
const uniqueRefundAddresses = new Set(normalisedAddresses.map((address) => ovalConfigs[address].refundAddress));
const uniqueRefundAddresses = new Set(normalisedAddresses.map((address) => getOvalRefundConfig(address).refundAddress));
if (uniqueRefundAddresses.size > 1) {
throw new Error(`Value "${header}" only supports a single refund address`);
}
Expand Down
1 change: 1 addition & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from "./logging";
export * from "./bundleUtils";
export * from "./constants";
export * from "./walletManager";
export * from "./ovalDiscovery";
Loading

0 comments on commit 9dfd12e

Please sign in to comment.