Skip to content

Commit

Permalink
Merge pull request #76 from BootNodeDev/last-block-processed
Browse files Browse the repository at this point in the history
Add a way to process events from blocks in the past
  • Loading branch information
lmcorbalan authored Feb 18, 2025
2 parents 7a5a1c2 + e244e3d commit 95da9f9
Show file tree
Hide file tree
Showing 19 changed files with 2,834 additions and 2,268 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ web_modules/

# dotenv environment variable files
.env
.env.testnet
.env.mainnet
.env.development.local
.env.test.local
.env.production.local
Expand Down
4 changes: 4 additions & 0 deletions solidity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@
"run:deployToken": "dotenv-run-script deployToken",
"openOrder": "forge script script/OpenOrder.s.sol:OpenOrder -f $NETWORK --broadcast --verify --slow -vvv",
"run:openOrder": "dotenv-run-script openOrder",
"deployBatchOpen": "forge script script/DeployBatchOpen.s.sol:DeployBatchOpen -f $NETWORK --broadcast --verify --slow -vvv",
"run:deployBatchOpen": "dotenv-run-script deployBatchOpen",
"openBatchOrder": "forge script script/OpenBatchOrder.s.sol:OpenBatchOrder -f $NETWORK --broadcast --verify --slow -vvv",
"run:openBatchOrder": "dotenv-run-script openBatchOrder",
"enrollRouter": "forge script script/EnrollRouter.s.sol:EnrollRouter -f $NETWORK --broadcast --slow -vvv",
"run:enrollRouter": "dotenv-run-script enrollRouter"
}
Expand Down
44 changes: 44 additions & 0 deletions solidity/script/DeployBatchOpen.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.25 <0.9.0;

import { Script } from "forge-std/Script.sol";
import { console2 } from "forge-std/console2.sol";

import { TypeCasts } from "@hyperlane-xyz/libs/TypeCasts.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

import { Hyperlane7683 } from "../src/Hyperlane7683.sol";
import { OrderData, OrderEncoder } from "../src/libs/OrderEncoder.sol";

import {
OnchainCrossChainOrder
} from "../src/ERC7683/IERC7683.sol";

contract BatchOpen {
Hyperlane7683 public router;
constructor(address _router) {
router = Hyperlane7683(_router);
}

function openOrders(OnchainCrossChainOrder[] memory orders, address token, uint256 total) external {
ERC20(token).transferFrom(msg.sender, address(this), total);
ERC20(token).approve(address(router), total);
for (uint256 i = 0; i < orders.length; i++) {
router.open(orders[i]);
}
}
}

/// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting
contract DeployBatchOpen is Script {
function run() public {
uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PK");
address router = vm.envAddress("ROUTER_ADDRESS");

vm.startBroadcast(deployerPrivateKey);

new BatchOpen(router);

vm.stopBroadcast();
}
}
81 changes: 81 additions & 0 deletions solidity/script/OpenBatchOrder.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.25 <0.9.0;

import { Script } from "forge-std/Script.sol";
import { console2 } from "forge-std/console2.sol";

import { TypeCasts } from "@hyperlane-xyz/libs/TypeCasts.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

import { Hyperlane7683 } from "../src/Hyperlane7683.sol";
import { OrderData, OrderEncoder } from "../src/libs/OrderEncoder.sol";
import { BatchOpen } from "./DeployBatchOpen.s.sol";

import {
OnchainCrossChainOrder
} from "../src/ERC7683/IERC7683.sol";

/// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting
contract OpenBatchOrder is Script {
function run() public {
uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PK");

vm.startBroadcast(deployerPrivateKey);

address localRouter = vm.envAddress("ROUTER_ADDRESS");
address sender = vm.envAddress("ORDER_SENDER");
address recipient = vm.envAddress("ORDER_RECIPIENT");
address inputToken = vm.envAddress("ITT_INPUT");
address outputToken = vm.envAddress("ITT_OUTPUT");
uint256 amountIn = vm.envUint("AMOUNT_IN");
uint256 amountOut = vm.envUint("AMOUNT_OUT");
uint256 senderNonce = vm.envUint("SENDER_NONCE");
uint32 originDomain = Hyperlane7683(localRouter).localDomain();
uint256 destinationDomain = vm.envUint("DESTINATION_DOMAIN");
uint32 fillDeadline = type(uint32).max;
address batchOpen = vm.envAddress("BATCH_OPEN");

ERC20(inputToken).approve(batchOpen, amountIn * 3);

OrderData memory order = OrderData(
TypeCasts.addressToBytes32(sender),
TypeCasts.addressToBytes32(recipient),
TypeCasts.addressToBytes32(inputToken),
TypeCasts.addressToBytes32(outputToken),
amountIn,
amountOut,
senderNonce,
originDomain,
uint32(destinationDomain),
TypeCasts.addressToBytes32(localRouter),
fillDeadline,
new bytes(0)
);

OnchainCrossChainOrder[] memory onchainOrders = new OnchainCrossChainOrder[](3);

onchainOrders[0] = OnchainCrossChainOrder(
fillDeadline,
OrderEncoder.orderDataType(),
OrderEncoder.encode(order)
);

order.senderNonce = senderNonce + 1;
onchainOrders[1] = OnchainCrossChainOrder(
fillDeadline,
OrderEncoder.orderDataType(),
OrderEncoder.encode(order)
);

order.senderNonce = senderNonce + 2;
onchainOrders[2] = OnchainCrossChainOrder(
fillDeadline,
OrderEncoder.orderDataType(),
OrderEncoder.encode(order)
);

BatchOpen(batchOpen).openOrders(onchainOrders, inputToken, amountIn * 3);

vm.stopBroadcast();
}
}
2 changes: 1 addition & 1 deletion typescript/solver/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
typechain
local.db
**/local.db
10 changes: 8 additions & 2 deletions typescript/solver/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,14 @@ const main = async () => {
log.info("🙍 Intent Solver 📝");
log.info("Starting...");

// // TODO: implement a way to choose different listeners and fillers
const hyperlane7683Listener = solvers["hyperlane7683"].listener.create();
// TODO: implement a way to choose different listeners and fillers
// const ecoListener = solvers["eco"].listener.create();
// const ecoFiller = solvers["eco"].filler.create(multiProvider);

// ecoListener(ecoFiller);

const hyperlane7683Listener =
await solvers["hyperlane7683"].listener.create();
const hyperlane7683Filler =
solvers["hyperlane7683"].filler.create(multiProvider);

Expand Down
1 change: 1 addition & 0 deletions typescript/solver/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@hyperlane-xyz/registry": "^9.3.0",
"@hyperlane-xyz/sdk": "^8.6.1",
"@hyperlane-xyz/utils": "^8.6.1",
"@libsql/client": "^0.14.0",
"chalk": "^5.3.0",
"dotenv-flow": "^4.1.0",
"ethers": "^5.7.2",
Expand Down
11 changes: 8 additions & 3 deletions typescript/solver/solvers/BaseFiller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ type Metadata = {
protocolName: string;
};

type ParsedArgs = {
export type ParsedArgs = {
orderId: string;
senderAddress: string;
recipients: Array<{
Expand Down Expand Up @@ -46,7 +46,11 @@ export abstract class BaseFiller<
}

create() {
return async (parsedArgs: TParsedArgs, originChainName: string) => {
return async (
parsedArgs: TParsedArgs,
originChainName: string,
blockNumber: number,
) => {
const origin = await this.retrieveOriginInfo(parsedArgs, originChainName);
const target = await this.retrieveTargetInfo(parsedArgs);

Expand All @@ -67,7 +71,7 @@ export abstract class BaseFiller<
const { data } = intent;

try {
await this.fill(parsedArgs, data, originChainName);
await this.fill(parsedArgs, data, originChainName, blockNumber);

await this.settleOrder(parsedArgs, data);
} catch (error) {
Expand Down Expand Up @@ -130,6 +134,7 @@ export abstract class BaseFiller<
parsedArgs: TParsedArgs,
data: TIntentData,
originChainName: string,
blockNumber: number,
): Promise<void>;

protected async settleOrder(parsedArgs: TParsedArgs, data: TIntentData) {
Expand Down
94 changes: 74 additions & 20 deletions typescript/solver/solvers/BaseListener.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import type { Provider } from "@ethersproject/providers";
import { MultiProvider } from "@hyperlane-xyz/sdk";
import type { Contract, Signer } from "ethers";
import type { Contract, EventFilter, Signer } from "ethers";

import { chainMetadata } from "../config/chainMetadata.js";
import type { Logger } from "../logger.js";
import type { TypedEvent, TypedListener } from "../typechain/common.js";
import type { ParsedArgs } from "./BaseFiller.js";

export abstract class BaseListener<
TContract extends Contract,
TEvent extends TypedEvent,
TParsedArgs,
TParsedArgs extends ParsedArgs,
> {
protected constructor(
private readonly contractFactory: {
Expand All @@ -20,40 +21,93 @@ export abstract class BaseListener<
contracts: Array<{
address: string;
chainName: string;
initialBlock?: number;
processedIds?: string[];
}>;
protocolName: string;
},
private readonly log: Logger,
) {}

create() {
return (handler: (args: TParsedArgs, originChainName: string) => void) => {
return async (
handler: (
args: TParsedArgs,
originChainName: string,
blockNumber: number,
) => void,
) => {
const multiProvider = new MultiProvider(chainMetadata);

this.metadata.contracts.forEach(({ address, chainName }) => {
const provider = multiProvider.getProvider(chainName);
const contract = this.contractFactory.connect(address, provider);
const filter = contract.filters[this.eventName]();
this.metadata.contracts.forEach(
async ({ address, chainName, initialBlock, processedIds }) => {
const provider = multiProvider.getProvider(chainName);
const contract = this.contractFactory.connect(address, provider);
const filter = contract.filters[this.eventName]();

const listener: TypedListener<TEvent> = (...args) => {
handler(this.parseEventArgs(args), chainName);
};
const listener: TypedListener<TEvent> = (...args) => {
handler(
this.parseEventArgs(args),
chainName,
args[args.length - 1].blockNumber,
);
};

contract.on(filter, listener);
const latest = (await provider.getBlockNumber()) - 1;
if (initialBlock && latest > initialBlock) {
this.processPrevBlocks(
chainName,
contract,
filter,
initialBlock,
latest,
handler,
processedIds,
);
}

contract.provider.getNetwork().then((network) => {
this.log.info({
msg: "Listener started",
event: this.eventName,
protocol: this.metadata.protocolName,
chainId: network.chainId,
chainName: chainName,
contract.on(filter, listener);

contract.provider.getNetwork().then((network) => {
this.log.info({
msg: "Listener started",
event: this.eventName,
protocol: this.metadata.protocolName,
chainId: network.chainId,
chainName: chainName,
});
});
});
});
},
);
};
}

protected async processPrevBlocks(
chainName: string,
contract: TContract,
filter: EventFilter,
from: number,
to: number,
handler: (
args: TParsedArgs,
originChainName: string,
blockNumber: number,
) => void,
processedIds?: string[],
) {
const pastEvents = await contract.queryFilter(filter, from, to);
for (let event of pastEvents) {
const parsedArgs = this.parseEventArgs((event as TEvent).args);
if (
event.blockNumber === from &&
processedIds?.includes(parsedArgs.orderId)
) {
continue;
}
await handler(parsedArgs, chainName, event.blockNumber);
}
}

protected abstract parseEventArgs(
args: Parameters<TypedListener<TEvent>>,
): TParsedArgs;
Expand Down
1 change: 1 addition & 0 deletions typescript/solver/solvers/eco/config/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const metadata: EcoMetadata = {
{
address: "0x734a3d5a8D691d9b911674E682De5f06517c79ec",
chainName: "optimismsepolia",
initialBlock: 123,
},
],
adapters: [
Expand Down
1 change: 1 addition & 0 deletions typescript/solver/solvers/eco/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const EcoMetadataSchema = z.object({
chainName: z.string().refine((name) => chainNames.includes(name), {
message: "Invalid chainName",
}),
initialBlock: z.number(),
}),
),
adapters: z.array(
Expand Down
6 changes: 6 additions & 0 deletions typescript/solver/solvers/hyperlane7683/config/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const metadata: Hyperlane7683Metadata = {
address: "0x9245A985d2055CeA7576B293Da8649bb6C5af9D0",
chainName: "form",
},

// testnet
// {
// address: "0x6d2175B89315A9EB6c7eA71fDE54Ac0f294aDC34",
Expand All @@ -52,6 +53,11 @@ const metadata: Hyperlane7683Metadata = {
// address: "0x6d2175B89315A9EB6c7eA71fDE54Ac0f294aDC34",
// chainName: "basesepolia",
// },
// {
// address: "0x6d2175B89315A9EB6c7eA71fDE54Ac0f294aDC34",
// chainName: "basesepolia",
// initialBlock: 21491220,
// }
],
};

Expand Down
Loading

0 comments on commit 95da9f9

Please sign in to comment.