Skip to content

Add CompactX solver #125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 41 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
b470b0a
feat: websocket listener
fernandomg Mar 11, 2025
57b9501
feat(compactX): add solver config
fernandomg Mar 12, 2025
7df218f
feat(compactX): add rules directory
fernandomg Mar 12, 2025
00b637b
feat: add contract abi
fernandomg Mar 12, 2025
c6fa919
feat: extend intentSources to support multiple origins
fernandomg Mar 12, 2025
3b6e369
chore: prettier
fernandomg Mar 12, 2025
aa34806
fix: type
fernandomg Mar 12, 2025
0320990
feat(compactX): listener minimum implementation
fernandomg Mar 12, 2025
8f60665
chore: prettier
fernandomg Mar 12, 2025
0cd858f
feat(compactX): mvp (no fill)
fernandomg Mar 12, 2025
4a7c251
fix: websocket listener shutdown
lmcorbalan Mar 13, 2025
43abd77
wip: add CompactX filler logic
lmcorbalan Mar 14, 2025
691d327
refactor: rename CompactSchema to CompactMessageSchema
fernandomg Mar 17, 2025
26c3e99
refactor: replace viem with ethers equivalences
fernandomg Mar 17, 2025
38aae9c
fix: update signature to latest changes
fernandomg Mar 17, 2025
efa333a
chore: prettier
fernandomg Mar 17, 2025
e239f75
wip: fill logic
lmcorbalan Mar 17, 2025
c0f8edc
feat(compactX): WIP filler
fernandomg Mar 17, 2025
2dfeea0
chore: prettier
fernandomg Mar 17, 2025
a466302
feat: less agressive price polling
fernandomg Mar 18, 2025
e76c15a
fix: be specific on the enncode data parameters
fernandomg Mar 18, 2025
ec6d2a4
refactor: comapctX filler
fernandomg Mar 19, 2025
27cf06a
fix: properly identify native tokens
fernandomg Mar 19, 2025
0f0bc6a
refactor(compactX): organize filler flow
fernandomg Mar 19, 2025
d0b0d61
fix: minor errors preventing the build from working
lmcorbalan Mar 20, 2025
bb6d726
refactor(compactX): move util functions to utils file
fernandomg Mar 20, 2025
dc80dac
refactor(compactX): move constants to metadata
fernandomg Mar 20, 2025
276a4fc
refactor(compactX): TheCompactService
fernandomg Mar 20, 2025
15f3432
refactor(compactX): move buffer values to config/metadata
fernandomg Mar 20, 2025
e2481e8
feat(compactX): extract checks to rules and simplify
fernandomg Mar 20, 2025
16e0466
feat(compactX): extract verifySignatures rules
fernandomg Mar 20, 2025
667129e
feat(compactX): extract checkExpirations rule
fernandomg Mar 20, 2025
398970f
feat(compactX): extract arbiter and tribunal checks
fernandomg Mar 21, 2025
532bd38
feat(compactX): extract nonce check
fernandomg Mar 21, 2025
12f94d8
refactor(compactX): code reorg
fernandomg Mar 21, 2025
3b16be8
feat(compactX): move zod checks/parse into listener parser
fernandomg Mar 21, 2025
ccf1cd0
fix: wrong message
fernandomg Mar 21, 2025
1f4b5f8
feat: better handle errors
fernandomg Mar 21, 2025
630f2c2
chore(compactX): reduce TheCompact abi size
fernandomg Mar 21, 2025
f0ad3b0
chore: reduce noise
fernandomg Mar 21, 2025
745e2b6
reenable all solvers
fernandomg Mar 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 70 additions & 24 deletions typescript/solver/NonceKeeperWallet.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { BigNumber } from "@ethersproject/bignumber";
import { defaultPath, HDNode } from "@ethersproject/hdnode";
import type { Deferrable } from "@ethersproject/properties";
import type {
Expand Down Expand Up @@ -36,7 +37,7 @@ export class NonceKeeperWallet extends Wallet {
// this check is necessary in order to not generate new nonces when a tx is going to fail
await super.estimateGas(transaction);
} catch (error) {
checkError(error, {transaction});
checkError(error, { transaction });
}

if (transaction.nonce == null) {
Expand All @@ -48,6 +49,14 @@ export class NonceKeeperWallet extends Wallet {
return super.sendTransaction(transaction);
}

async estimateGas(
transaction: Deferrable<TransactionRequest>,
): Promise<BigNumber> {
return super
.estimateGas(transaction)
.catch((error) => checkError(error, { transaction }));
}

static override fromMnemonic(
mnemonic: string,
path?: string,
Expand All @@ -67,47 +76,84 @@ function checkError(error: any, params: any): any {
const transaction = params.transaction || params.signedTransaction;

let message = error.message;
if (error.code === Logger.errors.SERVER_ERROR && error.error && typeof(error.error.message) === "string") {
message = error.error.message;
} else if (typeof(error.body) === "string") {
message = error.body;
} else if (typeof(error.responseText) === "string") {
message = error.responseText;
if (
error.code === Logger.errors.SERVER_ERROR &&
error.error &&
typeof error.error.message === "string"
) {
message = error.error.message;
} else if (typeof error.body === "string") {
message = error.body;
} else if (typeof error.responseText === "string") {
message = error.responseText;
}
message = (message || "").toLowerCase();

// "insufficient funds for gas * price + value + cost(data)"
if (message.match(/insufficient funds|base fee exceeds gas limit/i)) {
ethersLogger.throwError("insufficient funds for intrinsic transaction cost", Logger.errors.INSUFFICIENT_FUNDS, {
error, transaction
});
ethersLogger.throwError(
"insufficient funds for intrinsic transaction cost",
Logger.errors.INSUFFICIENT_FUNDS,
{
error,
transaction,
},
);
}

// "nonce too low"
if (message.match(/nonce (is )?too low/i)) {
ethersLogger.throwError("nonce has already been used", Logger.errors.NONCE_EXPIRED, {
error, transaction
});
ethersLogger.throwError(
"nonce has already been used",
Logger.errors.NONCE_EXPIRED,
{
error,
transaction,
},
);
}

// "replacement transaction underpriced"
if (message.match(/replacement transaction underpriced|transaction gas price.*too low/i)) {
ethersLogger.throwError("replacement fee too low", Logger.errors.REPLACEMENT_UNDERPRICED, {
error, transaction
});
if (
message.match(
/replacement transaction underpriced|transaction gas price.*too low/i,
)
) {
ethersLogger.throwError(
"replacement fee too low",
Logger.errors.REPLACEMENT_UNDERPRICED,
{
error,
transaction,
},
);
}

// "replacement transaction underpriced"
if (message.match(/only replay-protected/i)) {
ethersLogger.throwError("legacy pre-eip-155 transactions not supported", Logger.errors.UNSUPPORTED_OPERATION, {
error, transaction
});
ethersLogger.throwError(
"legacy pre-eip-155 transactions not supported",
Logger.errors.UNSUPPORTED_OPERATION,
{
error,
transaction,
},
);
}

if (message.match(/gas required exceeds allowance|always failing transaction|execution reverted/)) {
ethersLogger.throwError("cannot estimate gas; transaction may fail or may require manual gas limit", Logger.errors.UNPREDICTABLE_GAS_LIMIT, {
error, transaction
});
if (
message.match(
/gas required exceeds allowance|always failing transaction|execution reverted/,
)
) {
ethersLogger.throwError(
"cannot estimate gas; transaction may fail or may require manual gas limit",
Logger.errors.UNPREDICTABLE_GAS_LIMIT,
{
error,
transaction,
},
);
}

throw error;
Expand Down
3 changes: 3 additions & 0 deletions typescript/solver/config/solvers.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
},
"hyperlane7683": {
"enabled": true
},
"compactX": {
"enabled": true
}
}
2 changes: 1 addition & 1 deletion typescript/solver/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { getMultiProvider } from "./solvers/utils.js";

const main = async () => {
const multiProvider = await getMultiProvider(chainMetadata).catch(
(error) => (log.error(error.reason ?? error.message), process.exit(1))
(error) => (log.error(error.reason ?? error.message), process.exit(1)),
);

log.info("🙍 Intent Solver 📝");
Expand Down
4 changes: 3 additions & 1 deletion typescript/solver/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@inquirer/prompts": "^7.2.0",
"@typechain/ethers-v5": "^11.1.2",
"@types/copyfiles": "^2",
"@types/ws": "^8",
"copyfiles": "^2.4.1",
"prettier": "^3.3.3",
"tsx": "^4.19.2",
Expand All @@ -45,6 +46,7 @@
"pino": "^9.5.0",
"pino-pretty": "^13.0.0",
"pino-socket": "^7.4.0",
"uniqolor": "^1.1.1"
"uniqolor": "^1.1.1",
"ws": "^8.18.1"
}
}
13 changes: 11 additions & 2 deletions typescript/solver/solvers/BaseFiller.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { BigNumber } from "@ethersproject/bignumber";
import type { MultiProvider } from "@hyperlane-xyz/sdk";
import type { Result } from "@hyperlane-xyz/utils";
import {
Expand Down Expand Up @@ -82,11 +83,19 @@ export abstract class BaseFiller<
await this.fill(parsedArgs, data, originChainName, blockNumber);

await this.settleOrder(parsedArgs, data, originChainName);
} catch (error) {
} catch (error: any) {
this.log.error({
msg: `Failed processing intent`,
intent: `${this.metadata.protocolName}-${parsedArgs.orderId}`,
error: JSON.stringify(error),
error: {
stack: error.stack,
details: JSON.stringify(error, (_, value) => {
if (value instanceof BigNumber || typeof value === "bigint") {
return value.toString();
}
return value;
}),
},
});
}
};
Expand Down
5 changes: 2 additions & 3 deletions typescript/solver/solvers/BaseListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export abstract class BaseListener<
confirmationBlocks,
),
pollInterval ?? this.defaultPollInterval,
)
),
);

