Skip to content

Commit fdf9b44

Browse files
feat(hubpool): set pool rebalance route for token mappings (#49)
Signed-off-by: james-a-morris <[email protected]>
1 parent 00e747f commit fdf9b44

File tree

9 files changed

+10294
-7441
lines changed

9 files changed

+10294
-7441
lines changed

Diff for: packages/indexer-database/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"author": "",
2525
"license": "ISC",
2626
"dependencies": {
27-
"@across-protocol/sdk": "^3.1.30",
27+
"@across-protocol/sdk": "^3.2.2",
2828
"pg": "^8.4.0",
2929
"reflect-metadata": "^0.1.13",
3030
"superstruct": "2.0.3-1",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import {
2+
Column,
3+
CreateDateColumn,
4+
Entity,
5+
PrimaryGeneratedColumn,
6+
Unique,
7+
} from "typeorm";
8+
9+
@Entity({ schema: "evm" })
10+
@Unique("UK_setPoolRebalanceRoute_transactionHash_transactionIndex_logIndex", [
11+
"transactionHash",
12+
"transactionIndex",
13+
"logIndex",
14+
])
15+
export class SetPoolRebalanceRoute {
16+
@PrimaryGeneratedColumn()
17+
id: number;
18+
19+
@Column({ nullable: false })
20+
destinationChainId: number;
21+
22+
@Column({ nullable: false })
23+
l1Token: string;
24+
25+
@Column({ nullable: false })
26+
destinationToken: string;
27+
28+
@Column({ nullable: false })
29+
blockNumber: number;
30+
31+
@Column({ nullable: false })
32+
transactionHash: string;
33+
34+
@Column({ nullable: false })
35+
transactionIndex: number;
36+
37+
@Column({ nullable: false })
38+
logIndex: number;
39+
40+
@CreateDateColumn()
41+
createdAt: Date;
42+
}

Diff for: packages/indexer-database/src/entities/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export * from "./evm/ProposedRootBundle";
33
export * from "./evm/RootBundleCanceled";
44
export * from "./evm/RootBundleDisputed";
55
export * from "./evm/RootBundleExecuted";
6+
export * from "./evm/SetPoolRebalanceRoute";
67
// SpokePool
78
export * from "./evm/V3FundsDeposited";
89
export * from "./evm/FilledV3Relay";

Diff for: packages/indexer-database/src/main.ts

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const createDataSource = (config: DatabaseConfig): DataSource => {
2727
entities.RootBundleCanceled,
2828
entities.RootBundleDisputed,
2929
entities.RootBundleExecuted,
30+
entities.SetPoolRebalanceRoute,
3031
// SpokePool
3132
entities.ExecutedRelayerRefundRoot,
3233
entities.FilledV3Relay,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { MigrationInterface, QueryRunner } from "typeorm";
2+
3+
export class SetPoolRebalanceRoute1727374473839 implements MigrationInterface {
4+
name = "SetPoolRebalanceRoute1727374473839";
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
await queryRunner.query(
8+
`CREATE TABLE "evm"."set_pool_rebalance_route" (
9+
"id" SERIAL NOT NULL,
10+
"destinationChainId" integer NOT NULL,
11+
"l1Token" character varying NOT NULL,
12+
"destinationToken" character varying NOT NULL,
13+
"blockNumber" integer NOT NULL,
14+
"transactionHash" character varying NOT NULL,
15+
"transactionIndex" integer NOT NULL,
16+
"logIndex" integer NOT NULL,
17+
"createdAt" TIMESTAMP NOT NULL DEFAULT now(),
18+
CONSTRAINT "UK_setPoolRebalanceRoute_transactionHash_transactionIndex_logIndex" UNIQUE ("transactionHash", "transactionIndex", "logIndex"),
19+
CONSTRAINT "PK_93edcf0d94f29e5cd34513baf9d" PRIMARY KEY ("id")
20+
)
21+
`,
22+
);
23+
}
24+
25+
public async down(queryRunner: QueryRunner): Promise<void> {
26+
await queryRunner.query(`DROP TABLE "evm"."set_pool_rebalance_route"`);
27+
}
28+
}

Diff for: packages/indexer/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"dependencies": {
2424
"@across-protocol/constants": "^3.1.13",
2525
"@across-protocol/contracts": "^3.0.8",
26-
"@across-protocol/sdk": "^3.1.30",
26+
"@across-protocol/sdk": "^3.2.2",
2727
"@types/lodash": "^4.17.7",
2828
"bullmq": "^5.12.12",
2929
"ethers": "^5.7.2",

Diff for: packages/indexer/src/database/HubPoolRepository.ts

+93
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,97 @@ export class HubPoolRepository extends utils.BaseRepository {
7272
});
7373
await this.insert(entities.RootBundleExecuted, formattedEvents, throwError);
7474
}
75+
76+
public async formatAndSaveSetPoolRebalanceRouteEvents(
77+
setPoolRebalanceRouteEvents: (across.interfaces.DestinationTokenWithBlock & {
78+
l2ChainId: number;
79+
})[],
80+
throwError?: boolean,
81+
) {
82+
const formattedEvents = setPoolRebalanceRouteEvents.map((event) => {
83+
return {
84+
...event,
85+
destinationChainId: event.l2ChainId,
86+
destinationToken: event.l2Token,
87+
l1Token: event.l1Token,
88+
};
89+
});
90+
await this.insert(
91+
entities.SetPoolRebalanceRoute,
92+
formattedEvents,
93+
throwError,
94+
);
95+
}
96+
97+
/**
98+
* Finds the L1 token associated with an L2 token, closest in time (block number).
99+
* @param l2Token - The L2 token address.
100+
* @param chainId - The destination chain ID.
101+
* @param l1BlockNumber - Optional L1 block number to find the closest match less than or equal to this value.
102+
* @returns The L1 token address or undefined if not found.
103+
*/
104+
public async findL1TokenFromL2Token(
105+
l2Token: string,
106+
chainId: number,
107+
l1BlockNumber?: number,
108+
): Promise<string | undefined> {
109+
// Build the base query
110+
const queryBuilder = this.postgres
111+
.getRepository(entities.SetPoolRebalanceRoute)
112+
.createQueryBuilder("poolRebalanceRoot")
113+
.where("poolRebalanceRoot.destinationToken = :l2Token", { l2Token })
114+
.andWhere("poolRebalanceRoot.destinationChainId = :chainId", { chainId });
115+
116+
// If l1BlockNumber is provided, find the closest one that is <= the provided block number
117+
if (l1BlockNumber !== undefined) {
118+
queryBuilder.andWhere("poolRebalanceRoot.blockNumber <= :l1BlockNumber", {
119+
l1BlockNumber,
120+
});
121+
}
122+
123+
// Order by blockNumber descending to get the closest match
124+
queryBuilder.orderBy("poolRebalanceRoot.blockNumber", "DESC");
125+
126+
// Execute the query to find the closest matching entry
127+
const result = await queryBuilder.getOne();
128+
129+
// Return the L1 token if a result is found, otherwise undefined
130+
return result?.l1Token;
131+
}
132+
133+
/**
134+
* Finds the L2 token associated with an L1 token, closest in time (block number).
135+
* @param l1Token - The L1 token address.
136+
* @param chainId - The destination chain ID.
137+
* @param l1BlockNumber - Optional L1 block number to find the closest match less than or equal to this value.
138+
* @returns The L2 token address or undefined if not found.
139+
*/
140+
public async findL2TokenFromL1Token(
141+
l1Token: string,
142+
chainId: number,
143+
l1BlockNumber?: number,
144+
): Promise<string | undefined> {
145+
// Build the base query
146+
const queryBuilder = this.postgres
147+
.getRepository(entities.SetPoolRebalanceRoute)
148+
.createQueryBuilder("poolRebalanceRoot")
149+
.where("poolRebalanceRoot.l1Token = :l1Token", { l1Token })
150+
.andWhere("poolRebalanceRoot.destinationChainId = :chainId", { chainId });
151+
152+
// If l1BlockNumber is provided, find the closest one that is <= the provided block number
153+
if (l1BlockNumber !== undefined) {
154+
queryBuilder.andWhere("poolRebalanceRoot.blockNumber <= :l1BlockNumber", {
155+
l1BlockNumber,
156+
});
157+
}
158+
159+
// Order by blockNumber descending to get the closest match
160+
queryBuilder.orderBy("poolRebalanceRoot.blockNumber", "DESC");
161+
162+
// Execute the query to find the closest matching entry
163+
const result = await queryBuilder.getOne();
164+
165+
// Return the L2 token if a result is found, otherwise undefined
166+
return result?.destinationToken;
167+
}
75168
}

