Skip to content

Commit f30b31d

Browse files
committed
feat: add rules to decide wether an intent is worth fulfilling
1 parent 5bd996f commit f30b31d

File tree

5 files changed

+232
-168
lines changed

5 files changed

+232
-168
lines changed

typescript/solver/solvers/BaseFiller.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,31 @@ type ParsedArgs = {
1919
}>;
2020
};
2121

22+
export type Rule<
23+
TMetadata extends Metadata,
24+
TParsedArgs extends ParsedArgs,
25+
TIntentData extends unknown,
26+
> = (
27+
parsedArgs: TParsedArgs,
28+
context: BaseFiller<TMetadata, TParsedArgs, TIntentData>,
29+
) => Promise<Result<string>>;
30+
2231
export abstract class BaseFiller<
2332
TMetadata extends Metadata,
2433
TParsedArgs extends ParsedArgs,
2534
TIntentData extends unknown,
2635
> {
36+
rules: Array<Rule<TMetadata, TParsedArgs, TIntentData>> = [];
37+
2738
protected constructor(
2839
readonly multiProvider: MultiProvider,
2940
readonly allowBlockLists: GenericAllowBlockLists,
3041
readonly metadata: TMetadata,
3142
readonly log: Logger,
32-
) {}
43+
rules?: Array<Rule<TMetadata, TParsedArgs, TIntentData>>,
44+
) {
45+
if (rules) this.rules = rules;
46+
}
3347

3448
create() {
3549
return async (parsedArgs: TParsedArgs, originChainName: string) => {
@@ -84,9 +98,28 @@ export abstract class BaseFiller<
8498
};
8599
}
86100

101+
const result = await this.evaluateRules(parsedArgs);
102+
103+
if (!result.success) {
104+
throw new Error(result.error);
105+
}
106+
87107
return { error: "Not implemented", success: false };
88108
}
89109

110+
protected async evaluateRules(parsedArgs: TParsedArgs) {
111+
let result: Result<string> = { success: true, data: "No rules" };
112+
113+
for (const rule of this.rules) {
114+
result = await rule(parsedArgs, this);
115+
116+
if (!result.success) {
117+
break;
118+
}
119+
}
120+
121+
return result;
122+
}
90123