contract.provider.getNetwork().then((network) => {
Expand All @@ -118,9 +118,8 @@ export abstract class BaseListener<
clearInterval(this.pollIntervals[i]);
}
this.pollIntervals = [];
}
};
};

}

protected async pollEvents(
Expand Down
13 changes: 5 additions & 8 deletions typescript/solver/solvers/SolverManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,29 @@ type SolverModule = {
create: () => Promise<ListenerFn>;
};
filler: {
create: (
multiProvider: MultiProvider,
rules?: any
) => FillerFn;
create: (multiProvider: MultiProvider, rules?: any) => FillerFn;
};
rules?: any;
};

type ListenerFn = <T>(
handler: (args: T, originChainName: string, blockNumber: number) => void
handler: (args: T, originChainName: string, blockNumber: number) => void,
) => ShutdownFn;

type ShutdownFn = () => void;

type FillerFn = <T>(
args: T,
originChainName: string,
blockNumber: number
blockNumber: number,
) => Promise<void>;

export class SolverManager {
private activeListeners: Array<() => void> = [];

constructor(
private readonly multiProvider: MultiProvider,
private readonly log: Logger
private readonly log: Logger,
) {}

async initializeSolvers() {
Expand All @@ -47,7 +44,7 @@ export class SolverManager {
await this.initializeSolver(solverName as SolverName);
} catch (error: any) {
this.log.error(
`Failed to initialize solver ${solverName}: ${error.message}`
`Failed to initialize solver ${solverName}: ${error.message}`,
);
throw error;
}
Expand Down
Loading