Diff for: packages/indexer/src/services/hubPoolIndexer.ts

+10
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ export class Indexer extends BaseIndexer {
163163
hubPoolClient.getCancelledRootBundlesInBlockRange(fromBlock, toBlock);
164164
const rootBundleDisputedEvents =
165165
hubPoolClient.getDisputedRootBundlesInBlockRange(fromBlock, toBlock);
166+
const setPoolRebalanceRouteEvents =
167+
hubPoolClient.getTokenMappingsModifiedInBlockRange(fromBlock, toBlock);
166168
// we do not have a block range query for executed root bundles
167169
const rootBundleExecutedEvents = hubPoolClient.getExecutedRootBundles();
168170

@@ -178,6 +180,7 @@ export class Indexer extends BaseIndexer {
178180
(event) =>
179181
event.blockNumber >= fromBlock && event.blockNumber <= toBlock,
180182
),
183+
setPoolRebalanceRouteEvents,
181184
};
182185
}
183186

@@ -188,13 +191,17 @@ export class Indexer extends BaseIndexer {
188191
rootBundleCanceledEvents: across.interfaces.CancelledRootBundle[];
189192
rootBundleDisputedEvents: across.interfaces.DisputedRootBundle[];
190193
rootBundleExecutedEvents: across.interfaces.ExecutedRootBundle[];
194+
setPoolRebalanceRouteEvents: (across.interfaces.DestinationTokenWithBlock & {
195+
l2ChainId: number;
196+
})[];
191197
}) {
192198
const { hubPoolRepository } = this;
193199
const {
194200
proposedRootBundleEvents,
195201
rootBundleCanceledEvents,
196202
rootBundleDisputedEvents,
197203
rootBundleExecutedEvents,
204+
setPoolRebalanceRouteEvents,
198205
} = params;
199206
await hubPoolRepository.formatAndSaveProposedRootBundleEvents(
200207
proposedRootBundleEvents,
@@ -208,5 +215,8 @@ export class Indexer extends BaseIndexer {
208215
await hubPoolRepository.formatAndSaveRootBundleExecutedEvents(
209216
rootBundleExecutedEvents,
210217
);
218+
await hubPoolRepository.formatAndSaveSetPoolRebalanceRouteEvents(
219+
setPoolRebalanceRouteEvents,
220+
);
211221
}
212222
}

0 commit comments

Comments
 (0)