91124
protected abstract fill(
92125
parsedArgs: TParsedArgs,

typescript/solver/solvers/eco/filler.ts

Lines changed: 100 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,27 @@ import { chainIds, chainIdsToName } from "../../config/index.js";
88
import { Erc20__factory } from "../../typechain/factories/contracts/Erc20__factory.js";
99
import { EcoAdapter__factory } from "../../typechain/factories/eco/contracts/EcoAdapter__factory.js";
1010
import { BaseFiller } from "../BaseFiller.js";
11-
import { retrieveOriginInfo, retrieveTargetInfo } from "../utils.js";
11+
import {
12+
retrieveOriginInfo,
13+
retrieveTargetInfo,
14+
retrieveTokenBalance,
15+
} from "../utils.js";
1216
import { allowBlockLists, metadata } from "./config/index.js";
1317
import type { EcoMetadata, IntentData, ParsedArgs } from "./types.js";
1418
import { log, withdrawRewards } from "./utils.js";
1519

16-
type Metadata = {
20+
export type Metadata = {
1721
adapters: { [chainId: string]: EcoMetadata["adapters"][number] };
1822
protocolName: EcoMetadata["protocolName"];
1923
};
2024

25+
export type EcoRule = EcoFiller["rules"][number];
26+
2127
export class EcoFiller extends BaseFiller<Metadata, ParsedArgs, IntentData> {
22-
constructor(multiProvider: MultiProvider) {
28+
constructor(
29+
multiProvider: MultiProvider,
30+
rules?: BaseFiller<Metadata, ParsedArgs, IntentData>["rules"],
31+
) {
2332
const { adapters, protocolName } = metadata;
2433
const ecoFillerMetadata = {
2534
adapters: adapters.reduce<{
@@ -34,7 +43,7 @@ export class EcoFiller extends BaseFiller<Metadata, ParsedArgs, IntentData> {
3443
protocolName,
3544
};
3645

37-
super(multiProvider, allowBlockLists, ecoFillerMetadata, log);
46+
super(multiProvider, allowBlockLists, ecoFillerMetadata, log, rules);
3847
}
3948

4049
protected retrieveOriginInfo(parsedArgs: ParsedArgs, chainName: string) {
@@ -82,62 +91,8 @@ export class EcoFiller extends BaseFiller<Metadata, ParsedArgs, IntentData> {
8291
};
8392
}
8493

85-
await super.prepareIntent(parsedArgs);
86-
8794
try {
88-
const erc20Interface = Erc20__factory.createInterface();
89-
90-
const requiredAmountsByTarget = parsedArgs._targets.reduce<{
91-
[tokenAddress: string]: BigNumber;
92-
}>((acc, target, index) => {
93-
const [, amount] = erc20Interface.decodeFunctionData(
94-
"transfer",
95-
parsedArgs._data[index],
96-
) as [unknown, BigNumber];
97-
98-
acc[target] ||= Zero;
99-
acc[target] = acc[target].add(amount);
100-
101-
return acc;
102-
}, {});
103-
104-
const fillerAddress = await this.multiProvider.getSignerAddress(
105-
adapter.chainName,
106-
);
107-
const signer = this.multiProvider.getSigner(adapter.chainName);
108-
109-
const areTargetFundsAvailable = await Promise.all(
110-
Object.entries(requiredAmountsByTarget).map(
111-
async ([target, requiredAmount]) => {
112-
const erc20 = Erc20__factory.connect(target, signer);
113-
114-
const balance = await erc20.balanceOf(fillerAddress);
115-
return balance.gte(requiredAmount);
116-
},
117-
),
118-
);
119-
120-
if (!areTargetFundsAvailable.every(Boolean)) {
121-
return { error: "Not enough tokens", success: false };
122-
}
123-
124-
this.log.debug({
125-
msg: "Approving tokens",
126-
protocolName: this.metadata.protocolName,
127-
intentHash: parsedArgs._hash,
128-
adapterAddress: adapter.address,
129-
});
130-
131-
await Promise.all(
132-
Object.entries(requiredAmountsByTarget).map(
133-
async ([target, requiredAmount]) => {
134-
const erc20 = Erc20__factory.connect(target, signer);
135-
136-
const tx = await erc20.approve(adapter.address, requiredAmount);
137-
await tx.wait();
138-
},
139-
),
140-
);
95+
await super.prepareIntent(parsedArgs);
14196

14297
return { data: { adapter }, success: true };
14398
} catch (error: any) {
@@ -158,6 +113,41 @@ export class EcoFiller extends BaseFiller<Metadata, ParsedArgs, IntentData> {
158113
intent: `${this.metadata.protocolName}-${parsedArgs._hash}`,
159114
});
160115

116+
this.log.debug({
117+
msg: "Approving tokens",
118+
protocolName: this.metadata.protocolName,
119+
intentHash: parsedArgs._hash,
120+
adapterAddress: data.adapter.address,
121+
});
122+
123+
const erc20Interface = Erc20__factory.createInterface();
124+
125+
const requiredAmountsByTarget = parsedArgs._targets.reduce<{
126+
[tokenAddress: string]: BigNumber;
127+
}>((acc, target, index) => {
128+
const [, amount] = erc20Interface.decodeFunctionData(
129+
"transfer",
130+
parsedArgs._data[index],
131+
) as [unknown, BigNumber];
132+
133+
acc[target] ||= Zero;
134+
acc[target] = acc[target].add(amount);
135+
136+
return acc;
137+
}, {});
138+
139+
const signer = this.multiProvider.getSigner(data.adapter.chainName);
140+
await Promise.all(
141+
Object.entries(requiredAmountsByTarget).map(
142+
async ([target, requiredAmount]) => {
143+
const erc20 = Erc20__factory.connect(target, signer);
144+
145+
const tx = await erc20.approve(data.adapter.address, requiredAmount);
146+
await tx.wait();
147+
},
148+
),
149+
);
150+
161151
const _chainId = parsedArgs._destinationChain.toString();
162152

163153
const filler = this.multiProvider.getSigner(_chainId);
@@ -210,5 +200,54 @@ export class EcoFiller extends BaseFiller<Metadata, ParsedArgs, IntentData> {
210200
}
211201
}
212202

213-
export const create = (multiProvider: MultiProvider) =>
214-
new EcoFiller(multiProvider).create();
203+
const enoughBalanceOnDestination: EcoRule = async (parsedArgs, context) => {
204+
const erc20Interface = Erc20__factory.createInterface();
205+
206+
const requiredAmountsByTarget = parsedArgs._targets.reduce<{
207+
[tokenAddress: string]: BigNumber;
208+
}>((acc, target, index) => {
209+
const [, amount] = erc20Interface.decodeFunctionData(
210+
"transfer",
211+
parsedArgs._data[index],
212+
) as [unknown, BigNumber];
213+
214+
acc[target] ||= Zero;
215+
acc[target] = acc[target].add(amount);
216+
217+
return acc;
218+
}, {});
219+
220+
const chainId = parsedArgs._destinationChain.toString();
221+
const fillerAddress = await context.multiProvider.getSignerAddress(chainId);
222+
const provider = context.multiProvider.getProvider(chainId);
223+
224+
for (const tokenAddress in requiredAmountsByTarget) {
225+
const balance = await retrieveTokenBalance(
226+
tokenAddress,
227+
fillerAddress,
228+
provider,
229+
);
230+
231+
if (balance.lt(requiredAmountsByTarget[tokenAddress])) {
232+
return {
233+
error: `Insufficient balance on destination chain ${chainId} for token ${tokenAddress}`,
234+
success: false,
235+
};
236+
}
237+
}
238+
239+
return { data: "Enough tokens to fulfill the intent", success: true };
240+
};
241+
242+
export const create = (
243+
multiProvider: MultiProvider,
244+
rules?: EcoFiller["rules"],
245+
keepBaseRules = true,
246+
) => {
247+
const customRules = rules ?? [];
248+
249+
return new EcoFiller(
250+
multiProvider,
251+
keepBaseRules ? [enoughBalanceOnDestination, ...customRules] : customRules,
252+
).create();
253+
};

0 commit comments

Comments
 (0)