@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.cjs
CHANGED
|
@@ -636,7 +636,8 @@ var BUILTIN_ASSETS = {
|
|
|
636
636
|
ZARP: { resolve: (r, chain) => r.getAssetBySymbol(chain, "ZARP") },
|
|
637
637
|
EURC: { resolve: (r, chain) => r.getAssetBySymbol(chain, "EURC") },
|
|
638
638
|
XSGD: { resolve: (r, chain) => r.getAssetBySymbol(chain, "XSGD") },
|
|
639
|
-
TRYB: { resolve: (r, chain) => r.getAssetBySymbol(chain, "TRYB") }
|
|
639
|
+
TRYB: { resolve: (r, chain) => r.getAssetBySymbol(chain, "TRYB") },
|
|
640
|
+
BRIDGE: { resolve: (r, chain) => r.getAssetBySymbol(chain, "BRIDGE") }
|
|
640
641
|
};
|
|
641
642
|
var USD_STABLE_SYMBOLS = /* @__PURE__ */ new Set(["USDC", "USDT", "DAI"]);
|
|
642
643
|
function normalizeSymbol(symbol) {
|
|
@@ -774,7 +775,7 @@ function assertPairSymbolsResolve(pairs, registry, chainNames) {
|
|
|
774
775
|
const pairSymbols = new Set(pairs.flatMap((pair) => [pair.token0, pair.token1]));
|
|
775
776
|
for (const pair of pairs) {
|
|
776
777
|
for (const symbol of [pair.token0, pair.token1]) {
|
|
777
|
-
const resolvesSomewhere = chainNames.some((
|
|
778
|
+
const resolvesSomewhere = chainNames.some((chainName2) => registry.getAddress(symbol, chainName2) !== null);
|
|
778
779
|
if (!resolvesSomewhere) {
|
|
779
780
|
throw new Error(
|
|
780
781
|
`pairs.${pair.token0}/${pair.token1}: '${symbol}' does not resolve to a deployed contract on any configured chain`
|
|
@@ -782,15 +783,15 @@ function assertPairSymbolsResolve(pairs, registry, chainNames) {
|
|
|
782
783
|
}
|
|
783
784
|
}
|
|
784
785
|
}
|
|
785
|
-
for (const
|
|
786
|
+
for (const chainName2 of chainNames) {
|
|
786
787
|
const addressOwner = /* @__PURE__ */ new Map();
|
|
787
788
|
for (const symbol of pairSymbols) {
|
|
788
|
-
const address = registry.getAddress(symbol,
|
|
789
|
+
const address = registry.getAddress(symbol, chainName2)?.toLowerCase();
|
|
789
790
|
if (!address) continue;
|
|
790
791
|
const owner = addressOwner.get(address);
|
|
791
792
|
if (owner && normalizeSymbol(owner) !== normalizeSymbol(symbol)) {
|
|
792
793
|
throw new Error(
|
|
793
|
-
`assets: '${symbol}' and '${owner}' both resolve to ${address} on ${
|
|
794
|
+
`assets: '${symbol}' and '${owner}' both resolve to ${address} on ${chainName2} \u2014 symbols must map to distinct contracts`
|
|
794
795
|
);
|
|
795
796
|
}
|
|
796
797
|
addressOwner.set(address, symbol);
|
|
@@ -1495,7 +1496,7 @@ var FillerConfigService = class {
|
|
|
1495
1496
|
return this.fillerConfig?.substratePrivateKey;
|
|
1496
1497
|
}
|
|
1497
1498
|
getEntryPointAddress(chain) {
|
|
1498
|
-
return this.chainConfigService.
|
|
1499
|
+
return this.chainConfigService.getEntryPointAddress(chain);
|
|
1499
1500
|
}
|
|
1500
1501
|
getSolverAccountContractAddress(chain) {
|
|
1501
1502
|
return this.chainConfigService.getSolverAccountAddress(chain);
|
|
@@ -2544,6 +2545,13 @@ var FREE_RPC_URLS = {
|
|
|
2544
2545
|
"https://arb1.arbitrum.io/rpc"
|
|
2545
2546
|
]
|
|
2546
2547
|
};
|
|
2548
|
+
var HYPERBRIDGE_BUNDLER_URLS = {
|
|
2549
|
+
1: "https://bundler.polytope.technology/ethereum",
|
|
2550
|
+
56: "https://bundler.polytope.technology/bsc",
|
|
2551
|
+
137: "https://bundler.polytope.technology/polygon",
|
|
2552
|
+
8453: "https://bundler.polytope.technology/base",
|
|
2553
|
+
42161: "https://bundler.polytope.technology/arbitrum"
|
|
2554
|
+
};
|
|
2547
2555
|
var CHAIN_NATIVE_SYMBOLS = {
|
|
2548
2556
|
1: "ETH",
|
|
2549
2557
|
56: "BNB",
|
|
@@ -2572,7 +2580,8 @@ var INIT_CHAINS = [
|
|
|
2572
2580
|
network: "mainnet",
|
|
2573
2581
|
alchemySubdomain: "eth-mainnet",
|
|
2574
2582
|
explorerUrl: external_links_default.chainExplorers.ethereum,
|
|
2575
|
-
defaultRpcUrls: FREE_RPC_URLS[1]
|
|
2583
|
+
defaultRpcUrls: FREE_RPC_URLS[1],
|
|
2584
|
+
hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[1]
|
|
2576
2585
|
},
|
|
2577
2586
|
{
|
|
2578
2587
|
chainId: 42161,
|
|
@@ -2581,7 +2590,8 @@ var INIT_CHAINS = [
|
|
|
2581
2590
|
network: "mainnet",
|
|
2582
2591
|
alchemySubdomain: "arb-mainnet",
|
|
2583
2592
|
explorerUrl: external_links_default.chainExplorers.arbitrum,
|
|
2584
|
-
defaultRpcUrls: FREE_RPC_URLS[42161]
|
|
2593
|
+
defaultRpcUrls: FREE_RPC_URLS[42161],
|
|
2594
|
+
hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[42161]
|
|
2585
2595
|
},
|
|
2586
2596
|
{
|
|
2587
2597
|
chainId: 8453,
|
|
@@ -2590,7 +2600,8 @@ var INIT_CHAINS = [
|
|
|
2590
2600
|
network: "mainnet",
|
|
2591
2601
|
alchemySubdomain: "base-mainnet",
|
|
2592
2602
|
explorerUrl: external_links_default.chainExplorers.base,
|
|
2593
|
-
defaultRpcUrls: FREE_RPC_URLS[8453]
|
|
2603
|
+
defaultRpcUrls: FREE_RPC_URLS[8453],
|
|
2604
|
+
hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[8453]
|
|
2594
2605
|
},
|
|
2595
2606
|
{
|
|
2596
2607
|
chainId: 137,
|
|
@@ -2599,7 +2610,8 @@ var INIT_CHAINS = [
|
|
|
2599
2610
|
network: "mainnet",
|
|
2600
2611
|
alchemySubdomain: "polygon-mainnet",
|
|
2601
2612
|
explorerUrl: external_links_default.chainExplorers.polygon,
|
|
2602
|
-
defaultRpcUrls: FREE_RPC_URLS[137]
|
|
2613
|
+
defaultRpcUrls: FREE_RPC_URLS[137],
|
|
2614
|
+
hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[137]
|
|
2603
2615
|
},
|
|
2604
2616
|
{
|
|
2605
2617
|
chainId: 56,
|
|
@@ -2608,7 +2620,8 @@ var INIT_CHAINS = [
|
|
|
2608
2620
|
network: "mainnet",
|
|
2609
2621
|
alchemySubdomain: "bnb-mainnet",
|
|
2610
2622
|
explorerUrl: external_links_default.chainExplorers.bnb,
|
|
2611
|
-
defaultRpcUrls: FREE_RPC_URLS[56]
|
|
2623
|
+
defaultRpcUrls: FREE_RPC_URLS[56],
|
|
2624
|
+
hyperbridgeBundlerUrl: HYPERBRIDGE_BUNDLER_URLS[56]
|
|
2612
2625
|
},
|
|
2613
2626
|
{
|
|
2614
2627
|
chainId: 11155111,
|
|
@@ -3122,6 +3135,8 @@ var LimitOrderService = class {
|
|
|
3122
3135
|
this.delegationService = delegationService;
|
|
3123
3136
|
this.startingNonce = startingNonce;
|
|
3124
3137
|
this.vaultBalances = vaultBalances;
|
|
3138
|
+
/** Re-signs in flight, by order id. A fill's resize waits one out rather than reposting alongside it. */
|
|
3139
|
+
this.resigning = /* @__PURE__ */ new Map();
|
|
3125
3140
|
this.logger = loggers.get("limit-orders");
|
|
3126
3141
|
}
|
|
3127
3142
|
/**
|
|
@@ -3215,6 +3230,7 @@ var LimitOrderService = class {
|
|
|
3215
3230
|
bookExpiresAt: null,
|
|
3216
3231
|
bookPrice: null,
|
|
3217
3232
|
orderNonce: order.orderNonce,
|
|
3233
|
+
entryPoint: null,
|
|
3218
3234
|
status: "cancelled",
|
|
3219
3235
|
lastError: null
|
|
3220
3236
|
}),
|
|
@@ -3481,24 +3497,32 @@ var LimitOrderService = class {
|
|
|
3481
3497
|
async resize(remaining, delivered) {
|
|
3482
3498
|
const id = remaining.id;
|
|
3483
3499
|
const floor = await this.dustFloor(remaining);
|
|
3484
|
-
|
|
3500
|
+
for (let resigning = this.resigning.get(id); resigning; resigning = this.resigning.get(id)) await resigning;
|
|
3501
|
+
const order = await this.store.setStatus(id, "resizing", null, POSTABLE);
|
|
3502
|
+
if (!order) return null;
|
|
3503
|
+
if (BigInt(order.remaining) < floor) {
|
|
3485
3504
|
this.logger.info(
|
|
3486
|
-
{ id, remaining:
|
|
3505
|
+
{ id, remaining: order.remaining, floor: floor.toString() },
|
|
3487
3506
|
"Limit order worked down past the dust floor; closing it"
|
|
3488
3507
|
);
|
|
3489
|
-
if (
|
|
3490
|
-
const closed = await this.store.setPosting(
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3508
|
+
if (order.commitment) await this.withdraw(order.commitment);
|
|
3509
|
+
const closed = await this.store.setPosting(
|
|
3510
|
+
id,
|
|
3511
|
+
{
|
|
3512
|
+
commitment: null,
|
|
3513
|
+
bookExpiresAt: null,
|
|
3514
|
+
bookPrice: null,
|
|
3515
|
+
orderNonce: order.orderNonce,
|
|
3516
|
+
entryPoint: null,
|
|
3517
|
+
status: "filled",
|
|
3518
|
+
lastError: null
|
|
3519
|
+
},
|
|
3520
|
+
POSTABLE
|
|
3521
|
+
);
|
|
3498
3522
|
if (closed) this.report({ kind: "filled", order: closed });
|
|
3499
3523
|
return closed;
|
|
3500
3524
|
}
|
|
3501
|
-
const resized = await this.repost(
|
|
3525
|
+
const resized = await this.repost(order);
|
|
3502
3526
|
if (resized) this.report({ kind: "resized", order: resized, delivered });
|
|
3503
3527
|
return resized;
|
|
3504
3528
|
}
|
|
@@ -3511,7 +3535,7 @@ var LimitOrderService = class {
|
|
|
3511
3535
|
*/
|
|
3512
3536
|
async repost(order) {
|
|
3513
3537
|
if (!order) return null;
|
|
3514
|
-
if (isLocal(order)) return this.store.setStatus(order.id, "open");
|
|
3538
|
+
if (isLocal(order)) return this.store.setStatus(order.id, "open", null, POSTABLE);
|
|
3515
3539
|
if (hasExpired(order.expiresAt, /* @__PURE__ */ new Date())) return this.retire(order);
|
|
3516
3540
|
if (order.commitment) {
|
|
3517
3541
|
const withdrawn = await this.withdraw(order.commitment);
|
|
@@ -3522,10 +3546,13 @@ var LimitOrderService = class {
|
|
|
3522
3546
|
{ id: order.id, commitment: order.commitment, err: message },
|
|
3523
3547
|
"Could not clear the old entry; leaving the posting alone rather than adding a second"
|
|
3524
3548
|
);
|
|
3525
|
-
return this.store.setStatus(order.id, "open", message);
|
|
3549
|
+
return this.store.setStatus(order.id, "open", message, POSTABLE);
|
|
3526
3550
|
}
|
|
3527
3551
|
}
|
|
3528
|
-
await this.store.setStatus(order.id, "resizing")
|
|
3552
|
+
if (!await this.store.setStatus(order.id, "resizing", null, POSTABLE)) {
|
|
3553
|
+
this.logger.info({ id: order.id }, "Limit order closed while it was being reposted; leaving it closed");
|
|
3554
|
+
return null;
|
|
3555
|
+
}
|
|
3529
3556
|
const nonce = (BigInt(order.orderNonce) + 1n).toString();
|
|
3530
3557
|
return (await this.post({ ...order, commitment: null, orderNonce: nonce })).order;
|
|
3531
3558
|
}
|
|
@@ -3568,7 +3595,9 @@ var LimitOrderService = class {
|
|
|
3568
3595
|
* They drift apart when a request never got an answer or the process died
|
|
3569
3596
|
* between two of them: an entry the orderbook still lists that nothing here
|
|
3570
3597
|
* owns, an order here whose entry has gone, or an entry the orderbook has cut
|
|
3571
|
-
* down because the solver cannot cover what it quoted.
|
|
3598
|
+
* down because the solver cannot cover what it quoted. A posted op is also
|
|
3599
|
+
* signed over one EntryPoint, so when the fill chain moves to another, every
|
|
3600
|
+
* entry signed before the move is withdrawn and posted again.
|
|
3572
3601
|
*/
|
|
3573
3602
|
async reconcile(now = /* @__PURE__ */ new Date()) {
|
|
3574
3603
|
const [entries, live] = await Promise.all([this.postedOrders(), this.live()]);
|
|
@@ -3595,7 +3624,17 @@ var LimitOrderService = class {
|
|
|
3595
3624
|
}
|
|
3596
3625
|
for (const order of live) {
|
|
3597
3626
|
if (isLocal(order)) continue;
|
|
3598
|
-
if (order.commitment && found.has(order.commitment.toLowerCase()))
|
|
3627
|
+
if (order.commitment && found.has(order.commitment.toLowerCase())) {
|
|
3628
|
+
try {
|
|
3629
|
+
if (await this.resign(order)) report.reposted++;
|
|
3630
|
+
} catch (err) {
|
|
3631
|
+
this.logger.error(
|
|
3632
|
+
{ id: order.id, err },
|
|
3633
|
+
"Could not sign the limit order again for its chain's EntryPoint"
|
|
3634
|
+
);
|
|
3635
|
+
}
|
|
3636
|
+
continue;
|
|
3637
|
+
}
|
|
3599
3638
|
if (sinceMs(order.updatedAt, now) < POST_GRACE_MS) continue;
|
|
3600
3639
|
this.logger.warn({ id: order.id }, "Limit order has no entry on the orderbook; posting it again");
|
|
3601
3640
|
try {
|
|
@@ -3607,6 +3646,45 @@ var LimitOrderService = class {
|
|
|
3607
3646
|
}
|
|
3608
3647
|
return report;
|
|
3609
3648
|
}
|
|
3649
|
+
/**
|
|
3650
|
+
* Withdraws a posting signed for an EntryPoint its fill chain no longer uses,
|
|
3651
|
+
* and posts it again signed for the one it uses now. Answers whether a new
|
|
3652
|
+
* posting landed.
|
|
3653
|
+
*
|
|
3654
|
+
* The row is read again and claimed out of `open` first: a fill can have worked
|
|
3655
|
+
* it down or started its own repost since reconciliation listed it, and that
|
|
3656
|
+
* write is the one to follow. A fill that lands once the claim is taken waits
|
|
3657
|
+
* in `resize` for this to finish. The EntryPoint is only written with a posting
|
|
3658
|
+
* that landed, so a pass cut short anywhere leaves a row the next pass still
|
|
3659
|
+
* reads as stale, or as having no entry, and finishes the job.
|
|
3660
|
+
*/
|
|
3661
|
+
async resign(listed) {
|
|
3662
|
+
const entryPoint = this.configService.getEntryPointAddress(listed.fillChain);
|
|
3663
|
+
if (!entryPoint || sameAddress(signedFor(listed), entryPoint)) return false;
|
|
3664
|
+
const order = await this.store.get(listed.id);
|
|
3665
|
+
if (!order || order.status !== "open" || order.commitment !== listed.commitment) return false;
|
|
3666
|
+
if (sameAddress(signedFor(order), entryPoint) || this.resigning.has(order.id)) return false;
|
|
3667
|
+
const resigned = this.signAgain(order, entryPoint);
|
|
3668
|
+
this.resigning.set(
|
|
3669
|
+
order.id,
|
|
3670
|
+
resigned.catch(() => false)
|
|
3671
|
+
);
|
|
3672
|
+
try {
|
|
3673
|
+
return await resigned;
|
|
3674
|
+
} finally {
|
|
3675
|
+
this.resigning.delete(order.id);
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
async signAgain(order, entryPoint) {
|
|
3679
|
+
const claimed = await this.store.setStatus(order.id, "resizing", null, ["open"]);
|
|
3680
|
+
if (!claimed) return false;
|
|
3681
|
+
this.logger.warn(
|
|
3682
|
+
{ id: order.id, commitment: order.commitment, signedFor: signedFor(order), entryPoint },
|
|
3683
|
+
"Limit order was signed for an EntryPoint its chain no longer uses; signing it again"
|
|
3684
|
+
);
|
|
3685
|
+
const posted = await this.repost(claimed);
|
|
3686
|
+
return !!posted?.commitment && sameAddress(signedFor(posted), entryPoint);
|
|
3687
|
+
}
|
|
3610
3688
|
/**
|
|
3611
3689
|
* Lowers every open order's `remaining` to what the solver's account will still
|
|
3612
3690
|
* pay for it, and puts the order back on the book at that size.
|
|
@@ -3688,6 +3766,7 @@ var LimitOrderService = class {
|
|
|
3688
3766
|
bookExpiresAt: null,
|
|
3689
3767
|
bookPrice: null,
|
|
3690
3768
|
orderNonce: order.orderNonce,
|
|
3769
|
+
entryPoint: null,
|
|
3691
3770
|
status: "expired",
|
|
3692
3771
|
lastError: null
|
|
3693
3772
|
});
|
|
@@ -3744,11 +3823,11 @@ var LimitOrderService = class {
|
|
|
3744
3823
|
}
|
|
3745
3824
|
/** Builds and submits the posting, then writes the orderbook's answer onto the row. */
|
|
3746
3825
|
async post(order) {
|
|
3747
|
-
const { result, orderNonce } = await this.submit(order);
|
|
3826
|
+
const { result, orderNonce, entryPoint } = await this.submit(order);
|
|
3748
3827
|
if (result.kind === "accepted" || result.kind === "unchanged") {
|
|
3749
3828
|
const posted = result.order;
|
|
3750
3829
|
this.logger.info(
|
|
3751
|
-
{ id: order.id, commitment: posted.commitment, price: posted.price },
|
|
3830
|
+
{ id: order.id, commitment: posted.commitment, price: posted.price, entryPoint },
|
|
3752
3831
|
"Limit order posted to the orderbook"
|
|
3753
3832
|
);
|
|
3754
3833
|
const stored = await this.store.setPosting(
|
|
@@ -3758,6 +3837,10 @@ var LimitOrderService = class {
|
|
|
3758
3837
|
bookExpiresAt: posted.expiresAt,
|
|
3759
3838
|
bookPrice: posted.price,
|
|
3760
3839
|
orderNonce: orderNonce.toString(),
|
|
3840
|
+
// An entry the orderbook already held was signed by an earlier attempt,
|
|
3841
|
+
// for whichever EntryPoint was current then. Recorded as unknown, which
|
|
3842
|
+
// reconciliation reads as v0.8 and signs again once the chain is past it.
|
|
3843
|
+
entryPoint: result.kind === "accepted" ? entryPoint : null,
|
|
3761
3844
|
status: "open",
|
|
3762
3845
|
lastError: null
|
|
3763
3846
|
},
|
|
@@ -3787,6 +3870,7 @@ var LimitOrderService = class {
|
|
|
3787
3870
|
bookExpiresAt: null,
|
|
3788
3871
|
bookPrice: null,
|
|
3789
3872
|
orderNonce: orderNonce.toString(),
|
|
3873
|
+
entryPoint: null,
|
|
3790
3874
|
status: "open",
|
|
3791
3875
|
lastError: message
|
|
3792
3876
|
}),
|
|
@@ -3809,22 +3893,30 @@ var LimitOrderService = class {
|
|
|
3809
3893
|
* only the second one.
|
|
3810
3894
|
*/
|
|
3811
3895
|
async submit(order) {
|
|
3896
|
+
const entryPoint = this.entryPointFor(order.fillChain);
|
|
3812
3897
|
const orderNonce = BigInt(order.orderNonce);
|
|
3813
|
-
const attempt = await this.buildAndSubmit(order, orderNonce);
|
|
3898
|
+
const attempt = await this.buildAndSubmit(order, orderNonce, entryPoint);
|
|
3814
3899
|
const first = attempt.result;
|
|
3815
3900
|
if (first.kind !== "rejected" || first.code !== "REPLAYED" && first.code !== "ORDER_EXISTS") {
|
|
3816
|
-
return { result: first, orderNonce };
|
|
3901
|
+
return { result: first, orderNonce, entryPoint };
|
|
3817
3902
|
}
|
|
3818
3903
|
if (first.code === "ORDER_EXISTS" && attempt.commitment) {
|
|
3819
3904
|
const live = await this.entryAt(order, attempt.commitment);
|
|
3820
3905
|
if (live) {
|
|
3821
3906
|
this.logger.info({ id: order.id, commitment: live.commitment }, "This posting is already on the orderbook");
|
|
3822
|
-
return { result: { kind: "unchanged", order: live }, orderNonce };
|
|
3907
|
+
return { result: { kind: "unchanged", order: live }, orderNonce, entryPoint };
|
|
3823
3908
|
}
|
|
3824
3909
|
}
|
|
3825
3910
|
this.logger.warn({ id: order.id, code: first.code }, "Orderbook has seen this op before; reposting on a new nonce");
|
|
3826
3911
|
const retried = orderNonce + 1n;
|
|
3827
|
-
|
|
3912
|
+
const { result } = await this.buildAndSubmit(order, retried, entryPoint);
|
|
3913
|
+
return { result, orderNonce: retried, entryPoint };
|
|
3914
|
+
}
|
|
3915
|
+
/** The EntryPoint a posting on `chain` is signed for now. */
|
|
3916
|
+
entryPointFor(chain) {
|
|
3917
|
+
const entryPoint = this.configService.getEntryPointAddress(chain);
|
|
3918
|
+
if (!entryPoint) throw new LimitOrderValidationError(`No EntryPoint is configured for ${chain}`);
|
|
3919
|
+
return entryPoint;
|
|
3828
3920
|
}
|
|
3829
3921
|
/**
|
|
3830
3922
|
* The entry the orderbook holds at `commitment`, if it holds one. A lookup that
|
|
@@ -3840,8 +3932,8 @@ var LimitOrderService = class {
|
|
|
3840
3932
|
}
|
|
3841
3933
|
}
|
|
3842
3934
|
/** The orderbook's answer, and the commitment the op we sent hashes to. */
|
|
3843
|
-
async buildAndSubmit(order, orderNonce) {
|
|
3844
|
-
const { commitment, userOp } = await this.buildUserOp(order, orderNonce);
|
|
3935
|
+
async buildAndSubmit(order, orderNonce, entryPoint) {
|
|
3936
|
+
const { commitment, userOp } = await this.buildUserOp(order, orderNonce, entryPoint);
|
|
3845
3937
|
try {
|
|
3846
3938
|
return { result: await this.client.submitOrder(userOp), commitment };
|
|
3847
3939
|
} catch (err) {
|
|
@@ -3851,7 +3943,7 @@ var LimitOrderService = class {
|
|
|
3851
3943
|
throw err;
|
|
3852
3944
|
}
|
|
3853
3945
|
}
|
|
3854
|
-
async buildUserOp(order, orderNonce) {
|
|
3946
|
+
async buildUserOp(order, orderNonce, entryPointAddress) {
|
|
3855
3947
|
const baseToken = this.assetRegistry.getAddress(order.base, order.fillChain);
|
|
3856
3948
|
const quoteToken = this.assetRegistry.getAddress(order.quote, order.fillChain);
|
|
3857
3949
|
const [baseDecimals, quoteDecimals] = await Promise.all([
|
|
@@ -3866,10 +3958,6 @@ var LimitOrderService = class {
|
|
|
3866
3958
|
baseDecimals,
|
|
3867
3959
|
quoteDecimals
|
|
3868
3960
|
});
|
|
3869
|
-
const entryPointAddress = this.configService.getEntryPointAddress(order.fillChain);
|
|
3870
|
-
if (!entryPointAddress) {
|
|
3871
|
-
throw new LimitOrderValidationError(`No EntryPoint is configured for ${order.fillChain}`);
|
|
3872
|
-
}
|
|
3873
3961
|
return this.contractService.prepareLimitOrderUserOp({
|
|
3874
3962
|
fillChain: order.fillChain,
|
|
3875
3963
|
entryPointAddress,
|
|
@@ -3900,6 +3988,12 @@ function hasExpired(expiresAt, now) {
|
|
|
3900
3988
|
const at = Date.parse(expiresAt);
|
|
3901
3989
|
return !Number.isNaN(at) && at <= now.getTime();
|
|
3902
3990
|
}
|
|
3991
|
+
function signedFor(order) {
|
|
3992
|
+
return order.entryPoint ?? import_sdk4.ENTRY_POINT_V08;
|
|
3993
|
+
}
|
|
3994
|
+
function sameAddress(a, b) {
|
|
3995
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
3996
|
+
}
|
|
3903
3997
|
function sinceMs(updatedAt, now) {
|
|
3904
3998
|
const written = Date.parse(`${updatedAt.replace(" ", "T")}Z`);
|
|
3905
3999
|
return Number.isNaN(written) ? Number.POSITIVE_INFINITY : now.getTime() - written;
|
|
@@ -4364,6 +4458,80 @@ async function patchRuntimeState(store, patch) {
|
|
|
4364
4458
|
return next;
|
|
4365
4459
|
}
|
|
4366
4460
|
|
|
4461
|
+
// src/services/bundler-preflight.ts
|
|
4462
|
+
var BUNDLER_PREFLIGHT_TIMEOUT_MS = 1e4;
|
|
4463
|
+
async function assertBundlersServeEntryPoint(chains, configService, options = {}) {
|
|
4464
|
+
const timeoutMs = options.timeoutMs ?? BUNDLER_PREFLIGHT_TIMEOUT_MS;
|
|
4465
|
+
const logger = moduleLogger(configService.loggers, "bundler-preflight");
|
|
4466
|
+
const failures = await Promise.all(
|
|
4467
|
+
chains.map(async ({ chainId, bundlerUrl }) => {
|
|
4468
|
+
if (options.watchOnly?.[chainId] === true) return void 0;
|
|
4469
|
+
if (!bundlerUrl?.trim()) return void 0;
|
|
4470
|
+
const entryPoint = configService.getEntryPointAddress(formatChainKey(chainId));
|
|
4471
|
+
if (!entryPoint) return void 0;
|
|
4472
|
+
return checkBundler(chainId, bundlerUrl.trim(), entryPoint, timeoutMs, logger);
|
|
4473
|
+
})
|
|
4474
|
+
);
|
|
4475
|
+
const messages = failures.filter((failure) => failure !== void 0);
|
|
4476
|
+
if (messages.length > 0) throw new Error(messages.join("; "));
|
|
4477
|
+
}
|
|
4478
|
+
function servesEntryPoint(listed, entryPoint) {
|
|
4479
|
+
return Array.isArray(listed) && listed.some((address) => String(address).toLowerCase() === entryPoint.toLowerCase());
|
|
4480
|
+
}
|
|
4481
|
+
function describeEntryPoints(listed) {
|
|
4482
|
+
return Array.isArray(listed) && listed.length > 0 ? listed.map(String).join(", ") : "none";
|
|
4483
|
+
}
|
|
4484
|
+
function bundlerHost(url) {
|
|
4485
|
+
try {
|
|
4486
|
+
return new URL(url).host || "an unparseable URL";
|
|
4487
|
+
} catch {
|
|
4488
|
+
return "an unparseable URL";
|
|
4489
|
+
}
|
|
4490
|
+
}
|
|
4491
|
+
function chainName(chainId) {
|
|
4492
|
+
const label = chainByChainId(chainId)?.label;
|
|
4493
|
+
return label ? `${label} (${formatChainKey(chainId)})` : formatChainKey(chainId);
|
|
4494
|
+
}
|
|
4495
|
+
async function checkBundler(chainId, bundlerUrl, entryPoint, timeoutMs, logger) {
|
|
4496
|
+
const host = bundlerHost(bundlerUrl);
|
|
4497
|
+
const subject = `Bundler ${host} for ${chainName(chainId)}`;
|
|
4498
|
+
const signal = AbortSignal.timeout(timeoutMs);
|
|
4499
|
+
let listed;
|
|
4500
|
+
try {
|
|
4501
|
+
listed = await supportedEntryPoints(bundlerUrl, signal);
|
|
4502
|
+
} catch (err) {
|
|
4503
|
+
const reason = signal.aborted ? `timed out after ${timeoutMs}ms` : errorReason(err);
|
|
4504
|
+
logger.warn(
|
|
4505
|
+
{ chain: formatChainKey(chainId), bundler: host },
|
|
4506
|
+
`${subject} did not answer eth_supportedEntryPoints: ${reason.split(bundlerUrl).join(host)}. Continuing without confirming it serves EntryPoint ${entryPoint}`
|
|
4507
|
+
);
|
|
4508
|
+
return void 0;
|
|
4509
|
+
}
|
|
4510
|
+
if (servesEntryPoint(listed, entryPoint)) return void 0;
|
|
4511
|
+
return `${subject} does not support EntryPoint ${entryPoint}; it lists ${describeEntryPoints(listed)}`;
|
|
4512
|
+
}
|
|
4513
|
+
async function supportedEntryPoints(bundlerUrl, signal) {
|
|
4514
|
+
const response = await fetch(bundlerUrl, {
|
|
4515
|
+
method: "POST",
|
|
4516
|
+
headers: { "Content-Type": "application/json" },
|
|
4517
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_supportedEntryPoints", params: [] }),
|
|
4518
|
+
signal
|
|
4519
|
+
});
|
|
4520
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
4521
|
+
const json = await response.json();
|
|
4522
|
+
if (json.error) throw new Error(json.error.message ?? JSON.stringify(json.error));
|
|
4523
|
+
const { result } = json;
|
|
4524
|
+
if (!Array.isArray(result) || !result.every((address) => typeof address === "string")) {
|
|
4525
|
+
throw new Error("the result is not a list of addresses");
|
|
4526
|
+
}
|
|
4527
|
+
return result;
|
|
4528
|
+
}
|
|
4529
|
+
function errorReason(err) {
|
|
4530
|
+
if (!(err instanceof Error)) return String(err);
|
|
4531
|
+
const { cause } = err;
|
|
4532
|
+
return cause instanceof Error ? `${err.message} (${cause.message})` : err.message;
|
|
4533
|
+
}
|
|
4534
|
+
|
|
4367
4535
|
// src/services/wallet/mpcvault.ts
|
|
4368
4536
|
var import_viem7 = require("viem");
|
|
4369
4537
|
var grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
@@ -17527,9 +17695,9 @@ var INTENT_GATEWAY_V2_ABI = [
|
|
|
17527
17695
|
internalType: "bytes32"
|
|
17528
17696
|
},
|
|
17529
17697
|
{
|
|
17530
|
-
name: "
|
|
17531
|
-
type: "
|
|
17532
|
-
internalType: "
|
|
17698
|
+
name: "userOpHash",
|
|
17699
|
+
type: "bytes32",
|
|
17700
|
+
internalType: "bytes32"
|
|
17533
17701
|
},
|
|
17534
17702
|
{
|
|
17535
17703
|
name: "signature",
|
|
@@ -18904,10 +19072,13 @@ var LIMIT_ORDER_CALL_GAS_LIMIT = 500000n;
|
|
|
18904
19072
|
var LIMIT_ORDER_VERIFICATION_GAS_LIMIT = 150000n;
|
|
18905
19073
|
var LIMIT_ORDER_PRE_VERIFICATION_GAS = 50000n;
|
|
18906
19074
|
var BID_DISCOVERY_PAD_SECONDS = 30;
|
|
19075
|
+
var BLOCK_TIME_SAMPLE_BLOCKS = 1000n;
|
|
19076
|
+
var FALLBACK_BLOCK_TIME_SECONDS = 2;
|
|
18907
19077
|
var ContractInteractionService = class {
|
|
18908
19078
|
constructor(clientManager, configService, signer, sharedCacheService) {
|
|
18909
19079
|
this.clientManager = clientManager;
|
|
18910
19080
|
this.sdkHelperCache = /* @__PURE__ */ new Map();
|
|
19081
|
+
this.blockTimes = /* @__PURE__ */ new Map();
|
|
18911
19082
|
this.logger = moduleLogger(configService.loggers, "contract-service");
|
|
18912
19083
|
this.configService = configService;
|
|
18913
19084
|
this.cacheService = sharedCacheService || new CacheService();
|
|
@@ -18967,11 +19138,20 @@ var ContractInteractionService = class {
|
|
|
18967
19138
|
try {
|
|
18968
19139
|
const chainIds = this.configService.getConfiguredChainIds();
|
|
18969
19140
|
const chainNames = chainIds.map((id) => `EVM-${id}`);
|
|
18970
|
-
|
|
19141
|
+
await Promise.all(
|
|
19142
|
+
chainNames.map(async (chainName2) => {
|
|
19143
|
+
try {
|
|
19144
|
+
await this.blockTimeSeconds(chainName2);
|
|
19145
|
+
} catch (err) {
|
|
19146
|
+
this.logger.warn({ err, chain: chainName2 }, "Could not measure the block time at startup");
|
|
19147
|
+
}
|
|
19148
|
+
})
|
|
19149
|
+
);
|
|
19150
|
+
for (const chainName2 of chainNames) {
|
|
18971
19151
|
try {
|
|
18972
|
-
await this.getFeeTokenWithDecimals(
|
|
19152
|
+
await this.getFeeTokenWithDecimals(chainName2);
|
|
18973
19153
|
} catch (err) {
|
|
18974
|
-
this.logger.warn({ err, chain:
|
|
19154
|
+
this.logger.warn({ err, chain: chainName2 }, "Could not prewarm fee token decimals");
|
|
18975
19155
|
}
|
|
18976
19156
|
}
|
|
18977
19157
|
for (const destChain of chainNames) {
|
|
@@ -19556,18 +19736,59 @@ var ContractInteractionService = class {
|
|
|
19556
19736
|
*
|
|
19557
19737
|
* Operators configure seconds because that is the unit the risk is actually in, but the
|
|
19558
19738
|
* contract compares against block numbers so `order.deadline` and this read the same
|
|
19559
|
-
* clock. The conversion uses the chain's
|
|
19560
|
-
* up, since erring long costs a slightly stale quote while erring
|
|
19561
|
-
* winnable bids.
|
|
19739
|
+
* clock. The conversion uses the chain's block time from {@link blockTimeSeconds}; it is
|
|
19740
|
+
* deliberately rounded up, since erring long costs a slightly stale quote while erring
|
|
19741
|
+
* short silently drops winnable bids.
|
|
19562
19742
|
*/
|
|
19563
19743
|
async bidValidUntilBlock(chain) {
|
|
19564
19744
|
const client = this.clientManager.getPublicClient(chain);
|
|
19565
|
-
const currentBlock = await client.getBlockNumber();
|
|
19566
|
-
const blockTimeMs = client.chain?.blockTime;
|
|
19567
|
-
const blockTimeSec = blockTimeMs ? blockTimeMs / 1e3 : 2;
|
|
19745
|
+
const [currentBlock, blockTimeSec] = await Promise.all([client.getBlockNumber(), this.blockTimeSeconds(chain)]);
|
|
19568
19746
|
const windowSec = this.configService.getBidValiditySeconds() + BID_DISCOVERY_PAD_SECONDS;
|
|
19569
19747
|
return currentBlock + BigInt(Math.ceil(windowSec / blockTimeSec));
|
|
19570
19748
|
}
|
|
19749
|
+
/**
|
|
19750
|
+
* Seconds per block on `chain`. Measured once, when simplex starts ({@link initCache}), and
|
|
19751
|
+
* kept until it stops. A chain with no measurement yet, because it was added later or its
|
|
19752
|
+
* blocks could not be read at startup, is measured at its first bid.
|
|
19753
|
+
*
|
|
19754
|
+
* The nominal `Chain.blockTime` cannot be relied on alone: it goes stale when a chain
|
|
19755
|
+
* speeds up (BSC makes a block every 0.45s against a declared 0.75s) and some chains
|
|
19756
|
+
* declare none. Converting at a slower block time than the chain's ends a bid early, so
|
|
19757
|
+
* the faster of the measured and the declared figure is used, and a stalled or idle
|
|
19758
|
+
* stretch in the sample never shortens a bid.
|
|
19759
|
+
*
|
|
19760
|
+
* Blocks that cannot be read fall back to the declared figure, then to
|
|
19761
|
+
* {@link FALLBACK_BLOCK_TIME_SECONDS}. That answer is not kept.
|
|
19762
|
+
*/
|
|
19763
|
+
blockTimeSeconds(chain) {
|
|
19764
|
+
const known = this.blockTimes.get(chain);
|
|
19765
|
+
if (known) return known;
|
|
19766
|
+
const client = this.clientManager.getPublicClient(chain);
|
|
19767
|
+
const declaredMs = client.chain?.blockTime;
|
|
19768
|
+
const declared = declaredMs ? declaredMs / 1e3 : void 0;
|
|
19769
|
+
const seconds = this.measureBlockTimeSeconds(client).then((measured) => declared ? Math.min(measured, declared) : measured).catch((err) => {
|
|
19770
|
+
this.blockTimes.delete(chain);
|
|
19771
|
+
const fallback2 = declared ?? FALLBACK_BLOCK_TIME_SECONDS;
|
|
19772
|
+
this.logger.warn(
|
|
19773
|
+
{ err, chain, seconds: fallback2 },
|
|
19774
|
+
"Could not measure the block time; using the declared one"
|
|
19775
|
+
);
|
|
19776
|
+
return fallback2;
|
|
19777
|
+
});
|
|
19778
|
+
this.blockTimes.set(chain, seconds);
|
|
19779
|
+
return seconds;
|
|
19780
|
+
}
|
|
19781
|
+
/** The average block time over the chain's last {@link BLOCK_TIME_SAMPLE_BLOCKS} blocks, in seconds. */
|
|
19782
|
+
async measureBlockTimeSeconds(client) {
|
|
19783
|
+
const head = await client.getBlock({ blockTag: "latest" });
|
|
19784
|
+
const span = head.number < BLOCK_TIME_SAMPLE_BLOCKS ? head.number : BLOCK_TIME_SAMPLE_BLOCKS;
|
|
19785
|
+
const tail = await client.getBlock({ blockNumber: head.number - span });
|
|
19786
|
+
const measured = Number(head.timestamp - tail.timestamp) / Number(span);
|
|
19787
|
+
if (!Number.isFinite(measured) || measured <= 0) {
|
|
19788
|
+
throw new Error(`${span} blocks span ${head.timestamp - tail.timestamp}s`);
|
|
19789
|
+
}
|
|
19790
|
+
return measured;
|
|
19791
|
+
}
|
|
19571
19792
|
/**
|
|
19572
19793
|
* Builds the signed, never-executed UserOperation that carries one limit order
|
|
19573
19794
|
* to the HyperFX orderbook.
|
|
@@ -19747,6 +19968,8 @@ var import_viem24 = require("viem");
|
|
|
19747
19968
|
var FALLBACK_VERIFICATION_GAS_LIMIT = 250000n;
|
|
19748
19969
|
var FALLBACK_CALL_GAS_LIMIT = 1500000n;
|
|
19749
19970
|
var FALLBACK_PRE_VERIFICATION_GAS = 150000n;
|
|
19971
|
+
var PRIORITY_FEE_BUMP_PERCENT = 8n;
|
|
19972
|
+
var MAX_FEE_BUMP_PERCENT = 10n;
|
|
19750
19973
|
var UserOpSender = class {
|
|
19751
19974
|
constructor(clientManager, configService, signer) {
|
|
19752
19975
|
this.clientManager = clientManager;
|
|
@@ -19927,7 +20150,11 @@ var UserOpSender = class {
|
|
|
19927
20150
|
);
|
|
19928
20151
|
return userOp;
|
|
19929
20152
|
}
|
|
19930
|
-
/**
|
|
20153
|
+
/**
|
|
20154
|
+
* Mirrors the gas-price selection used by the delegation and fill paths: Pimlico's and
|
|
20155
|
+
* Alchemy's own pricing, otherwise the chain's gas price raised to the priority fee a
|
|
20156
|
+
* rundler bundler requires, when the bundler gives one.
|
|
20157
|
+
*/
|
|
19931
20158
|
async getGasPrice(bundlerUrl, publicClient, chainId) {
|
|
19932
20159
|
const lower = bundlerUrl.toLowerCase();
|
|
19933
20160
|
if (lower.includes("pimlico.io")) {
|
|
@@ -19939,19 +20166,33 @@ var UserOpSender = class {
|
|
|
19939
20166
|
return { maxFeePerGas: BigInt(res.fast.maxFeePerGas), maxPriorityFeePerGas: BigInt(res.fast.maxPriorityFeePerGas) };
|
|
19940
20167
|
}
|
|
19941
20168
|
if (lower.includes("alchemy.com")) {
|
|
19942
|
-
const [
|
|
20169
|
+
const [rundlerPriorityFee2, latestBlock2] = await Promise.all([
|
|
19943
20170
|
this.sendBundlerRpc(bundlerUrl, import_sdk8.BundlerMethod.RUNDLER_MAX_PRIORITY_FEE_PER_GAS, []),
|
|
19944
20171
|
publicClient.getBlock({ blockTag: "latest" })
|
|
19945
20172
|
]);
|
|
19946
|
-
const baseFeePerGas =
|
|
20173
|
+
const baseFeePerGas = latestBlock2.baseFeePerGas ?? await publicClient.getGasPrice();
|
|
19947
20174
|
const isArbitrum = BigInt(chainId) === 42161n;
|
|
19948
20175
|
const prioBump = isArbitrum ? 0n : 25n;
|
|
19949
|
-
const maxPriorityFeePerGas = BigInt(
|
|
20176
|
+
const maxPriorityFeePerGas = BigInt(rundlerPriorityFee2) + BigInt(rundlerPriorityFee2) * prioBump / 100n;
|
|
19950
20177
|
const bufferedBaseFee = baseFeePerGas + baseFeePerGas * 50n / 100n;
|
|
19951
20178
|
return { maxFeePerGas: bufferedBaseFee + maxPriorityFeePerGas, maxPriorityFeePerGas };
|
|
19952
20179
|
}
|
|
19953
|
-
const gasPrice = await
|
|
19954
|
-
|
|
20180
|
+
const [gasPrice, rundlerPriorityFee] = await Promise.all([
|
|
20181
|
+
publicClient.getGasPrice(),
|
|
20182
|
+
(0, import_sdk8.fetchRundlerPriorityFee)(bundlerUrl)
|
|
20183
|
+
]);
|
|
20184
|
+
const fees = {
|
|
20185
|
+
maxFeePerGas: gasPrice + gasPrice * MAX_FEE_BUMP_PERCENT / 100n,
|
|
20186
|
+
maxPriorityFeePerGas: gasPrice + gasPrice * PRIORITY_FEE_BUMP_PERCENT / 100n
|
|
20187
|
+
};
|
|
20188
|
+
if (rundlerPriorityFee === null) return fees;
|
|
20189
|
+
const latestBlock = await publicClient.getBlock({ blockTag: "latest" });
|
|
20190
|
+
return (0, import_sdk8.applyRundlerPriorityFee)(fees, {
|
|
20191
|
+
rundlerPriorityFee,
|
|
20192
|
+
baseFeePerGas: latestBlock.baseFeePerGas ?? gasPrice,
|
|
20193
|
+
priorityFeeBumpPercent: PRIORITY_FEE_BUMP_PERCENT,
|
|
20194
|
+
maxFeeBumpPercent: MAX_FEE_BUMP_PERCENT
|
|
20195
|
+
});
|
|
19955
20196
|
}
|
|
19956
20197
|
async sendBundlerRpc(bundlerUrl, method, params) {
|
|
19957
20198
|
const response = await fetch(bundlerUrl, {
|
|
@@ -20763,7 +21004,7 @@ var BinanceRebalancer = class {
|
|
|
20763
21004
|
// src/services/rebalancers/cctp.ts
|
|
20764
21005
|
var import_bridge_kit = require("@circle-fin/bridge-kit");
|
|
20765
21006
|
var import_adapter_viem_v2 = require("@circle-fin/adapter-viem-v2");
|
|
20766
|
-
var
|
|
21007
|
+
var import_chains2 = require("@circle-fin/bridge-kit/chains");
|
|
20767
21008
|
var import_sdk11 = require("@hyperbridge/sdk");
|
|
20768
21009
|
var CHAIN_ID_TO_CCTP = {
|
|
20769
21010
|
1: "Ethereum",
|
|
@@ -20816,18 +21057,18 @@ var CctpRebalancer = class {
|
|
|
20816
21057
|
{
|
|
20817
21058
|
addressContext: "user-controlled",
|
|
20818
21059
|
supportedChains: [
|
|
20819
|
-
|
|
20820
|
-
|
|
20821
|
-
|
|
20822
|
-
|
|
20823
|
-
|
|
20824
|
-
|
|
20825
|
-
|
|
20826
|
-
|
|
20827
|
-
|
|
20828
|
-
|
|
20829
|
-
|
|
20830
|
-
|
|
21060
|
+
import_chains2.Ethereum,
|
|
21061
|
+
import_chains2.EthereumSepolia,
|
|
21062
|
+
import_chains2.Arbitrum,
|
|
21063
|
+
import_chains2.ArbitrumSepolia,
|
|
21064
|
+
import_chains2.Base,
|
|
21065
|
+
import_chains2.BaseSepolia,
|
|
21066
|
+
import_chains2.Optimism,
|
|
21067
|
+
import_chains2.OptimismSepolia,
|
|
21068
|
+
import_chains2.Polygon,
|
|
21069
|
+
import_chains2.PolygonAmoy,
|
|
21070
|
+
import_chains2.Unichain,
|
|
21071
|
+
import_chains2.UnichainSepolia
|
|
20831
21072
|
]
|
|
20832
21073
|
}
|
|
20833
21074
|
);
|
|
@@ -24482,8 +24723,8 @@ async function bootFiller(config, options) {
|
|
|
24482
24723
|
};
|
|
24483
24724
|
const configService = new FillerConfigService(resolvedChains, fillerConfigForService, options.loggers);
|
|
24484
24725
|
const chainConfigs = resolvedChains.map((chain) => {
|
|
24485
|
-
const
|
|
24486
|
-
return configService.getChainConfig(
|
|
24726
|
+
const chainName2 = `EVM-${chain.chainId}`;
|
|
24727
|
+
return configService.getChainConfig(chainName2);
|
|
24487
24728
|
});
|
|
24488
24729
|
let watchOnlyConfig;
|
|
24489
24730
|
const globalWatchOnly = options.watchOnlyOverride === true || config.simplex.watchOnly === true;
|
|
@@ -24521,6 +24762,8 @@ async function bootFiller(config, options) {
|
|
|
24521
24762
|
"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]."
|
|
24522
24763
|
);
|
|
24523
24764
|
}
|
|
24765
|
+
logger.info("Checking each bundler serves its chain's EntryPoint...");
|
|
24766
|
+
await assertBundlersServeEntryPoint(resolvedChains, configService, { watchOnly: watchOnlyConfig });
|
|
24524
24767
|
const chainClientManager = new ChainClientManager(configService, options.signer);
|
|
24525
24768
|
const runtimeSigner = chainClientManager.getSigner();
|
|
24526
24769
|
if (options.signer) {
|
|
@@ -24711,10 +24954,10 @@ async function bootFiller(config, options) {
|
|
|
24711
24954
|
const token1 = {};
|
|
24712
24955
|
for (const pair of config.pairs ?? []) {
|
|
24713
24956
|
if (normalizeSymbol(pair.token0) === normalizeSymbol(pair.token1)) continue;
|
|
24714
|
-
for (const
|
|
24715
|
-
const address = assetRegistry.getAddress(pair.token1,
|
|
24957
|
+
for (const chainName2 of configuredChainNames) {
|
|
24958
|
+
const address = assetRegistry.getAddress(pair.token1, chainName2);
|
|
24716
24959
|
if (!address) continue;
|
|
24717
|
-
const list = token1[
|
|
24960
|
+
const list = token1[chainName2] ?? (token1[chainName2] = []);
|
|
24718
24961
|
if (!list.includes(address)) list.push(address);
|
|
24719
24962
|
}
|
|
24720
24963
|
}
|
|
@@ -25110,6 +25353,7 @@ var MemoryLimitOrderStore = class {
|
|
|
25110
25353
|
status: "open",
|
|
25111
25354
|
commitment: null,
|
|
25112
25355
|
orderNonce: order.orderNonce ?? "0",
|
|
25356
|
+
entryPoint: null,
|
|
25113
25357
|
bookExpiresAt: null,
|
|
25114
25358
|
bookPrice: null,
|
|
25115
25359
|
lastError: null,
|
|
@@ -26095,10 +26339,10 @@ var PairController = class {
|
|
|
26095
26339
|
if (assets && Object.keys(assets).length > 0) config.assets = nextAssets;
|
|
26096
26340
|
if (normalizeSymbol(pair.token0) !== normalizeSymbol(pair.token1)) {
|
|
26097
26341
|
for (const chain of this.runtime.resolvedChains) {
|
|
26098
|
-
const
|
|
26099
|
-
const address = assetRegistry.getAddress(pair.token1,
|
|
26342
|
+
const chainName2 = formatChainKey(chain.chainId);
|
|
26343
|
+
const address = assetRegistry.getAddress(pair.token1, chainName2);
|
|
26100
26344
|
if (!address) continue;
|
|
26101
|
-
const list = balanceTokens[
|
|
26345
|
+
const list = balanceTokens[chainName2] ?? (balanceTokens[chainName2] = []);
|
|
26102
26346
|
if (!list.includes(address)) list.push(address);
|
|
26103
26347
|
}
|
|
26104
26348
|
}
|
|
@@ -26203,8 +26447,9 @@ var ChainController = class {
|
|
|
26203
26447
|
`Chain ${chainId} cannot be added as filling: this solver was started without a signer. Restart with \`signer\` to fill.`
|
|
26204
26448
|
);
|
|
26205
26449
|
}
|
|
26206
|
-
configService.addChain(resolved);
|
|
26207
26450
|
const watchOnly = chain.watchOnly ?? (this.runtime.signerless || this.runtime.globalWatchOnly);
|
|
26451
|
+
await assertBundlersServeEntryPoint([resolved], configService, { watchOnly: { [chainId]: watchOnly } });
|
|
26452
|
+
configService.addChain(resolved);
|
|
26208
26453
|
if (watchOnly) intentFiller.setWatchOnly(chainId, true);
|
|
26209
26454
|
try {
|
|
26210
26455
|
if (!inScanner) {
|
|
@@ -26296,6 +26541,9 @@ var ChainController = class {
|
|
|
26296
26541
|
if (!bundlerUrl?.trim()) throw new Error("A bundler URL is required");
|
|
26297
26542
|
const index = this.runtime.resolvedChains.findIndex((chain) => chain.chainId === chainId);
|
|
26298
26543
|
if (index < 0) throw new Error(`Chain ${chainId} is not configured`);
|
|
26544
|
+
await assertBundlersServeEntryPoint([{ chainId, bundlerUrl }], this.runtime.configService, {
|
|
26545
|
+
watchOnly: this.runtime.intentFiller.getWatchOnly()
|
|
26546
|
+
});
|
|
26299
26547
|
this.runtime.configService.setBundlerUrl(chainId, bundlerUrl);
|
|
26300
26548
|
this.runtime.resolvedChains[index].bundlerUrl = bundlerUrl;
|
|
26301
26549
|
this.runtime.config.chains[index].bundlerUrl = bundlerUrl;
|
|
@@ -26310,6 +26558,10 @@ var ChainController = class {
|
|
|
26310
26558
|
"Watch-only cannot be disabled: this solver was started without a signer. Restart with `signer` to fill."
|
|
26311
26559
|
);
|
|
26312
26560
|
}
|
|
26561
|
+
if (!watchOnly) {
|
|
26562
|
+
const chain = this.runtime.resolvedChains.find((resolved) => resolved.chainId === chainId);
|
|
26563
|
+
if (chain) await assertBundlersServeEntryPoint([chain], this.runtime.configService);
|
|
26564
|
+
}
|
|
26313
26565
|
this.runtime.intentFiller.setWatchOnly(chainId, watchOnly);
|
|
26314
26566
|
this.syncWatchOnlyToConfig();
|
|
26315
26567
|
await this.persist();
|