@hyperbridge/simplex 0.16.13 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/simplex.js +762 -261
- package/dist/bin/simplex.js.map +1 -1
- package/dist/index.cjs +333 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +58 -13
- package/dist/index.d.ts +58 -13
- package/dist/index.js +327 -70
- package/dist/index.js.map +1 -1
- package/dist/sqlite.cjs +8 -1
- package/dist/sqlite.cjs.map +1 -1
- package/dist/sqlite.d.cts +1 -1
- package/dist/sqlite.d.ts +1 -1
- package/dist/sqlite.js +8 -1
- package/dist/sqlite.js.map +1 -1
- package/dist/{types-BISgqJhM.d.cts → types-BPyUX8qu.d.cts} +7 -0
- package/dist/{types-BISgqJhM.d.ts → types-BPyUX8qu.d.ts} +7 -0
- package/dist/ui/assets/index-D8DLeEH0.js +106 -0
- package/dist/ui/index.html +1 -1
- package/package.json +3 -3
- package/dist/ui/assets/index-DShjZe_c.js +0 -106
package/dist/index.js
CHANGED
|
@@ -574,7 +574,8 @@ var BUILTIN_ASSETS = {
|
|
|
574
574
|
ZARP: { resolve: (r, chain) => r.getAssetBySymbol(chain, "ZARP") },
|
|
575
575
|
EURC: { resolve: (r, chain) => r.getAssetBySymbol(chain, "EURC") },
|
|
576
576
|
XSGD: { resolve: (r, chain) => r.getAssetBySymbol(chain, "XSGD") },
|
|
577
|
-
TRYB: { resolve: (r, chain) => r.getAssetBySymbol(chain, "TRYB") }
|
|
577
|
+
TRYB: { resolve: (r, chain) => r.getAssetBySymbol(chain, "TRYB") },
|
|
578
|
+
BRIDGE: { resolve: (r, chain) => r.getAssetBySymbol(chain, "BRIDGE") }
|
|
578
579
|
};
|
|
579
580
|
var USD_STABLE_SYMBOLS = /* @__PURE__ */ new Set(["USDC", "USDT", "DAI"]);
|
|
580
581
|
function normalizeSymbol(symbol) {
|
|
@@ -712,7 +713,7 @@ function assertPairSymbolsResolve(pairs, registry, chainNames) {
|
|
|
712
713
|
const pairSymbols = new Set(pairs.flatMap((pair) => [pair.token0, pair.token1]));
|
|
713
714
|
for (const pair of pairs) {
|
|
714
715
|
for (const symbol of [pair.token0, pair.token1]) {
|
|
715
|
-
const resolvesSomewhere = chainNames.some((
|
|
716
|
+
const resolvesSomewhere = chainNames.some((chainName2) => registry.getAddress(symbol, chainName2) !== null);
|
|
716
717
|
if (!resolvesSomewhere) {
|
|
717
718
|
throw new Error(
|
|
718
719
|
`pairs.${pair.token0}/${pair.token1}: '${symbol}' does not resolve to a deployed contract on any configured chain`
|
|
@@ -720,15 +721,15 @@ function assertPairSymbolsResolve(pairs, registry, chainNames) {
|
|
|
720
721
|
}
|
|
721
722
|
}
|
|
722
723
|
}
|
|
723
|
-
for (const
|
|
724
|
+
for (const chainName2 of chainNames) {
|
|
724
725
|
const addressOwner = /* @__PURE__ */ new Map();
|
|
725
726
|
for (const symbol of pairSymbols) {
|
|
726
|
-
const address = registry.getAddress(symbol,
|
|
727
|
+
const address = registry.getAddress(symbol, chainName2)?.toLowerCase();
|
|
727
728
|
if (!address) continue;
|
|
728
729
|
const owner = addressOwner.get(address);
|
|
729
730
|
if (owner && normalizeSymbol(owner) !== normalizeSymbol(symbol)) {
|
|
730
731
|
throw new Error(
|
|
731
|
-
`assets: '${symbol}' and '${owner}' both resolve to ${address} on ${
|
|
732
|
+
`assets: '${symbol}' and '${owner}' both resolve to ${address} on ${chainName2} \u2014 symbols must map to distinct contracts`
|
|
732
733
|
);
|
|
733
734
|
}
|
|
734
735
|
addressOwner.set(address, symbol);
|
|
@@ -1433,7 +1434,7 @@ var FillerConfigService = class {
|
|
|
1433
1434
|
return this.fillerConfig?.substratePrivateKey;
|
|
1434
1435
|
}
|
|
1435
1436
|
getEntryPointAddress(chain) {
|
|
1436
|
-
return this.chainConfigService.
|
|
1437
|
+
return this.chainConfigService.getEntryPointAddress(chain);
|
|
1437
1438
|
}
|
|
1438
1439
|
getSolverAccountContractAddress(chain) {
|
|
1439
1440
|
return this.chainConfigService.getSolverAccountAddress(chain);
|
|
@@ -2482,6 +2483,13 @@ var FREE_RPC_URLS = {
|
|
|
2482
2483
|
"https://arb1.arbitrum.io/rpc"
|
|
2483
2484
|
]
|
|
2484
2485
|
};
|
|
2486
|
+
var HYPERBRIDGE_BUNDLER_URLS = {
|
|
2487
|
+
1: "https://bundler.polytope.technology/ethereum",
|
|
2488
|
+
56: "https://bundler.polytope.technology/bsc",
|
|
2489
|
+
137: "https://bundler.polytope.technology/polygon",
|
|
2490
|
+
8453: "https://bundler.polytope.technology/base",
|
|
2491
|
+
42161: "https://bundler.polytope.technology/arbitrum"
|
|
2492
|
+
};
|
|
2485
2493
|
var CHAIN_NATIVE_SYMBOLS = {
|
|
2486
2494
|
1: "ETH",
|
|
2487
2495
|
56: "BNB",
|
|
@@ -2510,7 +2518,8 @@ var INIT_CHAINS = [
|
|
|
2510
2518
|
network: "mainnet",
|
|
2511
2519
|
alchemySubdomain: "eth-mainnet",
|
|
2512
2520
|
explorerUrl: external_links_default.chainExplorers.ethereum,
|
|
2513
|
-
defaultRpcUrls: FREE_RPC_URLS[1]
|
|
2521
|
+
defaultRpcUrls: FREE_RPC_URLS[1],
|
|
2522
|
+
hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[1]
|
|
2514
2523
|
},
|
|
2515
2524
|
{
|
|
2516
2525
|
chainId: 42161,
|
|
@@ -2519,7 +2528,8 @@ var INIT_CHAINS = [
|
|
|
2519
2528
|
network: "mainnet",
|
|
2520
2529
|
alchemySubdomain: "arb-mainnet",
|
|
2521
2530
|
explorerUrl: external_links_default.chainExplorers.arbitrum,
|
|
2522
|
-
defaultRpcUrls: FREE_RPC_URLS[42161]
|
|
2531
|
+
defaultRpcUrls: FREE_RPC_URLS[42161],
|
|
2532
|
+
hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[42161]
|
|
2523
2533
|
},
|
|
2524
2534
|
{
|
|
2525
2535
|
chainId: 8453,
|
|
@@ -2528,7 +2538,8 @@ var INIT_CHAINS = [
|
|
|
2528
2538
|
network: "mainnet",
|
|
2529
2539
|
alchemySubdomain: "base-mainnet",
|
|
2530
2540
|
explorerUrl: external_links_default.chainExplorers.base,
|
|
2531
|
-
defaultRpcUrls: FREE_RPC_URLS[8453]
|
|
2541
|
+
defaultRpcUrls: FREE_RPC_URLS[8453],
|
|
2542
|
+
hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[8453]
|
|
2532
2543
|
},
|
|
2533
2544
|
{
|
|
2534
2545
|
chainId: 137,
|
|
@@ -2537,7 +2548,8 @@ var INIT_CHAINS = [
|
|
|
2537
2548
|
network: "mainnet",
|
|
2538
2549
|
alchemySubdomain: "polygon-mainnet",
|
|
2539
2550
|
explorerUrl: external_links_default.chainExplorers.polygon,
|
|
2540
|
-
defaultRpcUrls: FREE_RPC_URLS[137]
|
|
2551
|
+
defaultRpcUrls: FREE_RPC_URLS[137],
|
|
2552
|
+
hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[137]
|
|
2541
2553
|
},
|
|
2542
2554
|
{
|
|
2543
2555
|
chainId: 56,
|
|
@@ -2546,7 +2558,8 @@ var INIT_CHAINS = [
|
|
|
2546
2558
|
network: "mainnet",
|
|
2547
2559
|
alchemySubdomain: "bnb-mainnet",
|
|
2548
2560
|
explorerUrl: external_links_default.chainExplorers.bnb,
|
|
2549
|
-
defaultRpcUrls: FREE_RPC_URLS[56]
|
|
2561
|
+
defaultRpcUrls: FREE_RPC_URLS[56],
|
|
2562
|
+
hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[56]
|
|
2550
2563
|
},
|
|
2551
2564
|
{
|
|
2552
2565
|
chainId: 11155111,
|
|
@@ -2859,7 +2872,7 @@ function describe(err) {
|
|
|
2859
2872
|
|
|
2860
2873
|
// src/orderbook/limit-orders.ts
|
|
2861
2874
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
2862
|
-
import { getChainId, MAX_DECLARED_ENTRIES } from "@hyperbridge/sdk";
|
|
2875
|
+
import { ENTRY_POINT_V08, getChainId, MAX_DECLARED_ENTRIES } from "@hyperbridge/sdk";
|
|
2863
2876
|
|
|
2864
2877
|
// src/orderbook/amounts.ts
|
|
2865
2878
|
import { keccak256, stringToBytes } from "viem";
|
|
@@ -3060,6 +3073,8 @@ var LimitOrderService = class {
|
|
|
3060
3073
|
this.delegationService = delegationService;
|
|
3061
3074
|
this.startingNonce = startingNonce;
|
|
3062
3075
|
this.vaultBalances = vaultBalances;
|
|
3076
|
+
/** Re-signs in flight, by order id. A fill's resize waits one out rather than reposting alongside it. */
|
|
3077
|
+
this.resigning = /* @__PURE__ */ new Map();
|
|
3063
3078
|
this.logger = loggers.get("limit-orders");
|
|
3064
3079
|
}
|
|
3065
3080
|
/**
|
|
@@ -3153,6 +3168,7 @@ var LimitOrderService = class {
|
|
|
3153
3168
|
bookExpiresAt: null,
|
|
3154
3169
|
bookPrice: null,
|
|
3155
3170
|
orderNonce: order.orderNonce,
|
|
3171
|
+
entryPoint: null,
|
|
3156
3172
|
status: "cancelled",
|
|
3157
3173
|
lastError: null
|
|
3158
3174
|
}),
|
|
@@ -3419,24 +3435,32 @@ var LimitOrderService = class {
|
|
|
3419
3435
|
async resize(remaining, delivered) {
|
|
3420
3436
|
const id = remaining.id;
|
|
3421
3437
|
const floor = await this.dustFloor(remaining);
|
|
3422
|
-
|
|
3438
|
+
for (let resigning = this.resigning.get(id); resigning; resigning = this.resigning.get(id)) await resigning;
|
|
3439
|
+
const order = await this.store.setStatus(id, "resizing", null, POSTABLE);
|
|
3440
|
+
if (!order) return null;
|
|
3441
|
+
if (BigInt(order.remaining) < floor) {
|
|
3423
3442
|
this.logger.info(
|
|
3424
|
-
{ id, remaining:
|
|
3443
|
+
{ id, remaining: order.remaining, floor: floor.toString() },
|
|
3425
3444
|
"Limit order worked down past the dust floor; closing it"
|
|
3426
3445
|
);
|
|
3427
|
-
if (
|
|
3428
|
-
const closed = await this.store.setPosting(
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3446
|
+
if (order.commitment) await this.withdraw(order.commitment);
|
|
3447
|
+
const closed = await this.store.setPosting(
|
|
3448
|
+
id,
|
|
3449
|
+
{
|
|
3450
|
+
commitment: null,
|
|
3451
|
+
bookExpiresAt: null,
|
|
3452
|
+
bookPrice: null,
|
|
3453
|
+
orderNonce: order.orderNonce,
|
|
3454
|
+
entryPoint: null,
|
|
3455
|
+
status: "filled",
|
|
3456
|
+
lastError: null
|
|
3457
|
+
},
|
|
3458
|
+
POSTABLE
|
|
3459
|
+
);
|
|
3436
3460
|
if (closed) this.report({ kind: "filled", order: closed });
|
|
3437
3461
|
return closed;
|
|
3438
3462
|
}
|
|
3439
|
-
const resized = await this.repost(
|
|
3463
|
+
const resized = await this.repost(order);
|
|
3440
3464
|
if (resized) this.report({ kind: "resized", order: resized, delivered });
|
|
3441
3465
|
return resized;
|
|
3442
3466
|
}
|
|
@@ -3449,7 +3473,7 @@ var LimitOrderService = class {
|
|
|
3449
3473
|
*/
|
|
3450
3474
|
async repost(order) {
|
|
3451
3475
|
if (!order) return null;
|
|
3452
|
-
if (isLocal(order)) return this.store.setStatus(order.id, "open");
|
|
3476
|
+
if (isLocal(order)) return this.store.setStatus(order.id, "open", null, POSTABLE);
|
|
3453
3477
|
if (hasExpired(order.expiresAt, /* @__PURE__ */ new Date())) return this.retire(order);
|
|
3454
3478
|
if (order.commitment) {
|
|
3455
3479
|
const withdrawn = await this.withdraw(order.commitment);
|
|
@@ -3460,10 +3484,13 @@ var LimitOrderService = class {
|
|
|
3460
3484
|
{ id: order.id, commitment: order.commitment, err: message },
|
|
3461
3485
|
"Could not clear the old entry; leaving the posting alone rather than adding a second"
|
|
3462
3486
|
);
|
|
3463
|
-
return this.store.setStatus(order.id, "open", message);
|
|
3487
|
+
return this.store.setStatus(order.id, "open", message, POSTABLE);
|
|
3464
3488
|
}
|
|
3465
3489
|
}
|
|
3466
|
-
await this.store.setStatus(order.id, "resizing")
|
|
3490
|
+
if (!await this.store.setStatus(order.id, "resizing", null, POSTABLE)) {
|
|
3491
|
+
this.logger.info({ id: order.id }, "Limit order closed while it was being reposted; leaving it closed");
|
|
3492
|
+
return null;
|
|
3493
|
+
}
|
|
3467
3494
|
const nonce = (BigInt(order.orderNonce) + 1n).toString();
|
|
3468
3495
|
return (await this.post({ ...order, commitment: null, orderNonce: nonce })).order;
|
|
3469
3496
|
}
|
|
@@ -3506,7 +3533,9 @@ var LimitOrderService = class {
|
|
|
3506
3533
|
* They drift apart when a request never got an answer or the process died
|
|
3507
3534
|
* between two of them: an entry the orderbook still lists that nothing here
|
|
3508
3535
|
* owns, an order here whose entry has gone, or an entry the orderbook has cut
|
|
3509
|
-
* down because the solver cannot cover what it quoted.
|
|
3536
|
+
* down because the solver cannot cover what it quoted. A posted op is also
|
|
3537
|
+
* signed over one EntryPoint, so when the fill chain moves to another, every
|
|
3538
|
+
* entry signed before the move is withdrawn and posted again.
|
|
3510
3539
|
*/
|
|
3511
3540
|
async reconcile(now = /* @__PURE__ */ new Date()) {
|
|
3512
3541
|
const [entries, live] = await Promise.all([this.postedOrders(), this.live()]);
|
|
@@ -3533,7 +3562,17 @@ var LimitOrderService = class {
|
|
|
3533
3562
|
}
|
|
3534
3563
|
for (const order of live) {
|
|
3535
3564
|
if (isLocal(order)) continue;
|
|
3536
|
-
if (order.commitment && found.has(order.commitment.toLowerCase()))
|
|
3565
|
+
if (order.commitment && found.has(order.commitment.toLowerCase())) {
|
|
3566
|
+
try {
|
|
3567
|
+
if (await this.resign(order)) report.reposted++;
|
|
3568
|
+
} catch (err) {
|
|
3569
|
+
this.logger.error(
|
|
3570
|
+
{ id: order.id, err },
|
|
3571
|
+
"Could not sign the limit order again for its chain's EntryPoint"
|
|
3572
|
+
);
|
|
3573
|
+
}
|
|
3574
|
+
continue;
|
|
3575
|
+
}
|
|
3537
3576
|
if (sinceMs(order.updatedAt, now) < POST_GRACE_MS) continue;
|
|
3538
3577
|
this.logger.warn({ id: order.id }, "Limit order has no entry on the orderbook; posting it again");
|
|
3539
3578
|
try {
|
|
@@ -3545,6 +3584,45 @@ var LimitOrderService = class {
|
|
|
3545
3584
|
}
|
|
3546
3585
|
return report;
|
|
3547
3586
|
}
|
|
3587
|
+
/**
|
|
3588
|
+
* Withdraws a posting signed for an EntryPoint its fill chain no longer uses,
|
|
3589
|
+
* and posts it again signed for the one it uses now. Answers whether a new
|
|
3590
|
+
* posting landed.
|
|
3591
|
+
*
|
|
3592
|
+
* The row is read again and claimed out of `open` first: a fill can have worked
|
|
3593
|
+
* it down or started its own repost since reconciliation listed it, and that
|
|
3594
|
+
* write is the one to follow. A fill that lands once the claim is taken waits
|
|
3595
|
+
* in `resize` for this to finish. The EntryPoint is only written with a posting
|
|
3596
|
+
* that landed, so a pass cut short anywhere leaves a row the next pass still
|
|
3597
|
+
* reads as stale, or as having no entry, and finishes the job.
|
|
3598
|
+
*/
|
|
3599
|
+
async resign(listed) {
|
|
3600
|
+
const entryPoint = this.configService.getEntryPointAddress(listed.fillChain);
|
|
3601
|
+
if (!entryPoint || sameAddress(signedFor(listed), entryPoint)) return false;
|
|
3602
|
+
const order = await this.store.get(listed.id);
|
|
3603
|
+
if (!order || order.status !== "open" || order.commitment !== listed.commitment) return false;
|
|
3604
|
+
if (sameAddress(signedFor(order), entryPoint) || this.resigning.has(order.id)) return false;
|
|
3605
|
+
const resigned = this.signAgain(order, entryPoint);
|
|
3606
|
+
this.resigning.set(
|
|
3607
|
+
order.id,
|
|
3608
|
+
resigned.catch(() => false)
|
|
3609
|
+
);
|
|
3610
|
+
try {
|
|
3611
|
+
return await resigned;
|
|
3612
|
+
} finally {
|
|
3613
|
+
this.resigning.delete(order.id);
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3616
|
+
async signAgain(order, entryPoint) {
|
|
3617
|
+
const claimed = await this.store.setStatus(order.id, "resizing", null, ["open"]);
|
|
3618
|
+
if (!claimed) return false;
|
|
3619
|
+
this.logger.warn(
|
|
3620
|
+
{ id: order.id, commitment: order.commitment, signedFor: signedFor(order), entryPoint },
|
|
3621
|
+
"Limit order was signed for an EntryPoint its chain no longer uses; signing it again"
|
|
3622
|
+
);
|
|
3623
|
+
const posted = await this.repost(claimed);
|
|
3624
|
+
return !!posted?.commitment && sameAddress(signedFor(posted), entryPoint);
|
|
3625
|
+
}
|
|
3548
3626
|
/**
|
|
3549
3627
|
* Lowers every open order's `remaining` to what the solver's account will still
|
|
3550
3628
|
* pay for it, and puts the order back on the book at that size.
|
|
@@ -3626,6 +3704,7 @@ var LimitOrderService = class {
|
|
|
3626
3704
|
bookExpiresAt: null,
|
|
3627
3705
|
bookPrice: null,
|
|
3628
3706
|
orderNonce: order.orderNonce,
|
|
3707
|
+
entryPoint: null,
|
|
3629
3708
|
status: "expired",
|
|
3630
3709
|
lastError: null
|
|
3631
3710
|
});
|
|
@@ -3682,11 +3761,11 @@ var LimitOrderService = class {
|
|
|
3682
3761
|
}
|
|
3683
3762
|
/** Builds and submits the posting, then writes the orderbook's answer onto the row. */
|
|
3684
3763
|
async post(order) {
|
|
3685
|
-
const { result, orderNonce } = await this.submit(order);
|
|
3764
|
+
const { result, orderNonce, entryPoint } = await this.submit(order);
|
|
3686
3765
|
if (result.kind === "accepted" || result.kind === "unchanged") {
|
|
3687
3766
|
const posted = result.order;
|
|
3688
3767
|
this.logger.info(
|
|
3689
|
-
{ id: order.id, commitment: posted.commitment, price: posted.price },
|
|
3768
|
+
{ id: order.id, commitment: posted.commitment, price: posted.price, entryPoint },
|
|
3690
3769
|
"Limit order posted to the orderbook"
|
|
3691
3770
|
);
|
|
3692
3771
|
const stored = await this.store.setPosting(
|
|
@@ -3696,6 +3775,10 @@ var LimitOrderService = class {
|
|
|
3696
3775
|
bookExpiresAt: posted.expiresAt,
|
|
3697
3776
|
bookPrice: posted.price,
|
|
3698
3777
|
orderNonce: orderNonce.toString(),
|
|
3778
|
+
// An entry the orderbook already held was signed by an earlier attempt,
|
|
3779
|
+
// for whichever EntryPoint was current then. Recorded as unknown, which
|
|
3780
|
+
// reconciliation reads as v0.8 and signs again once the chain is past it.
|
|
3781
|
+
entryPoint: result.kind === "accepted" ? entryPoint : null,
|
|
3699
3782
|
status: "open",
|
|
3700
3783
|
lastError: null
|
|
3701
3784
|
},
|
|
@@ -3725,6 +3808,7 @@ var LimitOrderService = class {
|
|
|
3725
3808
|
bookExpiresAt: null,
|
|
3726
3809
|
bookPrice: null,
|
|
3727
3810
|
orderNonce: orderNonce.toString(),
|
|
3811
|
+
entryPoint: null,
|
|
3728
3812
|
status: "open",
|
|
3729
3813
|
lastError: message
|
|
3730
3814
|
}),
|
|
@@ -3747,22 +3831,30 @@ var LimitOrderService = class {
|
|
|
3747
3831
|
* only the second one.
|
|
3748
3832
|
*/
|
|
3749
3833
|
async submit(order) {
|
|
3834
|
+
const entryPoint = this.entryPointFor(order.fillChain);
|
|
3750
3835
|
const orderNonce = BigInt(order.orderNonce);
|
|
3751
|
-
const attempt = await this.buildAndSubmit(order, orderNonce);
|
|
3836
|
+
const attempt = await this.buildAndSubmit(order, orderNonce, entryPoint);
|
|
3752
3837
|
const first = attempt.result;
|
|
3753
3838
|
if (first.kind !== "rejected" || first.code !== "REPLAYED" && first.code !== "ORDER_EXISTS") {
|
|
3754
|
-
return { result: first, orderNonce };
|
|
3839
|
+
return { result: first, orderNonce, entryPoint };
|
|
3755
3840
|
}
|
|
3756
3841
|
if (first.code === "ORDER_EXISTS" && attempt.commitment) {
|
|
3757
3842
|
const live = await this.entryAt(order, attempt.commitment);
|
|
3758
3843
|
if (live) {
|
|
3759
3844
|
this.logger.info({ id: order.id, commitment: live.commitment }, "This posting is already on the orderbook");
|
|
3760
|
-
return { result: { kind: "unchanged", order: live }, orderNonce };
|
|
3845
|
+
return { result: { kind: "unchanged", order: live }, orderNonce, entryPoint };
|
|
3761
3846
|
}
|
|
3762
3847
|
}
|
|
3763
3848
|
this.logger.warn({ id: order.id, code: first.code }, "Orderbook has seen this op before; reposting on a new nonce");
|
|
3764
3849
|
const retried = orderNonce + 1n;
|
|
3765
|
-
|
|
3850
|
+
const { result } = await this.buildAndSubmit(order, retried, entryPoint);
|
|
3851
|
+
return { result, orderNonce: retried, entryPoint };
|
|
3852
|
+
}
|
|
3853
|
+
/** The EntryPoint a posting on `chain` is signed for now. */
|
|
3854
|
+
entryPointFor(chain) {
|
|
3855
|
+
const entryPoint = this.configService.getEntryPointAddress(chain);
|
|
3856
|
+
if (!entryPoint) throw new LimitOrderValidationError(`No EntryPoint is configured for ${chain}`);
|
|
3857
|
+
return entryPoint;
|
|
3766
3858
|
}
|
|
3767
3859
|
/**
|
|
3768
3860
|
* The entry the orderbook holds at `commitment`, if it holds one. A lookup that
|
|
@@ -3778,8 +3870,8 @@ var LimitOrderService = class {
|
|
|
3778
3870
|
}
|
|
3779
3871
|
}
|
|
3780
3872
|
/** The orderbook's answer, and the commitment the op we sent hashes to. */
|
|
3781
|
-
async buildAndSubmit(order, orderNonce) {
|
|
3782
|
-
const { commitment, userOp } = await this.buildUserOp(order, orderNonce);
|
|
3873
|
+
async buildAndSubmit(order, orderNonce, entryPoint) {
|
|
3874
|
+
const { commitment, userOp } = await this.buildUserOp(order, orderNonce, entryPoint);
|
|
3783
3875
|
try {
|
|
3784
3876
|
return { result: await this.client.submitOrder(userOp), commitment };
|
|
3785
3877
|
} catch (err) {
|
|
@@ -3789,7 +3881,7 @@ var LimitOrderService = class {
|
|
|
3789
3881
|
throw err;
|
|
3790
3882
|
}
|
|
3791
3883
|
}
|
|
3792
|
-
async buildUserOp(order, orderNonce) {
|
|
3884
|
+
async buildUserOp(order, orderNonce, entryPointAddress) {
|
|
3793
3885
|
const baseToken = this.assetRegistry.getAddress(order.base, order.fillChain);
|
|
3794
3886
|
const quoteToken = this.assetRegistry.getAddress(order.quote, order.fillChain);
|
|
3795
3887
|
const [baseDecimals, quoteDecimals] = await Promise.all([
|
|
@@ -3804,10 +3896,6 @@ var LimitOrderService = class {
|
|
|
3804
3896
|
baseDecimals,
|
|
3805
3897
|
quoteDecimals
|
|
3806
3898
|
});
|
|
3807
|
-
const entryPointAddress = this.configService.getEntryPointAddress(order.fillChain);
|
|
3808
|
-
if (!entryPointAddress) {
|
|
3809
|
-
throw new LimitOrderValidationError(`No EntryPoint is configured for ${order.fillChain}`);
|
|
3810
|
-
}
|
|
3811
3899
|
return this.contractService.prepareLimitOrderUserOp({
|
|
3812
3900
|
fillChain: order.fillChain,
|
|
3813
3901
|
entryPointAddress,
|
|
@@ -3838,6 +3926,12 @@ function hasExpired(expiresAt, now) {
|
|
|
3838
3926
|
const at = Date.parse(expiresAt);
|
|
3839
3927
|
return !Number.isNaN(at) && at <= now.getTime();
|
|
3840
3928
|
}
|
|
3929
|
+
function signedFor(order) {
|
|
3930
|
+
return order.entryPoint ?? ENTRY_POINT_V08;
|
|
3931
|
+
}
|
|
3932
|
+
function sameAddress(a, b) {
|
|
3933
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
3934
|
+
}
|
|
3841
3935
|
function sinceMs(updatedAt, now) {
|
|
3842
3936
|
const written = Date.parse(`${updatedAt.replace(" ", "T")}Z`);
|
|
3843
3937
|
return Number.isNaN(written) ? Number.POSITIVE_INFINITY : now.getTime() - written;
|
|
@@ -4302,6 +4396,80 @@ async function patchRuntimeState(store, patch) {
|
|
|
4302
4396
|
return next;
|
|
4303
4397
|
}
|
|
4304
4398
|
|
|
4399
|
+
// src/services/bundler-preflight.ts
|
|
4400
|
+
var BUNDLER_PREFLIGHT_TIMEOUT_MS = 1e4;
|
|
4401
|
+
async function assertBundlersServeEntryPoint(chains, configService, options = {}) {
|
|
4402
|
+
const timeoutMs = options.timeoutMs ?? BUNDLER_PREFLIGHT_TIMEOUT_MS;
|
|
4403
|
+
const logger = moduleLogger(configService.loggers, "bundler-preflight");
|
|
4404
|
+
const failures = await Promise.all(
|
|
4405
|
+
chains.map(async ({ chainId, bundlerUrl }) => {
|
|
4406
|
+
if (options.watchOnly?.[chainId] === true) return void 0;
|
|
4407
|
+
if (!bundlerUrl?.trim()) return void 0;
|
|
4408
|
+
const entryPoint = configService.getEntryPointAddress(formatChainKey(chainId));
|
|
4409
|
+
if (!entryPoint) return void 0;
|
|
4410
|
+
return checkBundler(chainId, bundlerUrl.trim(), entryPoint, timeoutMs, logger);
|
|
4411
|
+
})
|
|
4412
|
+
);
|
|
4413
|
+
const messages = failures.filter((failure) => failure !== void 0);
|
|
4414
|
+
if (messages.length > 0) throw new Error(messages.join("; "));
|
|
4415
|
+
}
|
|
4416
|
+
function servesEntryPoint(listed, entryPoint) {
|
|
4417
|
+
return Array.isArray(listed) && listed.some((address) => String(address).toLowerCase() === entryPoint.toLowerCase());
|
|
4418
|
+
}
|
|
4419
|
+
function describeEntryPoints(listed) {
|
|
4420
|
+
return Array.isArray(listed) && listed.length > 0 ? listed.map(String).join(", ") : "none";
|
|
4421
|
+
}
|
|
4422
|
+
function bundlerHost(url) {
|
|
4423
|
+
try {
|
|
4424
|
+
return new URL(url).host || "an unparseable URL";
|
|
4425
|
+
} catch {
|
|
4426
|
+
return "an unparseable URL";
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4429
|
+
function chainName(chainId) {
|
|
4430
|
+
const label = chainByChainId(chainId)?.label;
|
|
4431
|
+
return label ? `${label} (${formatChainKey(chainId)})` : formatChainKey(chainId);
|
|
4432
|
+
}
|
|
4433
|
+
async function checkBundler(chainId, bundlerUrl, entryPoint, timeoutMs, logger) {
|
|
4434
|
+
const host = bundlerHost(bundlerUrl);
|
|
4435
|
+
const subject = `Bundler ${host} for ${chainName(chainId)}`;
|
|
4436
|
+
const signal = AbortSignal.timeout(timeoutMs);
|
|
4437
|
+
let listed;
|
|
4438
|
+
try {
|
|
4439
|
+
listed = await supportedEntryPoints(bundlerUrl, signal);
|
|
4440
|
+
} catch (err) {
|
|
4441
|
+
const reason = signal.aborted ? `timed out after ${timeoutMs}ms` : errorReason(err);
|
|
4442
|
+
logger.warn(
|
|
4443
|
+
{ chain: formatChainKey(chainId), bundler: host },
|
|
4444
|
+
`${subject} did not answer eth_supportedEntryPoints: ${reason.split(bundlerUrl).join(host)}. Continuing without confirming it serves EntryPoint ${entryPoint}`
|
|
4445
|
+
);
|
|
4446
|
+
return void 0;
|
|
4447
|
+
}
|
|
4448
|
+
if (servesEntryPoint(listed, entryPoint)) return void 0;
|
|
4449
|
+
return `${subject} does not support EntryPoint ${entryPoint}; it lists ${describeEntryPoints(listed)}`;
|
|
4450
|
+
}
|
|
4451
|
+
async function supportedEntryPoints(bundlerUrl, signal) {
|
|
4452
|
+
const response = await fetch(bundlerUrl, {
|
|
4453
|
+
method: "POST",
|
|
4454
|
+
headers: { "Content-Type": "application/json" },
|
|
4455
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_supportedEntryPoints", params: [] }),
|
|
4456
|
+
signal
|
|
4457
|
+
});
|
|
4458
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
4459
|
+
const json = await response.json();
|
|
4460
|
+
if (json.error) throw new Error(json.error.message ?? JSON.stringify(json.error));
|
|
4461
|
+
const { result } = json;
|
|
4462
|
+
if (!Array.isArray(result) || !result.every((address) => typeof address === "string")) {
|
|
4463
|
+
throw new Error("the result is not a list of addresses");
|
|
4464
|
+
}
|
|
4465
|
+
return result;
|
|
4466
|
+
}
|
|
4467
|
+
function errorReason(err) {
|
|
4468
|
+
if (!(err instanceof Error)) return String(err);
|
|
4469
|
+
const { cause } = err;
|
|
4470
|
+
return cause instanceof Error ? `${err.message} (${cause.message})` : err.message;
|
|
4471
|
+
}
|
|
4472
|
+
|
|
4305
4473
|
// src/services/wallet/mpcvault.ts
|
|
4306
4474
|
import { concatHex, keccak256 as keccak2562, padHex, toHex } from "viem";
|
|
4307
4475
|
import * as grpc from "@grpc/grpc-js";
|
|
@@ -17496,9 +17664,9 @@ var INTENT_GATEWAY_V2_ABI = [
|
|
|
17496
17664
|
internalType: "bytes32"
|
|
17497
17665
|
},
|
|
17498
17666
|
{
|
|
17499
|
-
name: "
|
|
17500
|
-
type: "
|
|
17501
|
-
internalType: "
|
|
17667
|
+
name: "userOpHash",
|
|
17668
|
+
type: "bytes32",
|
|
17669
|
+
internalType: "bytes32"
|
|
17502
17670
|
},
|
|
17503
17671
|
{
|
|
17504
17672
|
name: "signature",
|
|
@@ -18887,10 +19055,13 @@ var LIMIT_ORDER_CALL_GAS_LIMIT = 500000n;
|
|
|
18887
19055
|
var LIMIT_ORDER_VERIFICATION_GAS_LIMIT = 150000n;
|
|
18888
19056
|
var LIMIT_ORDER_PRE_VERIFICATION_GAS = 50000n;
|
|
18889
19057
|
var BID_DISCOVERY_PAD_SECONDS = 30;
|
|
19058
|
+
var BLOCK_TIME_SAMPLE_BLOCKS = 1000n;
|
|
19059
|
+
var FALLBACK_BLOCK_TIME_SECONDS = 2;
|
|
18890
19060
|
var ContractInteractionService = class {
|
|
18891
19061
|
constructor(clientManager, configService, signer, sharedCacheService) {
|
|
18892
19062
|
this.clientManager = clientManager;
|
|
18893
19063
|
this.sdkHelperCache = /* @__PURE__ */ new Map();
|
|
19064
|
+
this.blockTimes = /* @__PURE__ */ new Map();
|
|
18894
19065
|
this.logger = moduleLogger(configService.loggers, "contract-service");
|
|
18895
19066
|
this.configService = configService;
|
|
18896
19067
|
this.cacheService = sharedCacheService || new CacheService();
|
|
@@ -18950,11 +19121,20 @@ var ContractInteractionService = class {
|
|
|
18950
19121
|
try {
|
|
18951
19122
|
const chainIds = this.configService.getConfiguredChainIds();
|
|
18952
19123
|
const chainNames = chainIds.map((id) => `EVM-${id}`);
|
|
18953
|
-
|
|
19124
|
+
await Promise.all(
|
|
19125
|
+
chainNames.map(async (chainName2) => {
|
|
19126
|
+
try {
|
|
19127
|
+
await this.blockTimeSeconds(chainName2);
|
|
19128
|
+
} catch (err) {
|
|
19129
|
+
this.logger.warn({ err, chain: chainName2 }, "Could not measure the block time at startup");
|
|
19130
|
+
}
|
|
19131
|
+
})
|
|
19132
|
+
);
|
|
19133
|
+
for (const chainName2 of chainNames) {
|
|
18954
19134
|
try {
|
|
18955
|
-
await this.getFeeTokenWithDecimals(
|
|
19135
|
+
await this.getFeeTokenWithDecimals(chainName2);
|
|
18956
19136
|
} catch (err) {
|
|
18957
|
-
this.logger.warn({ err, chain:
|
|
19137
|
+
this.logger.warn({ err, chain: chainName2 }, "Could not prewarm fee token decimals");
|
|
18958
19138
|
}
|
|
18959
19139
|
}
|
|
18960
19140
|
for (const destChain of chainNames) {
|
|
@@ -19539,18 +19719,59 @@ var ContractInteractionService = class {
|
|
|
19539
19719
|
*
|
|
19540
19720
|
* Operators configure seconds because that is the unit the risk is actually in, but the
|
|
19541
19721
|
* contract compares against block numbers so `order.deadline` and this read the same
|
|
19542
|
-
* clock. The conversion uses the chain's
|
|
19543
|
-
* up, since erring long costs a slightly stale quote while erring
|
|
19544
|
-
* winnable bids.
|
|
19722
|
+
* clock. The conversion uses the chain's block time from {@link blockTimeSeconds}; it is
|
|
19723
|
+
* deliberately rounded up, since erring long costs a slightly stale quote while erring
|
|
19724
|
+
* short silently drops winnable bids.
|
|
19545
19725
|
*/
|
|
19546
19726
|
async bidValidUntilBlock(chain) {
|
|
19547
19727
|
const client = this.clientManager.getPublicClient(chain);
|
|
19548
|
-
const currentBlock = await client.getBlockNumber();
|
|
19549
|
-
const blockTimeMs = client.chain?.blockTime;
|
|
19550
|
-
const blockTimeSec = blockTimeMs ? blockTimeMs / 1e3 : 2;
|
|
19728
|
+
const [currentBlock, blockTimeSec] = await Promise.all([client.getBlockNumber(), this.blockTimeSeconds(chain)]);
|
|
19551
19729
|
const windowSec = this.configService.getBidValiditySeconds() + BID_DISCOVERY_PAD_SECONDS;
|
|
19552
19730
|
return currentBlock + BigInt(Math.ceil(windowSec / blockTimeSec));
|
|
19553
19731
|
}
|
|
19732
|
+
/**
|
|
19733
|
+
* Seconds per block on `chain`. Measured once, when simplex starts ({@link initCache}), and
|
|
19734
|
+
* kept until it stops. A chain with no measurement yet, because it was added later or its
|
|
19735
|
+
* blocks could not be read at startup, is measured at its first bid.
|
|
19736
|
+
*
|
|
19737
|
+
* The nominal `Chain.blockTime` cannot be relied on alone: it goes stale when a chain
|
|
19738
|
+
* speeds up (BSC makes a block every 0.45s against a declared 0.75s) and some chains
|
|
19739
|
+
* declare none. Converting at a slower block time than the chain's ends a bid early, so
|
|
19740
|
+
* the faster of the measured and the declared figure is used, and a stalled or idle
|
|
19741
|
+
* stretch in the sample never shortens a bid.
|
|
19742
|
+
*
|
|
19743
|
+
* Blocks that cannot be read fall back to the declared figure, then to
|
|
19744
|
+
* {@link FALLBACK_BLOCK_TIME_SECONDS}. That answer is not kept.
|
|
19745
|
+
*/
|
|
19746
|
+
blockTimeSeconds(chain) {
|
|
19747
|
+
const known = this.blockTimes.get(chain);
|
|
19748
|
+
if (known) return known;
|
|
19749
|
+
const client = this.clientManager.getPublicClient(chain);
|
|
19750
|
+
const declaredMs = client.chain?.blockTime;
|
|
19751
|
+
const declared = declaredMs ? declaredMs / 1e3 : void 0;
|
|
19752
|
+
const seconds = this.measureBlockTimeSeconds(client).then((measured) => declared ? Math.min(measured, declared) : measured).catch((err) => {
|
|
19753
|
+
this.blockTimes.delete(chain);
|
|
19754
|
+
const fallback2 = declared ?? FALLBACK_BLOCK_TIME_SECONDS;
|
|
19755
|
+
this.logger.warn(
|
|
19756
|
+
{ err, chain, seconds: fallback2 },
|
|
19757
|
+
"Could not measure the block time; using the declared one"
|
|
19758
|
+
);
|
|
19759
|
+
return fallback2;
|
|
19760
|
+
});
|
|
19761
|
+
this.blockTimes.set(chain, seconds);
|
|
19762
|
+
return seconds;
|
|
19763
|
+
}
|
|
19764
|
+
/** The average block time over the chain's last {@link BLOCK_TIME_SAMPLE_BLOCKS} blocks, in seconds. */
|
|
19765
|
+
async measureBlockTimeSeconds(client) {
|
|
19766
|
+
const head = await client.getBlock({ blockTag: "latest" });
|
|
19767
|
+
const span = head.number < BLOCK_TIME_SAMPLE_BLOCKS ? head.number : BLOCK_TIME_SAMPLE_BLOCKS;
|
|
19768
|
+
const tail = await client.getBlock({ blockNumber: head.number - span });
|
|
19769
|
+
const measured = Number(head.timestamp - tail.timestamp) / Number(span);
|
|
19770
|
+
if (!Number.isFinite(measured) || measured <= 0) {
|
|
19771
|
+
throw new Error(`${span} blocks span ${head.timestamp - tail.timestamp}s`);
|
|
19772
|
+
}
|
|
19773
|
+
return measured;
|
|
19774
|
+
}
|
|
19554
19775
|
/**
|
|
19555
19776
|
* Builds the signed, never-executed UserOperation that carries one limit order
|
|
19556
19777
|
* to the HyperFX orderbook.
|
|
@@ -19725,11 +19946,18 @@ import { encodeFunctionData as encodeFunctionData3, erc20Abi as erc20Abi3, forma
|
|
|
19725
19946
|
import { encodeERC7821ExecuteBatch as encodeERC7821ExecuteBatch3 } from "@hyperbridge/sdk";
|
|
19726
19947
|
|
|
19727
19948
|
// src/services/UserOpSender.ts
|
|
19728
|
-
import {
|
|
19949
|
+
import {
|
|
19950
|
+
CryptoUtils as CryptoUtils2,
|
|
19951
|
+
BundlerMethod,
|
|
19952
|
+
applyRundlerPriorityFee,
|
|
19953
|
+
fetchRundlerPriorityFee
|
|
19954
|
+
} from "@hyperbridge/sdk";
|
|
19729
19955
|
import { toHex as toHex2 } from "viem";
|
|
19730
19956
|
var FALLBACK_VERIFICATION_GAS_LIMIT = 250000n;
|
|
19731
19957
|
var FALLBACK_CALL_GAS_LIMIT = 1500000n;
|
|
19732
19958
|
var FALLBACK_PRE_VERIFICATION_GAS = 150000n;
|
|
19959
|
+
var PRIORITY_FEE_BUMP_PERCENT = 8n;
|
|
19960
|
+
var MAX_FEE_BUMP_PERCENT = 10n;
|
|
19733
19961
|
var UserOpSender = class {
|
|
19734
19962
|
constructor(clientManager, configService, signer) {
|
|
19735
19963
|
this.clientManager = clientManager;
|
|
@@ -19910,7 +20138,11 @@ var UserOpSender = class {
|
|
|
19910
20138
|
);
|
|
19911
20139
|
return userOp;
|
|
19912
20140
|
}
|
|
19913
|
-
/**
|
|
20141
|
+
/**
|
|
20142
|
+
* Mirrors the gas-price selection used by the delegation and fill paths: Pimlico's and
|
|
20143
|
+
* Alchemy's own pricing, otherwise the chain's gas price raised to the priority fee a
|
|
20144
|
+
* rundler bundler requires, when the bundler gives one.
|
|
20145
|
+
*/
|
|
19914
20146
|
async getGasPrice(bundlerUrl, publicClient, chainId) {
|
|
19915
20147
|
const lower = bundlerUrl.toLowerCase();
|
|
19916
20148
|
if (lower.includes("pimlico.io")) {
|
|
@@ -19922,19 +20154,33 @@ var UserOpSender = class {
|
|
|
19922
20154
|
return { maxFeePerGas: BigInt(res.fast.maxFeePerGas), maxPriorityFeePerGas: BigInt(res.fast.maxPriorityFeePerGas) };
|
|
19923
20155
|
}
|
|
19924
20156
|
if (lower.includes("alchemy.com")) {
|
|
19925
|
-
const [
|
|
20157
|
+
const [rundlerPriorityFee2, latestBlock2] = await Promise.all([
|
|
19926
20158
|
this.sendBundlerRpc(bundlerUrl, BundlerMethod.RUNDLER_MAX_PRIORITY_FEE_PER_GAS, []),
|
|
19927
20159
|
publicClient.getBlock({ blockTag: "latest" })
|
|
19928
20160
|
]);
|
|
19929
|
-
const baseFeePerGas =
|
|
20161
|
+
const baseFeePerGas = latestBlock2.baseFeePerGas ?? await publicClient.getGasPrice();
|
|
19930
20162
|
const isArbitrum = BigInt(chainId) === 42161n;
|
|
19931
20163
|
const prioBump = isArbitrum ? 0n : 25n;
|
|
19932
|
-
const maxPriorityFeePerGas = BigInt(
|
|
20164
|
+
const maxPriorityFeePerGas = BigInt(rundlerPriorityFee2) + BigInt(rundlerPriorityFee2) * prioBump / 100n;
|
|
19933
20165
|
const bufferedBaseFee = baseFeePerGas + baseFeePerGas * 50n / 100n;
|
|
19934
20166
|
return { maxFeePerGas: bufferedBaseFee + maxPriorityFeePerGas, maxPriorityFeePerGas };
|
|
19935
20167
|
}
|
|
19936
|
-
const gasPrice = await
|
|
19937
|
-
|
|
20168
|
+
const [gasPrice, rundlerPriorityFee] = await Promise.all([
|
|
20169
|
+
publicClient.getGasPrice(),
|
|
20170
|
+
fetchRundlerPriorityFee(bundlerUrl)
|
|
20171
|
+
]);
|
|
20172
|
+
const fees = {
|
|
20173
|
+
maxFeePerGas: gasPrice + gasPrice * MAX_FEE_BUMP_PERCENT / 100n,
|
|
20174
|
+
maxPriorityFeePerGas: gasPrice + gasPrice * PRIORITY_FEE_BUMP_PERCENT / 100n
|
|
20175
|
+
};
|
|
20176
|
+
if (rundlerPriorityFee === null) return fees;
|
|
20177
|
+
const latestBlock = await publicClient.getBlock({ blockTag: "latest" });
|
|
20178
|
+
return applyRundlerPriorityFee(fees, {
|
|
20179
|
+
rundlerPriorityFee,
|
|
20180
|
+
baseFeePerGas: latestBlock.baseFeePerGas ?? gasPrice,
|
|
20181
|
+
priorityFeeBumpPercent: PRIORITY_FEE_BUMP_PERCENT,
|
|
20182
|
+
maxFeeBumpPercent: MAX_FEE_BUMP_PERCENT
|
|
20183
|
+
});
|
|
19938
20184
|
}
|
|
19939
20185
|
async sendBundlerRpc(bundlerUrl, method, params) {
|
|
19940
20186
|
const response = await fetch(bundlerUrl, {
|
|
@@ -24481,8 +24727,8 @@ async function bootFiller(config, options) {
|
|
|
24481
24727
|
};
|
|
24482
24728
|
const configService = new FillerConfigService(resolvedChains, fillerConfigForService, options.loggers);
|
|
24483
24729
|
const chainConfigs = resolvedChains.map((chain) => {
|
|
24484
|
-
const
|
|
24485
|
-
return configService.getChainConfig(
|
|
24730
|
+
const chainName2 = `EVM-${chain.chainId}`;
|
|
24731
|
+
return configService.getChainConfig(chainName2);
|
|
24486
24732
|
});
|
|
24487
24733
|
let watchOnlyConfig;
|
|
24488
24734
|
const globalWatchOnly = options.watchOnlyOverride === true || config.simplex.watchOnly === true;
|
|
@@ -24520,6 +24766,8 @@ async function bootFiller(config, options) {
|
|
|
24520
24766
|
"A signer is required unless every chain is watch-only: pass `signer` to Simplex.start (privateKeySigner, secretPhraseSigner, turnkeySigner, mpcVaultSigner, viemSigner, or your own Signer implementation). Running the binary? Configure it under [simplex.signer]."
|
|
24521
24767
|
);
|
|
24522
24768
|
}
|
|
24769
|
+
logger.info("Checking each bundler serves its chain's EntryPoint...");
|
|
24770
|
+
await assertBundlersServeEntryPoint(resolvedChains, configService, { watchOnly: watchOnlyConfig });
|
|
24523
24771
|
const chainClientManager = new ChainClientManager(configService, options.signer);
|
|
24524
24772
|
const runtimeSigner = chainClientManager.getSigner();
|
|
24525
24773
|
if (options.signer) {
|
|
@@ -24710,10 +24958,10 @@ async function bootFiller(config, options) {
|
|
|
24710
24958
|
const token1 = {};
|
|
24711
24959
|
for (const pair of config.pairs ?? []) {
|
|
24712
24960
|
if (normalizeSymbol(pair.token0) === normalizeSymbol(pair.token1)) continue;
|
|
24713
|
-
for (const
|
|
24714
|
-
const address = assetRegistry.getAddress(pair.token1,
|
|
24961
|
+
for (const chainName2 of configuredChainNames) {
|
|
24962
|
+
const address = assetRegistry.getAddress(pair.token1, chainName2);
|
|
24715
24963
|
if (!address) continue;
|
|
24716
|
-
const list = token1[
|
|
24964
|
+
const list = token1[chainName2] ?? (token1[chainName2] = []);
|
|
24717
24965
|
if (!list.includes(address)) list.push(address);
|
|
24718
24966
|
}
|
|
24719
24967
|
}
|
|
@@ -25109,6 +25357,7 @@ var MemoryLimitOrderStore = class {
|
|
|
25109
25357
|
status: "open",
|
|
25110
25358
|
commitment: null,
|
|
25111
25359
|
orderNonce: order.orderNonce ?? "0",
|
|
25360
|
+
entryPoint: null,
|
|
25112
25361
|
bookExpiresAt: null,
|
|
25113
25362
|
bookPrice: null,
|
|
25114
25363
|
lastError: null,
|
|
@@ -26094,10 +26343,10 @@ var PairController = class {
|
|
|
26094
26343
|
if (assets && Object.keys(assets).length > 0) config.assets = nextAssets;
|
|
26095
26344
|
if (normalizeSymbol(pair.token0) !== normalizeSymbol(pair.token1)) {
|
|
26096
26345
|
for (const chain of this.runtime.resolvedChains) {
|
|
26097
|
-
const
|
|
26098
|
-
const address = assetRegistry.getAddress(pair.token1,
|
|
26346
|
+
const chainName2 = formatChainKey(chain.chainId);
|
|
26347
|
+
const address = assetRegistry.getAddress(pair.token1, chainName2);
|
|
26099
26348
|
if (!address) continue;
|
|
26100
|
-
const list = balanceTokens[
|
|
26349
|
+
const list = balanceTokens[chainName2] ?? (balanceTokens[chainName2] = []);
|
|
26101
26350
|
if (!list.includes(address)) list.push(address);
|
|
26102
26351
|
}
|
|
26103
26352
|
}
|
|
@@ -26202,8 +26451,9 @@ var ChainController = class {
|
|
|
26202
26451
|
`Chain ${chainId} cannot be added as filling: this solver was started without a signer. Restart with \`signer\` to fill.`
|
|
26203
26452
|
);
|
|
26204
26453
|
}
|
|
26205
|
-
configService.addChain(resolved);
|
|
26206
26454
|
const watchOnly = chain.watchOnly ?? (this.runtime.signerless || this.runtime.globalWatchOnly);
|
|
26455
|
+
await assertBundlersServeEntryPoint([resolved], configService, { watchOnly: { [chainId]: watchOnly } });
|
|
26456
|
+
configService.addChain(resolved);
|
|
26207
26457
|
if (watchOnly) intentFiller.setWatchOnly(chainId, true);
|
|
26208
26458
|
try {
|
|
26209
26459
|
if (!inScanner) {
|
|
@@ -26295,6 +26545,9 @@ var ChainController = class {
|
|
|
26295
26545
|
if (!bundlerUrl?.trim()) throw new Error("A bundler URL is required");
|
|
26296
26546
|
const index = this.runtime.resolvedChains.findIndex((chain) => chain.chainId === chainId);
|
|
26297
26547
|
if (index < 0) throw new Error(`Chain ${chainId} is not configured`);
|
|
26548
|
+
await assertBundlersServeEntryPoint([{ chainId, bundlerUrl }], this.runtime.configService, {
|
|
26549
|
+
watchOnly: this.runtime.intentFiller.getWatchOnly()
|
|
26550
|
+
});
|
|
26298
26551
|
this.runtime.configService.setBundlerUrl(chainId, bundlerUrl);
|
|
26299
26552
|
this.runtime.resolvedChains[index].bundlerUrl = bundlerUrl;
|
|
26300
26553
|
this.runtime.config.chains[index].bundlerUrl = bundlerUrl;
|
|
@@ -26309,6 +26562,10 @@ var ChainController = class {
|
|
|
26309
26562
|
"Watch-only cannot be disabled: this solver was started without a signer. Restart with `signer` to fill."
|
|
26310
26563
|
);
|
|
26311
26564
|
}
|
|
26565
|
+
if (!watchOnly) {
|
|
26566
|
+
const chain = this.runtime.resolvedChains.find((resolved) => resolved.chainId === chainId);
|
|
26567
|
+
if (chain) await assertBundlersServeEntryPoint([chain], this.runtime.configService);
|
|
26568
|
+
}
|
|
26312
26569
|
this.runtime.intentFiller.setWatchOnly(chainId, watchOnly);
|
|
26313
26570
|
this.syncWatchOnlyToConfig();
|
|
26314
26571
|
await this.persist();
|