Skip to content

Commit 8307e53

Browse files
authored
Merge pull request #57 from BootNodeDev/multichain-listeners
feat: add support for listening on multiple chains
2 parents ec24717 + 7c79c0d commit 8307e53

File tree

11 files changed

+107
-103
lines changed

11 files changed

+107
-103
lines changed

typescript/solver/solvers/BaseListener.ts

Lines changed: 26 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -17,47 +17,38 @@ export abstract class BaseListener<
1717
},
1818
private readonly eventName: Extract<keyof TContract["filters"], string>,
1919
private readonly metadata: {
20-
address: string;
21-
chainName: string;
20+
contracts: Array<{
21+
address: string;
22+
chainName: string;
23+
}>;
2224
protocolName: string;
2325
},
2426
private readonly log: Logger,
2527
) {}
2628

27-
protected setup(): TContract {
28-
if (!this.metadata.address || !this.metadata.chainName) {
29-
throw new Error("Origin contract information must be provided");
30-
}
31-
32-
if (!this.metadata.protocolName) {
33-
throw new Error("Solver name must be provided");
34-
}
35-
36-
const multiProvider = new MultiProvider(chainMetadata);
37-
const provider = multiProvider.getProvider(this.metadata.chainName);
38-
39-
return this.contractFactory.connect(this.metadata.address, provider);
40-
}
41-
4229
create() {
43-
const contract = this.setup();
44-
45-
return (handler: (args: TParsedArgs) => void) => {
46-
const filter = contract.filters[this.eventName]();
47-
48-
const listener: TypedListener<TEvent> = (...args) => {
49-
handler(this.parseEventArgs(args));
50-
};
51-
52-
contract.on(filter, listener);
53-
54-
contract.provider.getNetwork().then((network) => {
55-
this.log.info({
56-
msg: "Listener started",
57-
event: this.eventName,
58-
protocol: this.metadata.protocolName,
59-
chainId: network.chainId,
60-
chainName: this.metadata.chainName,
30+
return (handler: (args: TParsedArgs, originChainName: string) => void) => {
31+
const multiProvider = new MultiProvider(chainMetadata);
32+
33+
this.metadata.contracts.forEach(({ address, chainName }) => {
34+
const provider = multiProvider.getProvider(chainName);
35+
const contract = this.contractFactory.connect(address, provider);
36+
const filter = contract.filters[this.eventName]();
37+
38+
const listener: TypedListener<TEvent> = (...args) => {
39+
handler(this.parseEventArgs(args), chainName);
40+
};
41+
42+
contract.on(filter, listener);
43+
44+
contract.provider.getNetwork().then((network) => {
45+
this.log.info({
46+
msg: "Listener started",
47+
event: this.eventName,
48+
protocol: this.metadata.protocolName,
49+
chainId: network.chainId,
50+
chainName: chainName,
51+
});
6152
});
6253
});
6354
};

typescript/solver/solvers/eco/config/metadata.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import { type EcoMetadata, EcoMetadataSchema } from "../types.js";
22

33
const metadata: EcoMetadata = {
44
protocolName: "Eco",
5-
intentSource: {
6-
address: "0x734a3d5a8D691d9b9,11674E682De5f06517c79ec",
7-
chainName: "optimismsepolia",
8-
},
5+
intentSources: [
6+
{
7+
address: "0x734a3d5a8D691d9b9,11674E682De5f06517c79ec",
8+
chainName: "optimismsepolia",
9+
},
10+
],
911
adapters: [
1012
{
1113
address: "0x218FB5210d4eE248f046F3EC8B5Dd1c7Bc0756e5",

typescript/solver/solvers/eco/filler.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@ import {
2222
} from "../../config/index.js";
2323

2424
export const create = (multiProvider: MultiProvider) => {
25-
const { adapters, intentSource, protocolName } = setup();
25+
const { adapters, protocolName } = setup();
2626

27-
return async function eco(intent: IntentCreatedEventObject) {
27+
return async function eco(
28+
intent: IntentCreatedEventObject,
29+
originChainName: string,
30+
) {
2831
const origin = await retrieveOriginInfo(
2932
intent,
30-
intentSource,
33+
originChainName,
3134
multiProvider,
3235
);
3336
const target = await retrieveTargetInfo(intent, adapters, multiProvider);
@@ -56,12 +59,12 @@ export const create = (multiProvider: MultiProvider) => {
5659
await fill(
5760
intent,
5861
result.data.adapter,
59-
intentSource,
62+
originChainName,
6063
multiProvider,
6164
protocolName,
6265
);
6366

64-
await withdrawRewards(intent, intentSource, multiProvider, protocolName);
67+
await withdrawRewards(intent, originChainName, multiProvider, protocolName);
6568
};
6669
};
6770

@@ -181,7 +184,7 @@ async function prepareIntent(
181184
async function fill(
182185
intent: IntentCreatedEventObject,
183186
adapter: EcoMetadata["adapters"][number],
184-
intentSource: EcoMetadata["intentSource"],
187+
originChainName: string,
185188
multiProvider: MultiProvider,
186189
protocolName: string,
187190
): Promise<void> {
@@ -195,19 +198,17 @@ async function fill(
195198
const filler = multiProvider.getSigner(_chainId);
196199
const ecoAdapter = EcoAdapter__factory.connect(adapter.address, filler);
197200

198-
const claimantAddress = await multiProvider.getSignerAddress(
199-
intentSource.chainName,
200-
);
201+
const claimantAddress = await multiProvider.getSignerAddress(originChainName);
201202

202203
const { _targets, _data, _expiryTime, nonce, _hash, _prover } = intent;
203204
const value = await ecoAdapter.fetchFee(
204-
chainIds[intentSource.chainName],
205+
chainIds[originChainName],
205206
[_hash],
206207
[claimantAddress],
207208
_prover,
208209
);
209210
const tx = await ecoAdapter.fulfillHyperInstant(
210-
chainIds[intentSource.chainName],
211+
chainIds[originChainName],
211212
_targets,
212213
_data,
213214
_expiryTime,

typescript/solver/solvers/eco/listener.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,8 @@ export class EcoListener extends BaseListener<
1515
IntentCreatedEventObject
1616
> {
1717
constructor() {
18-
const {
19-
intentSource: { address, chainName },
20-
protocolName,
21-
} = metadata;
22-
const ecoMetadata = { address, chainName, protocolName };
18+
const { intentSources, protocolName } = metadata;
19+
const ecoMetadata = { contracts: intentSources, protocolName };
2320

2421
super(IntentSource__factory, "IntentCreated", ecoMetadata, log);
2522
}

typescript/solver/solvers/eco/types.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import { chainNames } from "../../config/index.js";
33

44
export const EcoMetadataSchema = z.object({
55
protocolName: z.string(),
6-
intentSource: z.object({
7-
address: z.string(),
8-
chainName: z.string().refine((name) => chainNames.includes(name), {
9-
message: "Invalid chainName",
6+
intentSources: z.array(
7+
z.object({
8+
address: z.string(),
9+
chainName: z.string().refine((name) => chainNames.includes(name), {
10+
message: "Invalid chainName",
11+
}),
1012
}),
11-
}),
13+
),
1214
adapters: z.array(
1315
z.object({
1416
address: z.string(),

typescript/solver/solvers/eco/utils.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const log = createLogger(metadata.protocolName);
1515

1616
export async function withdrawRewards(
1717
intent: IntentCreatedEventObject,
18-
intentSource: EcoMetadata["intentSource"],
18+
originChainName: string,
1919
multiProvider: MultiProvider,
2020
protocolName: string,
2121
) {
@@ -25,7 +25,7 @@ export async function withdrawRewards(
2525
});
2626

2727
const { _hash, _prover } = intent;
28-
const signer = multiProvider.getSigner(intentSource.chainName);
28+
const signer = multiProvider.getSigner(originChainName);
2929
const claimantAddress = await signer.getAddress();
3030
const prover = HyperProver__factory.connect(_prover, signer);
3131

@@ -36,13 +36,16 @@ export async function withdrawRewards(
3636
log.debug(`${protocolName} - Intent proven: ${_hash}`);
3737

3838
const settler = IntentSource__factory.connect(
39-
intentSource.address,
39+
metadata.intentSources.find(
40+
(source) => source.chainName == originChainName,
41+
)!.address,
4042
signer,
4143
);
4244
const tx = await settler.withdrawRewards(_hash);
4345
const receipt = await tx.wait();
44-
const baseUrl = multiProvider.getChainMetadata(intentSource.chainName)
45-
.blockExplorers?.[0].url;
46+
const baseUrl =
47+
multiProvider.getChainMetadata(originChainName).blockExplorers?.[0]
48+
.url;
4649

4750
const txInfo = baseUrl
4851
? `${baseUrl}/tx/${receipt.transactionHash}`
@@ -63,14 +66,14 @@ export async function withdrawRewards(
6366

6467
export async function retrieveOriginInfo(
6568
intent: IntentCreatedEventObject,
66-
intentSource: EcoMetadata["intentSource"],
69+
originChainName: string,
6770
multiProvider: MultiProvider,
6871
): Promise<Array<string>> {
6972
const originInfo = await Promise.all(
7073
intent._rewardTokens.map(async (tokenAddress, index) => {
7174
const erc20 = Erc20__factory.connect(
7275
tokenAddress,
73-
multiProvider.getProvider(intentSource.chainName),
76+
multiProvider.getProvider(originChainName),
7477
);
7578
const [decimals, symbol] = await Promise.all([
7679
erc20.decimals(),
@@ -84,7 +87,7 @@ export async function retrieveOriginInfo(
8487

8588
return originInfo.map(
8689
({ amount, decimals, symbol }) =>
87-
`${formatUnits(amount, decimals)} ${symbol} in on ${intentSource.chainName}`,
90+
`${formatUnits(amount, decimals)} ${symbol} in on ${originChainName}`,
8891
);
8992
}
9093

typescript/solver/solvers/hyperlane7683/config/metadata.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,24 @@ import {
55

66
const metadata: Hyperlane7683Metadata = {
77
protocolName: "Hyperlane7683",
8-
originSettler: {
9-
address: "0x376dc8E71A223Af488D885ce04A7021f32C2D1e0",
10-
chainName: "optimismsepolia",
11-
},
8+
originSettlers: [
9+
{
10+
address: "0xe0c8f83bA0686FDF1a76AF0cC202181AEaA25a03",
11+
chainName: "optimismsepolia",
12+
},
13+
{
14+
address: "0xe0c8f83bA0686FDF1a76AF0cC202181AEaA25a03",
15+
chainName: "arbitrumsepolia",
16+
},
17+
{
18+
address: "0xe0c8f83bA0686FDF1a76AF0cC202181AEaA25a03",
19+
chainName: "sepolia",
20+
},
21+
{
22+
address: "0xe0c8f83bA0686FDF1a76AF0cC202181AEaA25a03",
23+
chainName: "basesepolia",
24+
},
25+
],
1226
};
1327

1428
Hyperlane7683MetadataSchema.parse(metadata);

typescript/solver/solvers/hyperlane7683/filler.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,13 @@ import { allowBlockLists, metadata } from "./config/index.js";
2424
import { chainIdsToName, isAllowedIntent } from "../../config/index.js";
2525

2626
export const create = (multiProvider: MultiProvider) => {
27-
const { originSettler, protocolName } = setup();
27+
const { protocolName } = setup();
2828

29-
return async function hyperlane7683({
30-
orderId,
31-
resolvedOrder,
32-
}: OpenEventArgs) {
33-
const origin = await retrieveOriginInfo(
34-
resolvedOrder,
35-
originSettler,
36-
multiProvider,
37-
);
29+
return async function hyperlane7683(
30+
{ orderId, resolvedOrder }: OpenEventArgs,
31+
_originChainName: string,
32+
) {
33+
const origin = await retrieveOriginInfo(resolvedOrder, multiProvider);
3834
const target = await retrieveTargetInfo(resolvedOrder, multiProvider);
3935

4036
log.info({

typescript/solver/solvers/hyperlane7683/listener.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,8 @@ export class Hyperlane7683Listener extends BaseListener<
1515
OpenEventArgs
1616
> {
1717
constructor() {
18-
const {
19-
originSettler: { address, chainName },
20-
protocolName,
21-
} = metadata;
22-
const hyperlane7683Metadata = { address, chainName, protocolName };
18+
const { originSettlers, protocolName } = metadata;
19+
const hyperlane7683Metadata = { contracts: originSettlers, protocolName };
2320

2421
super(Hyperlane7683__factory, "Open", hyperlane7683Metadata, log);
2522
}

typescript/solver/solvers/hyperlane7683/types.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@ export type IntentData = {
3737

3838
export const Hyperlane7683MetadataSchema = z.object({
3939
protocolName: z.string(),
40-
originSettler: z.object({
41-
address: z.string(),
42-
chainName: z.string().refine((name) => chainNames.includes(name), {
43-
message: "Invalid chainName",
40+
originSettlers: z.array(
41+
z.object({
42+
address: z.string(),
43+
chainName: z.string().refine((name) => chainNames.includes(name), {
44+
message: "Invalid chainName",
45+
}),
4446
}),
45-
}),
47+
),
4648
});
4749

4850
export type Hyperlane7683Metadata = z.infer<typeof Hyperlane7683MetadataSchema>;

0 commit comments

Comments
 (0)