@hyperbridge/simplex 0.16.7 → 0.16.11
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/README.md +31 -14
- package/dist/bin/simplex.js +23907 -2147
- package/dist/bin/simplex.js.map +1 -1
- package/dist/config-storage.cjs +186 -0
- package/dist/config-storage.cjs.map +1 -0
- package/dist/config-storage.d.cts +28 -0
- package/dist/config-storage.d.ts +28 -0
- package/dist/config-storage.js +163 -0
- package/dist/config-storage.js.map +1 -0
- package/dist/index.cjs +644 -340
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +205 -104
- package/dist/index.d.ts +205 -104
- package/dist/index.js +515 -216
- package/dist/index.js.map +1 -1
- package/dist/sqlite.cjs +13 -1
- package/dist/sqlite.cjs.map +1 -1
- package/dist/sqlite.d.cts +2 -1
- package/dist/sqlite.d.ts +2 -1
- package/dist/sqlite.js +13 -1
- package/dist/sqlite.js.map +1 -1
- package/dist/{types-DUDABLPI.d.cts → types-B5zkFhxR.d.cts} +17 -5
- package/dist/{types-DUDABLPI.d.ts → types-B5zkFhxR.d.ts} +17 -5
- package/dist/ui/assets/{index-C3T-gubp.css → index-B_p2oQkc.css} +1 -1
- package/dist/ui/assets/{index-Be446eYK.js → index-CD2NZXpG.js} +35 -35
- package/dist/ui/index.html +2 -2
- package/package.json +13 -2
package/dist/index.cjs
CHANGED
|
@@ -32,6 +32,8 @@ var src_exports = {};
|
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
AssetController: () => AssetController,
|
|
34
34
|
ChainController: () => ChainController,
|
|
35
|
+
LimitOrderController: () => LimitOrderController,
|
|
36
|
+
LimitOrderValidationError: () => LimitOrderValidationError,
|
|
35
37
|
LoggerContext: () => LoggerContext,
|
|
36
38
|
MemoryDataStore: () => MemoryDataStore,
|
|
37
39
|
OrderScanner: () => OrderScanner,
|
|
@@ -44,6 +46,7 @@ __export(src_exports, {
|
|
|
44
46
|
WalletController: () => WalletController,
|
|
45
47
|
addLogSink: () => addLogSink,
|
|
46
48
|
assertConfirmationCoverage: () => assertConfirmationCoverage,
|
|
49
|
+
canDerive: () => canDerive,
|
|
47
50
|
configureLogger: () => configureLogger,
|
|
48
51
|
createSigner: () => createSigner,
|
|
49
52
|
digestSigner: () => digestSigner,
|
|
@@ -56,10 +59,12 @@ __export(src_exports, {
|
|
|
56
59
|
pickAnchorStable: () => pickAnchorStable,
|
|
57
60
|
privateKeySigner: () => privateKeySigner,
|
|
58
61
|
registrySymbols: () => registrySymbols,
|
|
62
|
+
secretPhraseSigner: () => secretPhraseSigner,
|
|
59
63
|
turnkeySigner: () => turnkeySigner,
|
|
60
64
|
validateAssetDefinitions: () => validateAssetDefinitions,
|
|
61
65
|
validateConfig: () => validateConfig,
|
|
62
66
|
validatePairConfigs: () => validatePairConfigs,
|
|
67
|
+
validateSecretPhrase: () => validateSecretPhrase,
|
|
63
68
|
validateSignerConfig: () => validateSignerConfig,
|
|
64
69
|
validateVaultToml: () => validateVaultToml,
|
|
65
70
|
viemSigner: () => viemSigner
|
|
@@ -70,7 +75,7 @@ module.exports = __toCommonJS(src_exports);
|
|
|
70
75
|
var import_node_events3 = require("node:events");
|
|
71
76
|
|
|
72
77
|
// src/core/boot.ts
|
|
73
|
-
var
|
|
78
|
+
var import_viem32 = require("viem");
|
|
74
79
|
|
|
75
80
|
// src/core/filler.ts
|
|
76
81
|
var import_util_crypto = require("@polkadot/util-crypto");
|
|
@@ -2838,11 +2843,21 @@ var import_node_crypto = require("node:crypto");
|
|
|
2838
2843
|
var import_sdk4 = require("@hyperbridge/sdk");
|
|
2839
2844
|
|
|
2840
2845
|
// src/orderbook/amounts.ts
|
|
2846
|
+
var import_viem6 = require("viem");
|
|
2841
2847
|
var ORDERBOOK_SCALE = 10n ** 18n;
|
|
2842
2848
|
function toRaw(amount, decimals) {
|
|
2843
2849
|
const shift = 18 - decimals;
|
|
2844
2850
|
return shift >= 0 ? amount / 10n ** BigInt(shift) : amount * 10n ** BigInt(-shift);
|
|
2845
2851
|
}
|
|
2852
|
+
function budgetIdFor(limitOrderId) {
|
|
2853
|
+
return (0, import_viem6.keccak256)((0, import_viem6.stringToBytes)(limitOrderId));
|
|
2854
|
+
}
|
|
2855
|
+
function budgetFor(order, token, decimals) {
|
|
2856
|
+
return { budgetId: budgetIdFor(order.id), cap: toRaw(BigInt(order.size), decimals), token };
|
|
2857
|
+
}
|
|
2858
|
+
function budgetRoom(budget, spent, decimals) {
|
|
2859
|
+
return spent >= budget.cap ? 0n : toScaled(budget.cap - spent, decimals);
|
|
2860
|
+
}
|
|
2846
2861
|
function fromHuman(amount) {
|
|
2847
2862
|
const text = amount.trim();
|
|
2848
2863
|
if (!/^[0-9]+(\.[0-9]+)?$/.test(text)) {
|
|
@@ -2863,6 +2878,10 @@ function toScaled(amount, decimals) {
|
|
|
2863
2878
|
const shift = 18 - decimals;
|
|
2864
2879
|
return shift >= 0 ? amount * 10n ** BigInt(shift) : amount / 10n ** BigInt(-shift);
|
|
2865
2880
|
}
|
|
2881
|
+
function toScaledUp(amount, decimals) {
|
|
2882
|
+
const shift = 18 - decimals;
|
|
2883
|
+
return shift >= 0 ? amount * 10n ** BigInt(shift) : divCeil(amount, 10n ** BigInt(-shift));
|
|
2884
|
+
}
|
|
2866
2885
|
function divCeil(numerator, denominator) {
|
|
2867
2886
|
return (numerator + denominator - 1n) / denominator;
|
|
2868
2887
|
}
|
|
@@ -2904,6 +2923,87 @@ function inputFor(params) {
|
|
|
2904
2923
|
return divCeil(scaled, unit) * unit;
|
|
2905
2924
|
}
|
|
2906
2925
|
|
|
2926
|
+
// src/orderbook/matching.ts
|
|
2927
|
+
function limitOrderLegs(order) {
|
|
2928
|
+
return order.side === "BID" ? { input: order.base, output: order.quote } : { input: order.quote, output: order.base };
|
|
2929
|
+
}
|
|
2930
|
+
function matchLimitOrders(orders, incoming, resolve, now = /* @__PURE__ */ new Date()) {
|
|
2931
|
+
const qualifying = orders.filter((order) => serves(order, incoming, resolve, now)).map((order) => {
|
|
2932
|
+
const offer = offerFor({
|
|
2933
|
+
side: order.side,
|
|
2934
|
+
inputAmount: incoming.inputNet,
|
|
2935
|
+
price: BigInt(order.price),
|
|
2936
|
+
outputDecimals: incoming.outputDecimals
|
|
2937
|
+
});
|
|
2938
|
+
const available = BigInt(order.remaining);
|
|
2939
|
+
return { order, offer, available, payout: offer < available ? offer : available };
|
|
2940
|
+
}).filter((candidate) => candidate.offer >= incoming.requestedOutput && candidate.payout > 0n).sort(byBestOfferFirst);
|
|
2941
|
+
return qualifying;
|
|
2942
|
+
}
|
|
2943
|
+
function byBestOfferFirst(a, b) {
|
|
2944
|
+
if (a.offer !== b.offer) return a.offer > b.offer ? -1 : 1;
|
|
2945
|
+
if (a.available !== b.available) return a.available > b.available ? -1 : 1;
|
|
2946
|
+
return a.order.id < b.order.id ? -1 : 1;
|
|
2947
|
+
}
|
|
2948
|
+
function whyUnmatched(orders, incoming, resolve, now = /* @__PURE__ */ new Date()) {
|
|
2949
|
+
let furthest = -1;
|
|
2950
|
+
for (const order of orders) {
|
|
2951
|
+
const failed = firstFailed(order, incoming, resolve, now);
|
|
2952
|
+
furthest = Math.max(furthest, failed === null ? CHECKS.length : CHECKS.indexOf(failed));
|
|
2953
|
+
}
|
|
2954
|
+
const { source, destination, inputSymbol } = incoming;
|
|
2955
|
+
if (furthest < CHECKS.length) {
|
|
2956
|
+
switch (CHECKS[Math.max(furthest, 0)]) {
|
|
2957
|
+
case "open":
|
|
2958
|
+
return "no limit order is open";
|
|
2959
|
+
case "chain":
|
|
2960
|
+
return `no open limit order fills on ${destination}`;
|
|
2961
|
+
case "input":
|
|
2962
|
+
return `no limit order on ${destination} takes ${inputSymbol} in`;
|
|
2963
|
+
case "output":
|
|
2964
|
+
return `no limit order on ${destination} taking ${inputSymbol} in pays out the token this order asks for`;
|
|
2965
|
+
case "source":
|
|
2966
|
+
return `no limit order for this pair on ${destination} accepts swaps from ${source}`;
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
const priced = orders.filter((order) => firstFailed(order, incoming, resolve, now) === null).map((order) => ({
|
|
2970
|
+
order,
|
|
2971
|
+
offer: offerFor({
|
|
2972
|
+
side: order.side,
|
|
2973
|
+
inputAmount: incoming.inputNet,
|
|
2974
|
+
price: BigInt(order.price),
|
|
2975
|
+
outputDecimals: incoming.outputDecimals
|
|
2976
|
+
})
|
|
2977
|
+
}));
|
|
2978
|
+
if (priced.some(({ offer }) => offer >= incoming.requestedOutput)) {
|
|
2979
|
+
return "the limit orders that meet its rate have nothing left to pay out";
|
|
2980
|
+
}
|
|
2981
|
+
const best = priced.reduce((a, b) => b.offer > a.offer ? b : a);
|
|
2982
|
+
const symbol = limitOrderLegs(best.order).output;
|
|
2983
|
+
return `it asks for ${readable(incoming.requestedOutput)} ${symbol}; the best limit order offers ${readable(best.offer)} ${symbol}`;
|
|
2984
|
+
}
|
|
2985
|
+
var CHECKS = ["open", "chain", "input", "output", "source"];
|
|
2986
|
+
function serves(order, incoming, resolve, now) {
|
|
2987
|
+
return firstFailed(order, incoming, resolve, now) === null;
|
|
2988
|
+
}
|
|
2989
|
+
function firstFailed(order, incoming, resolve, now) {
|
|
2990
|
+
if (order.status !== "open") return "open";
|
|
2991
|
+
if (order.expiresAt !== null && new Date(order.expiresAt) <= now) return "open";
|
|
2992
|
+
if (order.fillChain !== incoming.destination) return "chain";
|
|
2993
|
+
const legs = limitOrderLegs(order);
|
|
2994
|
+
if (normalizeSymbol(legs.input) !== normalizeSymbol(incoming.inputSymbol)) return "input";
|
|
2995
|
+
const outputToken = resolve(legs.output, order.fillChain);
|
|
2996
|
+
if (!outputToken || outputToken.toLowerCase() !== incoming.outputToken.toLowerCase()) return "output";
|
|
2997
|
+
if (incoming.source === incoming.destination) return null;
|
|
2998
|
+
return order.acceptedSources.includes(incoming.source) ? null : "source";
|
|
2999
|
+
}
|
|
3000
|
+
function readable(amount) {
|
|
3001
|
+
const [whole, fraction] = toHuman(amount).split(".");
|
|
3002
|
+
const grouped = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
3003
|
+
const cut = fraction?.slice(0, 6).replace(/0+$/, "");
|
|
3004
|
+
return cut ? `${grouped}.${cut}` : grouped;
|
|
3005
|
+
}
|
|
3006
|
+
|
|
2907
3007
|
// src/orderbook/limit-orders.ts
|
|
2908
3008
|
var LIMITS_TTL_MS = 5 * 60 * 1e3;
|
|
2909
3009
|
var POSTABLE = ["open", "resizing"];
|
|
@@ -3418,6 +3518,58 @@ var LimitOrderService = class {
|
|
|
3418
3518
|
}
|
|
3419
3519
|
return report;
|
|
3420
3520
|
}
|
|
3521
|
+
/**
|
|
3522
|
+
* Lowers every open order's `remaining` to what the solver's account will still
|
|
3523
|
+
* pay for it, and puts the order back on the book at that size.
|
|
3524
|
+
*
|
|
3525
|
+
* The account tallies each order's payouts on the fill chain and refuses a fill
|
|
3526
|
+
* that would take the tally past the order's size. A fill that never settled
|
|
3527
|
+
* here, or a store restored from an older copy, leaves `remaining` above that
|
|
3528
|
+
* room and the order posted at a size the account will refuse. A tally behind
|
|
3529
|
+
* the store changes nothing: `remaining` is never raised.
|
|
3530
|
+
*
|
|
3531
|
+
* Answers how many orders were lowered.
|
|
3532
|
+
*/
|
|
3533
|
+
async reconcileTallies() {
|
|
3534
|
+
let lowered = 0;
|
|
3535
|
+
for (const order of await this.store.list({ status: "open" })) {
|
|
3536
|
+
try {
|
|
3537
|
+
const clamped = await this.clampToTally(order);
|
|
3538
|
+
if (!clamped) continue;
|
|
3539
|
+
lowered++;
|
|
3540
|
+
await this.resize(clamped, BigInt(order.remaining) - BigInt(clamped.remaining));
|
|
3541
|
+
} catch (err) {
|
|
3542
|
+
this.logger.error({ id: order.id, err }, "Could not bring the limit order in line with its on-chain tally");
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
return lowered;
|
|
3546
|
+
}
|
|
3547
|
+
/** The order as the store now holds it when its tally lowered it, null when it was left alone. */
|
|
3548
|
+
async clampToTally(order) {
|
|
3549
|
+
const token = this.assetRegistry.getAddress(limitOrderLegs(order).output, order.fillChain);
|
|
3550
|
+
if (!token) return null;
|
|
3551
|
+
const decimals = await this.contractService.getTokenDecimals(token, order.fillChain);
|
|
3552
|
+
const budget = budgetFor(order, token, decimals);
|
|
3553
|
+
const spent = await this.contractService.limitOrderSpent(order.fillChain, budget.budgetId);
|
|
3554
|
+
if (spent === null) return null;
|
|
3555
|
+
const room = budgetRoom(budget, spent, decimals);
|
|
3556
|
+
const over = BigInt(order.remaining) - (room + BigInt(order.reserved));
|
|
3557
|
+
if (over < toScaled(1n, decimals)) return null;
|
|
3558
|
+
const clamped = await this.store.clampRemaining(order.id, room.toString());
|
|
3559
|
+
if (!clamped) return null;
|
|
3560
|
+
this.logger.warn(
|
|
3561
|
+
{
|
|
3562
|
+
id: order.id,
|
|
3563
|
+
before: order.remaining,
|
|
3564
|
+
after: clamped.remaining,
|
|
3565
|
+
spent: spent.toString(),
|
|
3566
|
+
cap: budget.cap.toString(),
|
|
3567
|
+
reserved: clamped.reserved
|
|
3568
|
+
},
|
|
3569
|
+
"Limit order had more left than its on-chain tally allows; lowering it"
|
|
3570
|
+
);
|
|
3571
|
+
return clamped;
|
|
3572
|
+
}
|
|
3421
3573
|
/**
|
|
3422
3574
|
* Withdraws every order that has outlived the operator's own `expiresAt`.
|
|
3423
3575
|
*
|
|
@@ -3679,7 +3831,7 @@ async function patchRuntimeState(store, patch) {
|
|
|
3679
3831
|
}
|
|
3680
3832
|
|
|
3681
3833
|
// src/services/wallet/mpcvault.ts
|
|
3682
|
-
var
|
|
3834
|
+
var import_viem7 = require("viem");
|
|
3683
3835
|
var grpc = __toESM(require("@grpc/grpc-js"), 1);
|
|
3684
3836
|
|
|
3685
3837
|
// src/proto/mpcvault/platform/v1/api.ts
|
|
@@ -12243,18 +12395,18 @@ var MpcVaultService = class {
|
|
|
12243
12395
|
// Signature assembly
|
|
12244
12396
|
// -----------------------------------------------------------------------
|
|
12245
12397
|
signatureFromParts(parts, normalizeV = false) {
|
|
12246
|
-
const r = (0,
|
|
12247
|
-
const s = (0,
|
|
12398
|
+
const r = (0, import_viem7.padHex)((0, import_viem7.toHex)(BigInt(parts.R || "0")), { size: 32 });
|
|
12399
|
+
const s = (0, import_viem7.padHex)((0, import_viem7.toHex)(BigInt(parts.S || "0")), { size: 32 });
|
|
12248
12400
|
const vInt = this.parseFlexibleBigInt(parts.V || "0");
|
|
12249
|
-
let v = (0,
|
|
12401
|
+
let v = (0, import_viem7.padHex)((0, import_viem7.toHex)(vInt), { size: 1 });
|
|
12250
12402
|
if (normalizeV) {
|
|
12251
|
-
v = (0,
|
|
12403
|
+
v = (0, import_viem7.padHex)((0, import_viem7.toHex)(vInt < 27n ? vInt + 27n : vInt), { size: 1 });
|
|
12252
12404
|
}
|
|
12253
|
-
return (0,
|
|
12405
|
+
return (0, import_viem7.concatHex)([r, s, v]);
|
|
12254
12406
|
}
|
|
12255
12407
|
ecdsaPartsToSignatureComponents(parts) {
|
|
12256
|
-
const r = (0,
|
|
12257
|
-
const s = (0,
|
|
12408
|
+
const r = (0, import_viem7.padHex)((0, import_viem7.toHex)(BigInt(parts.R || "0")), { size: 32 });
|
|
12409
|
+
const s = (0, import_viem7.padHex)((0, import_viem7.toHex)(BigInt(parts.S || "0")), { size: 32 });
|
|
12258
12410
|
const vInt = this.parseFlexibleBigInt(parts.V || "0");
|
|
12259
12411
|
const yParity = vInt >= 27n ? Number(vInt - 27n) : Number(vInt);
|
|
12260
12412
|
if (yParity !== 0 && yParity !== 1) {
|
|
@@ -12290,7 +12442,7 @@ var MpcVaultService = class {
|
|
|
12290
12442
|
const messageBytes = Buffer.from(messageHash.slice(2), "hex");
|
|
12291
12443
|
const prefix = Buffer.from(`Ethereum Signed Message:
|
|
12292
12444
|
${messageBytes.length}`);
|
|
12293
|
-
const ethSignedMessageHash = (0,
|
|
12445
|
+
const ethSignedMessageHash = (0, import_viem7.keccak256)((0, import_viem7.concatHex)([(0, import_viem7.toHex)(prefix), messageHash]));
|
|
12294
12446
|
const ethSignedBytes = Buffer.from(ethSignedMessageHash.slice(2), "hex");
|
|
12295
12447
|
const uuid = await this.createSigningRequest({
|
|
12296
12448
|
vaultUuid: this.vaultUuid,
|
|
@@ -12378,11 +12530,12 @@ var SignerType = /* @__PURE__ */ ((SignerType2) => {
|
|
|
12378
12530
|
SignerType2["PrivateKey"] = "privateKey";
|
|
12379
12531
|
SignerType2["MpcVault"] = "mpcVault";
|
|
12380
12532
|
SignerType2["Turnkey"] = "turnkey";
|
|
12533
|
+
SignerType2["SecretPhrase"] = "secretPhrase";
|
|
12381
12534
|
return SignerType2;
|
|
12382
12535
|
})(SignerType || {});
|
|
12383
12536
|
|
|
12384
12537
|
// src/services/wallet/accounts/mpc.ts
|
|
12385
|
-
var
|
|
12538
|
+
var import_viem8 = require("viem");
|
|
12386
12539
|
var import_utils = require("viem/utils");
|
|
12387
12540
|
function mpcVaultSigner(config) {
|
|
12388
12541
|
const service = new MpcVaultService({
|
|
@@ -12417,9 +12570,9 @@ function mpcVaultSigner(config) {
|
|
|
12417
12570
|
if (tx.authorizationList?.length) {
|
|
12418
12571
|
const serializable = { ...tx, type: "eip7702" };
|
|
12419
12572
|
const signature = await service.signRawHashComponents(
|
|
12420
|
-
(0,
|
|
12573
|
+
(0, import_viem8.keccak256)((0, import_viem8.serializeTransaction)(serializable))
|
|
12421
12574
|
);
|
|
12422
|
-
return (0,
|
|
12575
|
+
return (0, import_viem8.serializeTransaction)(serializable, signature);
|
|
12423
12576
|
}
|
|
12424
12577
|
return service.signTransaction({
|
|
12425
12578
|
chainId: tx.chainId,
|
|
@@ -12444,10 +12597,10 @@ function requireChainId(value) {
|
|
|
12444
12597
|
var import_accounts = require("viem/accounts");
|
|
12445
12598
|
|
|
12446
12599
|
// src/services/wallet/accounts/viem.ts
|
|
12447
|
-
var
|
|
12600
|
+
var import_viem9 = require("viem");
|
|
12448
12601
|
var import_utils2 = require("viem/utils");
|
|
12449
12602
|
function splitSignature(signature, source) {
|
|
12450
|
-
const sig = (0,
|
|
12603
|
+
const sig = (0, import_viem9.parseSignature)(signature);
|
|
12451
12604
|
const yParity = sig.yParity ?? (sig.v !== void 0 ? Number(sig.v >= 27n ? sig.v - 27n : sig.v) : void 0);
|
|
12452
12605
|
if (yParity !== 0 && yParity !== 1) {
|
|
12453
12606
|
throw new Error(`Failed to derive yParity from ${source} signature`);
|
|
@@ -12499,9 +12652,54 @@ function privateKeySigner(privateKey) {
|
|
|
12499
12652
|
return viemSigner((0, import_accounts.privateKeyToAccount)(privateKey));
|
|
12500
12653
|
}
|
|
12501
12654
|
|
|
12655
|
+
// src/services/wallet/accounts/secretphrase.ts
|
|
12656
|
+
var import_bip39 = require("@scure/bip39");
|
|
12657
|
+
var import_accounts2 = require("viem/accounts");
|
|
12658
|
+
var WORD_COUNTS = [12, 15, 18, 21, 24];
|
|
12659
|
+
var MAX_DERIVATION_INDEX = 2 ** 31 - 1;
|
|
12660
|
+
function normaliseSecretPhrase(phrase) {
|
|
12661
|
+
return phrase.trim().split(/\s+/).join(" ").toLowerCase();
|
|
12662
|
+
}
|
|
12663
|
+
function validateSecretPhrase(phrase) {
|
|
12664
|
+
if (typeof phrase !== "string") throw new Error("Secret phrase must be a string");
|
|
12665
|
+
const normalised = normaliseSecretPhrase(phrase);
|
|
12666
|
+
const words = normalised === "" ? [] : normalised.split(" ");
|
|
12667
|
+
if (!WORD_COUNTS.includes(words.length)) {
|
|
12668
|
+
throw new Error(`Secret phrase must have ${WORD_COUNTS.join(", ")} words; got ${words.length}`);
|
|
12669
|
+
}
|
|
12670
|
+
if (words.some((word) => !import_accounts2.english.includes(word))) {
|
|
12671
|
+
throw new Error("Secret phrase contains a word that is not in the BIP-39 English wordlist");
|
|
12672
|
+
}
|
|
12673
|
+
if (!(0, import_bip39.validateMnemonic)(normalised, import_accounts2.english)) {
|
|
12674
|
+
throw new Error("Secret phrase checksum does not match; check the words and their order");
|
|
12675
|
+
}
|
|
12676
|
+
}
|
|
12677
|
+
function assertDerivationIndex(index, name = "Derivation index") {
|
|
12678
|
+
const expected = `${name} must be an integer between 0 and ${MAX_DERIVATION_INDEX}`;
|
|
12679
|
+
if (typeof index !== "number") throw new Error(expected);
|
|
12680
|
+
if (!Number.isInteger(index) || index < 0 || index > MAX_DERIVATION_INDEX) {
|
|
12681
|
+
throw new Error(`${expected}; got ${index}`);
|
|
12682
|
+
}
|
|
12683
|
+
}
|
|
12684
|
+
function secretPhraseSigner(config) {
|
|
12685
|
+
validateSecretPhrase(config.phrase);
|
|
12686
|
+
const phrase = normaliseSecretPhrase(config.phrase);
|
|
12687
|
+
const accountIndex = config.accountIndex ?? 0;
|
|
12688
|
+
assertDerivationIndex(accountIndex, "accountIndex");
|
|
12689
|
+
const derive = (index) => {
|
|
12690
|
+
assertDerivationIndex(index);
|
|
12691
|
+
return { ...viemSigner((0, import_accounts2.mnemonicToAccount)(phrase, { addressIndex: index })), mode: "secretPhrase" };
|
|
12692
|
+
};
|
|
12693
|
+
return { ...derive(accountIndex), accountIndex, derive };
|
|
12694
|
+
}
|
|
12695
|
+
function canDerive(signer) {
|
|
12696
|
+
const candidate = signer;
|
|
12697
|
+
return typeof candidate.derive === "function" && typeof candidate.accountIndex === "number";
|
|
12698
|
+
}
|
|
12699
|
+
|
|
12502
12700
|
// src/services/wallet/accounts/turnkey.ts
|
|
12503
12701
|
var import_sdk_server = require("@turnkey/sdk-server");
|
|
12504
|
-
var
|
|
12702
|
+
var import_viem12 = require("@turnkey/viem");
|
|
12505
12703
|
async function turnkeySigner(config) {
|
|
12506
12704
|
const turnkey = new import_sdk_server.Turnkey({
|
|
12507
12705
|
defaultOrganizationId: config.organizationId,
|
|
@@ -12509,7 +12707,7 @@ async function turnkeySigner(config) {
|
|
|
12509
12707
|
apiPrivateKey: config.apiPrivateKey,
|
|
12510
12708
|
apiPublicKey: config.apiPublicKey
|
|
12511
12709
|
});
|
|
12512
|
-
const account = await (0,
|
|
12710
|
+
const account = await (0, import_viem12.createAccount)({
|
|
12513
12711
|
client: turnkey.apiClient(),
|
|
12514
12712
|
organizationId: config.organizationId,
|
|
12515
12713
|
signWith: config.signWith
|
|
@@ -12522,6 +12720,7 @@ async function createSigner(config) {
|
|
|
12522
12720
|
if (config.type === "privateKey" /* PrivateKey */) return privateKeySigner(config.key);
|
|
12523
12721
|
if (config.type === "mpcVault" /* MpcVault */) return mpcVaultSigner(config);
|
|
12524
12722
|
if (config.type === "turnkey" /* Turnkey */) return turnkeySigner(config);
|
|
12723
|
+
if (config.type === "secretPhrase" /* SecretPhrase */) return secretPhraseSigner(config);
|
|
12525
12724
|
throw new Error(`Unsupported signer mode: ${config.type ?? "unknown"}`);
|
|
12526
12725
|
}
|
|
12527
12726
|
function validateSignerConfig(config) {
|
|
@@ -12547,15 +12746,23 @@ function validateSignerConfig(config) {
|
|
|
12547
12746
|
if (!config.signWith) throw new Error("simplex.signer.signWith is required");
|
|
12548
12747
|
return;
|
|
12549
12748
|
}
|
|
12749
|
+
if (config.type === "secretPhrase" /* SecretPhrase */) {
|
|
12750
|
+
if (!config.phrase) {
|
|
12751
|
+
throw new Error("simplex.signer.phrase is required when simplex.signer.type=secretPhrase");
|
|
12752
|
+
}
|
|
12753
|
+
validateSecretPhrase(config.phrase);
|
|
12754
|
+
if (config.accountIndex !== void 0) assertDerivationIndex(config.accountIndex, "simplex.signer.accountIndex");
|
|
12755
|
+
return;
|
|
12756
|
+
}
|
|
12550
12757
|
throw new Error(`Unsupported signer mode: ${config.type ?? "unknown"}`);
|
|
12551
12758
|
}
|
|
12552
12759
|
|
|
12553
12760
|
// src/services/wallet/account.ts
|
|
12554
|
-
var
|
|
12761
|
+
var import_viem14 = require("viem");
|
|
12555
12762
|
var import_utils3 = require("viem/utils");
|
|
12556
|
-
var
|
|
12763
|
+
var import_accounts3 = require("viem/accounts");
|
|
12557
12764
|
function accountFor(signer) {
|
|
12558
|
-
return (0,
|
|
12765
|
+
return (0, import_accounts3.toAccount)({
|
|
12559
12766
|
address: signer.address,
|
|
12560
12767
|
signTypedData: (typedData) => signer.signTypedData(typedData),
|
|
12561
12768
|
signTransaction: async (transaction) => await signer.signTransaction(toSignerTransaction(transaction)),
|
|
@@ -12595,7 +12802,7 @@ function digestSigner(opts) {
|
|
|
12595
12802
|
return {
|
|
12596
12803
|
address: opts.address,
|
|
12597
12804
|
mode: opts.mode,
|
|
12598
|
-
signTypedData: async (typedData) => (0,
|
|
12805
|
+
signTypedData: async (typedData) => (0, import_viem14.serializeSignature)(await sign((0, import_viem14.hashTypedData)(typedData))),
|
|
12599
12806
|
signAuthorization: (auth) => sign(
|
|
12600
12807
|
(0, import_utils3.hashAuthorization)({
|
|
12601
12808
|
address: auth.contractAddress,
|
|
@@ -12605,8 +12812,8 @@ function digestSigner(opts) {
|
|
|
12605
12812
|
),
|
|
12606
12813
|
signTransaction: async (tx) => {
|
|
12607
12814
|
const serializable = tx;
|
|
12608
|
-
const signature = await sign((0,
|
|
12609
|
-
return (0,
|
|
12815
|
+
const signature = await sign((0, import_viem14.keccak256)((0, import_viem14.serializeTransaction)(serializable)));
|
|
12816
|
+
return (0, import_viem14.serializeTransaction)(serializable, signature);
|
|
12610
12817
|
}
|
|
12611
12818
|
};
|
|
12612
12819
|
}
|
|
@@ -12635,6 +12842,12 @@ async function deriveSubstrateKeyPair(key) {
|
|
|
12635
12842
|
}
|
|
12636
12843
|
|
|
12637
12844
|
// src/services/CacheService.ts
|
|
12845
|
+
function budgetToCache(budget) {
|
|
12846
|
+
return { budgetId: budget.budgetId, cap: budget.cap.toString(), token: budget.token };
|
|
12847
|
+
}
|
|
12848
|
+
function budgetFromCache(budget) {
|
|
12849
|
+
return { budgetId: budget.budgetId, cap: BigInt(budget.cap), token: budget.token };
|
|
12850
|
+
}
|
|
12638
12851
|
var CacheService = class {
|
|
12639
12852
|
constructor(loggers = defaultLoggerContext()) {
|
|
12640
12853
|
this.CACHE_EXPIRY_MS = 1 * 60 * 1e3;
|
|
@@ -12792,7 +13005,24 @@ var CacheService = class {
|
|
|
12792
13005
|
return null;
|
|
12793
13006
|
}
|
|
12794
13007
|
}
|
|
12795
|
-
|
|
13008
|
+
/**
|
|
13009
|
+
* The budget of the limit order that priced the outputs cached for this order, or
|
|
13010
|
+
* null when no limit order did.
|
|
13011
|
+
*
|
|
13012
|
+
* Kept on the same entry as the outputs, so the two are replaced and expire
|
|
13013
|
+
* together: a bid can never sign one limit order's payout beside another's budget.
|
|
13014
|
+
*/
|
|
13015
|
+
getBidBudget(orderId) {
|
|
13016
|
+
try {
|
|
13017
|
+
const cache = this.cacheData.fillerOutputs[orderId];
|
|
13018
|
+
if (cache?.budget && this.isCacheValid(cache.timestamp)) return budgetFromCache(cache.budget);
|
|
13019
|
+
return null;
|
|
13020
|
+
} catch (error) {
|
|
13021
|
+
this.logger.error({ err: error }, "Error getting bid budget");
|
|
13022
|
+
return null;
|
|
13023
|
+
}
|
|
13024
|
+
}
|
|
13025
|
+
setFillerOutputs(orderId, outputs, inputs = [], budget) {
|
|
12796
13026
|
try {
|
|
12797
13027
|
this.cleanupStaleData();
|
|
12798
13028
|
this.cacheData.fillerOutputs[orderId] = {
|
|
@@ -12804,6 +13034,7 @@ var CacheService = class {
|
|
|
12804
13034
|
token: o.token,
|
|
12805
13035
|
amount: o.amount.toString()
|
|
12806
13036
|
})),
|
|
13037
|
+
...budget ? { budget: budgetToCache(budget) } : {},
|
|
12807
13038
|
timestamp: Date.now()
|
|
12808
13039
|
};
|
|
12809
13040
|
} catch (error) {
|
|
@@ -12849,7 +13080,8 @@ var CacheService = class {
|
|
|
12849
13080
|
data: call.data
|
|
12850
13081
|
})),
|
|
12851
13082
|
partialFill: plan.partialFill,
|
|
12852
|
-
profit: plan.profit
|
|
13083
|
+
profit: plan.profit,
|
|
13084
|
+
...plan.budget ? { budget: budgetFromCache(plan.budget) } : {}
|
|
12853
13085
|
}));
|
|
12854
13086
|
} catch (error) {
|
|
12855
13087
|
this.logger.error({ err: error }, "Error getting bid plans");
|
|
@@ -12878,7 +13110,8 @@ var CacheService = class {
|
|
|
12878
13110
|
data: call.data
|
|
12879
13111
|
})),
|
|
12880
13112
|
partialFill: plan.partialFill,
|
|
12881
|
-
profit: plan.profit
|
|
13113
|
+
profit: plan.profit,
|
|
13114
|
+
...plan.budget ? { budget: budgetToCache(plan.budget) } : {}
|
|
12882
13115
|
})),
|
|
12883
13116
|
timestamp: Date.now()
|
|
12884
13117
|
};
|
|
@@ -13036,12 +13269,12 @@ var CacheService = class {
|
|
|
13036
13269
|
};
|
|
13037
13270
|
|
|
13038
13271
|
// src/services/ChainClientManager.ts
|
|
13039
|
-
var
|
|
13040
|
-
var
|
|
13272
|
+
var import_viem17 = require("viem");
|
|
13273
|
+
var import_accounts4 = require("viem/accounts");
|
|
13041
13274
|
var import_sdk6 = require("@hyperbridge/sdk");
|
|
13042
13275
|
|
|
13043
13276
|
// src/services/QuorumPublicClient.ts
|
|
13044
|
-
var
|
|
13277
|
+
var import_viem16 = require("viem");
|
|
13045
13278
|
var import_sdk5 = require("@hyperbridge/sdk");
|
|
13046
13279
|
function quorumThreshold(numProviders) {
|
|
13047
13280
|
return Math.floor(2 * numProviders / 3) + 1;
|
|
@@ -13141,9 +13374,9 @@ var _QuorumPublicClient = class _QuorumPublicClient {
|
|
|
13141
13374
|
this.deadlineMs = budget.deadlineMs;
|
|
13142
13375
|
const chain = (0, import_sdk5.getViemChain)(chainId);
|
|
13143
13376
|
this.clients = this.rpcUrls.map(
|
|
13144
|
-
(url) => (0,
|
|
13377
|
+
(url) => (0, import_viem16.createPublicClient)({
|
|
13145
13378
|
chain,
|
|
13146
|
-
transport: (0,
|
|
13379
|
+
transport: (0, import_viem16.http)(url, {
|
|
13147
13380
|
timeout: budget.timeoutMs,
|
|
13148
13381
|
// No transport-level retry. viem's retry sleeps for the provider's
|
|
13149
13382
|
// `Retry-After` verbatim and uncapped, which reaches the socket
|
|
@@ -13559,9 +13792,9 @@ var HTTP_TRANSPORT_OPTS = {
|
|
|
13559
13792
|
};
|
|
13560
13793
|
function buildTransport(rpcUrls) {
|
|
13561
13794
|
if (rpcUrls.length === 1) {
|
|
13562
|
-
return (0,
|
|
13795
|
+
return (0, import_viem17.http)(rpcUrls[0], HTTP_TRANSPORT_OPTS);
|
|
13563
13796
|
}
|
|
13564
|
-
return (0,
|
|
13797
|
+
return (0, import_viem17.fallback)(rpcUrls.map((url) => (0, import_viem17.http)(url, HTTP_TRANSPORT_OPTS)));
|
|
13565
13798
|
}
|
|
13566
13799
|
function walletClientCacheKey(chainId, accountAddress) {
|
|
13567
13800
|
return `${chainId}:${accountAddress.toLowerCase()}`;
|
|
@@ -13587,7 +13820,7 @@ var ViemClientFactoryImpl = class {
|
|
|
13587
13820
|
if (!this.publicClients.has(chainConfig.chainId)) {
|
|
13588
13821
|
const chain = (0, import_sdk6.getViemChain)(chainConfig.chainId);
|
|
13589
13822
|
const urls = rpcUrls && rpcUrls.length > 0 ? rpcUrls : [chainConfig.rpcUrl];
|
|
13590
|
-
const publicClient = (0,
|
|
13823
|
+
const publicClient = (0, import_viem17.createPublicClient)({
|
|
13591
13824
|
chain,
|
|
13592
13825
|
transport: buildTransport(urls)
|
|
13593
13826
|
});
|
|
@@ -13600,7 +13833,7 @@ var ViemClientFactoryImpl = class {
|
|
|
13600
13833
|
if (!this.walletClients.has(key)) {
|
|
13601
13834
|
const chain = (0, import_sdk6.getViemChain)(chainConfig.chainId);
|
|
13602
13835
|
const urls = rpcUrls && rpcUrls.length > 0 ? rpcUrls : [chainConfig.rpcUrl];
|
|
13603
|
-
const walletClient = (0,
|
|
13836
|
+
const walletClient = (0, import_viem17.createWalletClient)({
|
|
13604
13837
|
chain,
|
|
13605
13838
|
account,
|
|
13606
13839
|
transport: buildTransport(urls)
|
|
@@ -13615,7 +13848,7 @@ var ChainClientManager = class {
|
|
|
13615
13848
|
this.clientFactory = new ViemClientFactoryImpl();
|
|
13616
13849
|
this.quorumClients = /* @__PURE__ */ new Map();
|
|
13617
13850
|
this.configService = configService;
|
|
13618
|
-
this.signer = signer ?? privateKeySigner((0,
|
|
13851
|
+
this.signer = signer ?? privateKeySigner((0, import_accounts4.generatePrivateKey)());
|
|
13619
13852
|
this.account = accountFor(this.signer);
|
|
13620
13853
|
}
|
|
13621
13854
|
/** This filler's logging destination, relayed from the config service. */
|
|
@@ -13684,9 +13917,47 @@ var ChainClientManager = class {
|
|
|
13684
13917
|
};
|
|
13685
13918
|
|
|
13686
13919
|
// src/services/ContractInteractionService.ts
|
|
13687
|
-
var
|
|
13920
|
+
var import_viem23 = require("viem");
|
|
13688
13921
|
var import_sdk7 = require("@hyperbridge/sdk");
|
|
13689
13922
|
|
|
13923
|
+
// src/config/abis/SolverAccount.ts
|
|
13924
|
+
var SOLVER_ACCOUNT_ABI = [
|
|
13925
|
+
{
|
|
13926
|
+
inputs: [
|
|
13927
|
+
{ name: "orderId", type: "bytes32" },
|
|
13928
|
+
{ name: "cap", type: "uint256" },
|
|
13929
|
+
{ name: "token", type: "address" },
|
|
13930
|
+
{ name: "approved", type: "uint256" },
|
|
13931
|
+
{ name: "fee", type: "uint256" }
|
|
13932
|
+
],
|
|
13933
|
+
name: "debitOrder",
|
|
13934
|
+
outputs: [],
|
|
13935
|
+
stateMutability: "nonpayable",
|
|
13936
|
+
type: "function"
|
|
13937
|
+
},
|
|
13938
|
+
{
|
|
13939
|
+
inputs: [{ name: "orderId", type: "bytes32" }],
|
|
13940
|
+
name: "spent",
|
|
13941
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
13942
|
+
stateMutability: "view",
|
|
13943
|
+
type: "function"
|
|
13944
|
+
},
|
|
13945
|
+
{
|
|
13946
|
+
inputs: [{ name: "sender", type: "address" }],
|
|
13947
|
+
name: "AccountUnauthorized",
|
|
13948
|
+
type: "error"
|
|
13949
|
+
},
|
|
13950
|
+
{
|
|
13951
|
+
inputs: [
|
|
13952
|
+
{ name: "orderId", type: "bytes32" },
|
|
13953
|
+
{ name: "total", type: "uint256" },
|
|
13954
|
+
{ name: "cap", type: "uint256" }
|
|
13955
|
+
],
|
|
13956
|
+
name: "LimitOrderExceeded",
|
|
13957
|
+
type: "error"
|
|
13958
|
+
}
|
|
13959
|
+
];
|
|
13960
|
+
|
|
13690
13961
|
// src/config/abis/EvmHost.ts
|
|
13691
13962
|
var EVM_HOST = [
|
|
13692
13963
|
{
|
|
@@ -17539,10 +17810,10 @@ var ENTRYPOINT_ABI = [
|
|
|
17539
17810
|
];
|
|
17540
17811
|
|
|
17541
17812
|
// src/services/paymaster/provider/simplex.ts
|
|
17542
|
-
var
|
|
17813
|
+
var import_viem22 = require("viem");
|
|
17543
17814
|
|
|
17544
17815
|
// src/services/paymaster/types.ts
|
|
17545
|
-
var
|
|
17816
|
+
var import_viem18 = require("viem");
|
|
17546
17817
|
var RECOMMENDED_AMOUNT_USD = 5n;
|
|
17547
17818
|
var VERIFICATION_GAS_LIMIT_PERMIT2 = 200000n;
|
|
17548
17819
|
var VERIFICATION_GAS_LIMIT_PERMIT = 250000n;
|
|
@@ -17550,19 +17821,19 @@ var PERMIT2_DEADLINE_SECONDS = 3600n;
|
|
|
17550
17821
|
var POST_OP_GAS_LIMIT_SIMPLEX = 40000n;
|
|
17551
17822
|
var DEPOSIT_HEADROOM_PERCENT = 150n;
|
|
17552
17823
|
function packPaymasterAndData(pm) {
|
|
17553
|
-
return (0,
|
|
17824
|
+
return (0, import_viem18.encodePacked)(
|
|
17554
17825
|
["address", "uint128", "uint128", "bytes"],
|
|
17555
17826
|
[pm.paymaster, pm.paymasterVerificationGasLimit, pm.paymasterPostOpGasLimit, pm.paymasterData]
|
|
17556
17827
|
);
|
|
17557
17828
|
}
|
|
17558
17829
|
|
|
17559
17830
|
// src/services/paymaster/permit.ts
|
|
17560
|
-
var
|
|
17831
|
+
var import_viem21 = require("viem");
|
|
17561
17832
|
|
|
17562
17833
|
// src/config/abis/EIP2612.ts
|
|
17563
|
-
var
|
|
17834
|
+
var import_viem19 = require("viem");
|
|
17564
17835
|
var EIP2612_ABI = [
|
|
17565
|
-
...
|
|
17836
|
+
...import_viem19.erc20Abi,
|
|
17566
17837
|
{
|
|
17567
17838
|
inputs: [{ internalType: "address", name: "owner", type: "address" }],
|
|
17568
17839
|
stateMutability: "view",
|
|
@@ -17580,7 +17851,7 @@ var EIP2612_ABI = [
|
|
|
17580
17851
|
];
|
|
17581
17852
|
|
|
17582
17853
|
// src/services/paymaster/permit2.ts
|
|
17583
|
-
var
|
|
17854
|
+
var import_viem20 = require("viem");
|
|
17584
17855
|
function permit2TransferTypedData(p) {
|
|
17585
17856
|
return {
|
|
17586
17857
|
types: {
|
|
@@ -17615,16 +17886,16 @@ function permit2TransferTypedData(p) {
|
|
|
17615
17886
|
};
|
|
17616
17887
|
}
|
|
17617
17888
|
function randomPermit2Nonce() {
|
|
17618
|
-
return (0,
|
|
17889
|
+
return (0, import_viem20.bytesToBigInt)(crypto.getRandomValues(new Uint8Array(32)));
|
|
17619
17890
|
}
|
|
17620
17891
|
async function signPermit2Transfer(signer, p) {
|
|
17621
17892
|
const signature = await signer.signTypedData(permit2TransferTypedData(p));
|
|
17622
17893
|
return normalizeSignature65(signature);
|
|
17623
17894
|
}
|
|
17624
17895
|
function normalizeSignature65(signature) {
|
|
17625
|
-
const length = (0,
|
|
17896
|
+
const length = (0, import_viem20.size)(signature);
|
|
17626
17897
|
if (length === 64) {
|
|
17627
|
-
return (0,
|
|
17898
|
+
return (0, import_viem20.serializeSignature)((0, import_viem20.compactSignatureToSignature)((0, import_viem20.parseCompactSignature)(signature)));
|
|
17628
17899
|
}
|
|
17629
17900
|
if (length !== 65) {
|
|
17630
17901
|
throw new Error(`Permit2 signature must be 64 or 65 bytes, got ${length}`);
|
|
@@ -17638,7 +17909,7 @@ function normalizeSignature65(signature) {
|
|
|
17638
17909
|
|
|
17639
17910
|
// src/services/paymaster/permit.ts
|
|
17640
17911
|
async function signEip2612Permit(client, signer, owner, spender, tokenAddress, value, chainId) {
|
|
17641
|
-
const token = (0,
|
|
17912
|
+
const token = (0, import_viem21.getContract)({
|
|
17642
17913
|
client,
|
|
17643
17914
|
address: tokenAddress,
|
|
17644
17915
|
abi: EIP2612_ABI
|
|
@@ -17676,7 +17947,7 @@ async function signEip2612Permit(client, signer, owner, spender, tokenAddress, v
|
|
|
17676
17947
|
spender,
|
|
17677
17948
|
value,
|
|
17678
17949
|
nonce,
|
|
17679
|
-
deadline:
|
|
17950
|
+
deadline: import_viem21.maxUint256
|
|
17680
17951
|
}
|
|
17681
17952
|
};
|
|
17682
17953
|
return normalizeSignature65(await signer.signTypedData(typedData));
|
|
@@ -17804,7 +18075,7 @@ async function selectToken(client, solverAccount, tokens, permit2) {
|
|
|
17804
18075
|
for (const token of tokens) {
|
|
17805
18076
|
const balance = await client.readContract({
|
|
17806
18077
|
address: token.address,
|
|
17807
|
-
abi:
|
|
18078
|
+
abi: import_viem22.erc20Abi,
|
|
17808
18079
|
functionName: "balanceOf",
|
|
17809
18080
|
args: [solverAccount]
|
|
17810
18081
|
});
|
|
@@ -17834,7 +18105,7 @@ async function paymasterSupportsPermit2(client, chainId, paymasterAddress) {
|
|
|
17834
18105
|
permit2Support.set(key, { supported: true, checkedAt: Date.now() });
|
|
17835
18106
|
return true;
|
|
17836
18107
|
} catch (error) {
|
|
17837
|
-
const isRevert = error instanceof
|
|
18108
|
+
const isRevert = error instanceof import_viem22.BaseError && error.walk((e) => e instanceof import_viem22.ContractFunctionRevertedError || e instanceof import_viem22.ContractFunctionZeroDataError);
|
|
17838
18109
|
if (isRevert) {
|
|
17839
18110
|
permit2Support.set(key, { supported: false, checkedAt: Date.now() });
|
|
17840
18111
|
return false;
|
|
@@ -17845,7 +18116,7 @@ async function paymasterSupportsPermit2(client, chainId, paymasterAddress) {
|
|
|
17845
18116
|
async function readAllowance(client, token, owner, spender) {
|
|
17846
18117
|
return await client.readContract({
|
|
17847
18118
|
address: token,
|
|
17848
|
-
abi:
|
|
18119
|
+
abi: import_viem22.erc20Abi,
|
|
17849
18120
|
functionName: "allowance",
|
|
17850
18121
|
args: [owner, spender]
|
|
17851
18122
|
});
|
|
@@ -17865,7 +18136,7 @@ async function buildPermit2Mode(signer, p) {
|
|
|
17865
18136
|
const r = `0x${signature.slice(2, 66)}`;
|
|
17866
18137
|
const s = `0x${signature.slice(66, 130)}`;
|
|
17867
18138
|
const v = Number.parseInt(signature.slice(130, 132), 16);
|
|
17868
|
-
const paymasterData = (0,
|
|
18139
|
+
const paymasterData = (0, import_viem22.encodePacked)(
|
|
17869
18140
|
["uint8", "address", "uint256", "uint256", "uint256", "uint8", "bytes32", "bytes32"],
|
|
17870
18141
|
[2, p.token, p.amount, nonce, deadline, v, r, s]
|
|
17871
18142
|
);
|
|
@@ -17889,9 +18160,9 @@ async function buildPermitMode(client, signer, solverAccount, paymasterAddress,
|
|
|
17889
18160
|
const r = `0x${permitSignature.slice(2, 66)}`;
|
|
17890
18161
|
const s = `0x${permitSignature.slice(66, 130)}`;
|
|
17891
18162
|
const v = Number.parseInt(permitSignature.slice(130, 132), 16);
|
|
17892
|
-
const paymasterData = (0,
|
|
18163
|
+
const paymasterData = (0, import_viem22.encodePacked)(
|
|
17893
18164
|
["uint8", "address", "uint256", "uint256", "uint8", "bytes32", "bytes32"],
|
|
17894
|
-
[0, tokenAddress, permitAmount,
|
|
18165
|
+
[0, tokenAddress, permitAmount, import_viem22.maxUint256, v, r, s]
|
|
17895
18166
|
);
|
|
17896
18167
|
return {
|
|
17897
18168
|
paymaster: paymasterAddress,
|
|
@@ -17905,7 +18176,7 @@ async function ensureFundedApprove(client, walletClient, chainId, solverAccount,
|
|
|
17905
18176
|
const key = `${chainId}:${tokenAddress.toLowerCase()}:${solverAccount.toLowerCase()}`;
|
|
17906
18177
|
const inFlight = approvalsInFlight.get(key);
|
|
17907
18178
|
if (inFlight) return inFlight;
|
|
17908
|
-
const pending = sendFundedApprove(client, walletClient, solverAccount, tokenAddress, spender,
|
|
18179
|
+
const pending = sendFundedApprove(client, walletClient, solverAccount, tokenAddress, spender, import_viem22.maxUint256).finally(
|
|
17909
18180
|
() => {
|
|
17910
18181
|
approvalsInFlight.delete(key);
|
|
17911
18182
|
}
|
|
@@ -17923,7 +18194,7 @@ async function sendFundedApprove(client, walletClient, solverAccount, tokenAddre
|
|
|
17923
18194
|
const requiredNative = APPROVE_TX_GAS * gasPrice * (needsReset ? 2n : 1n);
|
|
17924
18195
|
if (nativeBalance < requiredNative) {
|
|
17925
18196
|
throw new Error(
|
|
17926
|
-
`SimplexPaymaster needs a one-time Permit2 approval for the fee token on this chain; send native dust (>= ${(0,
|
|
18197
|
+
`SimplexPaymaster needs a one-time Permit2 approval for the fee token on this chain; send native dust (>= ${(0, import_viem22.formatEther)(requiredNative)}) to ${solverAccount}`
|
|
17927
18198
|
);
|
|
17928
18199
|
}
|
|
17929
18200
|
if (needsReset) {
|
|
@@ -17934,7 +18205,7 @@ async function sendFundedApprove(client, walletClient, solverAccount, tokenAddre
|
|
|
17934
18205
|
async function sendApproveTx(client, walletClient, tokenAddress, spender, amount) {
|
|
17935
18206
|
const hash = await walletClient.writeContract({
|
|
17936
18207
|
address: tokenAddress,
|
|
17937
|
-
abi:
|
|
18208
|
+
abi: import_viem22.erc20Abi,
|
|
17938
18209
|
functionName: "approve",
|
|
17939
18210
|
args: [spender, amount],
|
|
17940
18211
|
chain: walletClient.chain,
|
|
@@ -17959,7 +18230,7 @@ async function tokenSupportsPermit(client, tokenAddress) {
|
|
|
17959
18230
|
});
|
|
17960
18231
|
return true;
|
|
17961
18232
|
} catch (error) {
|
|
17962
|
-
const isRevert = error instanceof
|
|
18233
|
+
const isRevert = error instanceof import_viem22.BaseError && error.walk((e) => e instanceof import_viem22.ContractFunctionRevertedError || e instanceof import_viem22.ContractFunctionZeroDataError);
|
|
17963
18234
|
if (isRevert) return false;
|
|
17964
18235
|
throw error;
|
|
17965
18236
|
}
|
|
@@ -18066,6 +18337,10 @@ function describeShortfall(balances) {
|
|
|
18066
18337
|
// src/services/ContractInteractionService.ts
|
|
18067
18338
|
import_decimal2.Decimal.config({ precision: 28, rounding: 4 });
|
|
18068
18339
|
var FUNDING_GAS_PER_CALL = 400000n;
|
|
18340
|
+
var DEBIT_ORDER_GAS = 60000n;
|
|
18341
|
+
function settles(budget) {
|
|
18342
|
+
return !!budget && budget.token.toLowerCase() !== import_sdk7.ADDRESS_ZERO;
|
|
18343
|
+
}
|
|
18069
18344
|
var LIMIT_ORDER_CALL_GAS_LIMIT = 500000n;
|
|
18070
18345
|
var LIMIT_ORDER_VERIFICATION_GAS_LIMIT = 150000n;
|
|
18071
18346
|
var LIMIT_ORDER_PRE_VERIFICATION_GAS = 50000n;
|
|
@@ -18255,17 +18530,19 @@ var ContractInteractionService = class {
|
|
|
18255
18530
|
}
|
|
18256
18531
|
/**
|
|
18257
18532
|
* The call gas a bid needs: the shared estimate plus room for the funding calls
|
|
18258
|
-
* this bid prepends.
|
|
18533
|
+
* this bid prepends and for settling its limit order's budget.
|
|
18259
18534
|
*
|
|
18260
18535
|
* Funding calls are not simulated (the V4 PositionManager's flash accounting does
|
|
18261
18536
|
* not resolve in the bundler's estimation context), so each one adds a fixed
|
|
18262
18537
|
* allowance for its modifyLiquidities + take. They differ between the bids on one
|
|
18263
18538
|
* order, since each draws on its own limit order and so on its own funding, which
|
|
18264
18539
|
* is why this is worked out per bid rather than baked into the cached estimate.
|
|
18540
|
+
* The estimate is of a lone fillOrder, so the budget call is not in it either.
|
|
18265
18541
|
*/
|
|
18266
|
-
callGasLimitFor(order, baseCallGasLimit) {
|
|
18542
|
+
callGasLimitFor(order, baseCallGasLimit, budget) {
|
|
18267
18543
|
const funding = order.id ? this.cacheService.getFundingPrepends(order.id) : null;
|
|
18268
|
-
|
|
18544
|
+
const fundingGas = FUNDING_GAS_PER_CALL * BigInt(funding?.calls?.length ?? 0);
|
|
18545
|
+
return baseCallGasLimit + fundingGas + (settles(budget) ? DEBIT_ORDER_GAS : 0n);
|
|
18269
18546
|
}
|
|
18270
18547
|
/**
|
|
18271
18548
|
* Gets the fee token address and decimals for a given chain.
|
|
@@ -18337,7 +18614,7 @@ var ContractInteractionService = class {
|
|
|
18337
18614
|
this.logger.warn(
|
|
18338
18615
|
{
|
|
18339
18616
|
chain,
|
|
18340
|
-
walletBalance: (0,
|
|
18617
|
+
walletBalance: (0, import_viem23.formatEther)(solverBalance),
|
|
18341
18618
|
walletGasUnits: walletGasUnits.toString(),
|
|
18342
18619
|
gasPrice: gasPrice.toString()
|
|
18343
18620
|
},
|
|
@@ -18352,10 +18629,10 @@ var ContractInteractionService = class {
|
|
|
18352
18629
|
this.logger.info(
|
|
18353
18630
|
{
|
|
18354
18631
|
chain,
|
|
18355
|
-
currentDeposit: (0,
|
|
18632
|
+
currentDeposit: (0, import_viem23.formatEther)(currentDeposit),
|
|
18356
18633
|
depositGasUnits: depositGasUnits.toString(),
|
|
18357
18634
|
targetGasUnits: targetGasUnits.toString(),
|
|
18358
|
-
walletBalance: (0,
|
|
18635
|
+
walletBalance: (0, import_viem23.formatEther)(solverBalance)
|
|
18359
18636
|
},
|
|
18360
18637
|
"EntryPoint deposit covers target gas units, no top-up needed"
|
|
18361
18638
|
);
|
|
@@ -18366,8 +18643,8 @@ var ContractInteractionService = class {
|
|
|
18366
18643
|
this.logger.warn(
|
|
18367
18644
|
{
|
|
18368
18645
|
chain,
|
|
18369
|
-
deficit: (0,
|
|
18370
|
-
solverBalance: (0,
|
|
18646
|
+
deficit: (0, import_viem23.formatEther)(deficit),
|
|
18647
|
+
solverBalance: (0, import_viem23.formatEther)(solverBalance),
|
|
18371
18648
|
depositGasUnits: depositGasUnits.toString(),
|
|
18372
18649
|
targetGasUnits: targetGasUnits.toString()
|
|
18373
18650
|
},
|
|
@@ -18379,10 +18656,10 @@ var ContractInteractionService = class {
|
|
|
18379
18656
|
this.logger.info(
|
|
18380
18657
|
{
|
|
18381
18658
|
chain,
|
|
18382
|
-
currentDeposit: (0,
|
|
18659
|
+
currentDeposit: (0, import_viem23.formatEther)(currentDeposit),
|
|
18383
18660
|
depositGasUnits: depositGasUnits.toString(),
|
|
18384
18661
|
targetGasUnits: targetGasUnits.toString(),
|
|
18385
|
-
topUpAmount: (0,
|
|
18662
|
+
topUpAmount: (0, import_viem23.formatEther)(deficit)
|
|
18386
18663
|
},
|
|
18387
18664
|
"Topping up EntryPoint deposit to cover target gas units"
|
|
18388
18665
|
);
|
|
@@ -18405,7 +18682,7 @@ var ContractInteractionService = class {
|
|
|
18405
18682
|
const addr = tokenAddress.toLowerCase();
|
|
18406
18683
|
if (addr !== sourceUsdc && addr !== sourceUsdt) continue;
|
|
18407
18684
|
const decimals = await this.getTokenDecimals(tokenAddress, order.source);
|
|
18408
|
-
const tokenAmount = new import_decimal2.Decimal((0,
|
|
18685
|
+
const tokenAmount = new import_decimal2.Decimal((0, import_viem23.formatUnits)(input.amount, decimals));
|
|
18409
18686
|
inputUsdValue = inputUsdValue.plus(tokenAmount);
|
|
18410
18687
|
}
|
|
18411
18688
|
return inputUsdValue;
|
|
@@ -18447,6 +18724,27 @@ var ContractInteractionService = class {
|
|
|
18447
18724
|
{ maxRetries: 3, backoffMs: 250, logMessage: "Failed to read token balance" }
|
|
18448
18725
|
);
|
|
18449
18726
|
}
|
|
18727
|
+
/**
|
|
18728
|
+
* What the solver's account has tallied against a limit order's budget on `chain`, in
|
|
18729
|
+
* the paid token's own units, or null when it cannot be read.
|
|
18730
|
+
*
|
|
18731
|
+
* Null rather than a throw: an account whose implementation keeps no tally answers
|
|
18732
|
+
* with a revert or with no data, and the caller carries on without the figure either
|
|
18733
|
+
* way. Not retried, since that answer does not change on a second ask.
|
|
18734
|
+
*/
|
|
18735
|
+
async limitOrderSpent(chain, budgetId) {
|
|
18736
|
+
try {
|
|
18737
|
+
return await this.clientManager.getPublicClient(chain).readContract({
|
|
18738
|
+
address: this.solverAccountAddress,
|
|
18739
|
+
abi: SOLVER_ACCOUNT_ABI,
|
|
18740
|
+
functionName: "spent",
|
|
18741
|
+
args: [budgetId]
|
|
18742
|
+
});
|
|
18743
|
+
} catch (err) {
|
|
18744
|
+
this.logger.warn({ err, chain, budgetId }, "Could not read the limit order's tally from the solver account");
|
|
18745
|
+
return null;
|
|
18746
|
+
}
|
|
18747
|
+
}
|
|
18450
18748
|
async getSolverEntryPointBalance(chain) {
|
|
18451
18749
|
const entryPointAddress = this.configService.getEntryPointAddress(chain);
|
|
18452
18750
|
if (!entryPointAddress) {
|
|
@@ -18475,7 +18773,7 @@ var ContractInteractionService = class {
|
|
|
18475
18773
|
const walletClient = this.clientManager.getWalletClient(chain);
|
|
18476
18774
|
const publicClient = this.clientManager.getPublicClient(chain);
|
|
18477
18775
|
this.logger.info(
|
|
18478
|
-
{ chain, solver: this.solverAccountAddress, amount: (0,
|
|
18776
|
+
{ chain, solver: this.solverAccountAddress, amount: (0, import_viem23.formatEther)(amount) },
|
|
18479
18777
|
"Depositing to EntryPoint"
|
|
18480
18778
|
);
|
|
18481
18779
|
const hash = await walletClient.writeContract({
|
|
@@ -18490,7 +18788,7 @@ var ContractInteractionService = class {
|
|
|
18490
18788
|
if (receipt.status !== "success") {
|
|
18491
18789
|
throw new Error(`EntryPoint deposit transaction reverted: ${hash}`);
|
|
18492
18790
|
}
|
|
18493
|
-
this.logger.info({ chain, txHash: hash, amount: (0,
|
|
18791
|
+
this.logger.info({ chain, txHash: hash, amount: (0, import_viem23.formatEther)(amount) }, "EntryPoint deposit confirmed");
|
|
18494
18792
|
return hash;
|
|
18495
18793
|
}
|
|
18496
18794
|
/**
|
|
@@ -18510,7 +18808,7 @@ var ContractInteractionService = class {
|
|
|
18510
18808
|
const walletClient = this.clientManager.getWalletClient(chain);
|
|
18511
18809
|
const publicClient = this.clientManager.getPublicClient(chain);
|
|
18512
18810
|
this.logger.info(
|
|
18513
|
-
{ chain, solver: this.solverAccountAddress, amount: (0,
|
|
18811
|
+
{ chain, solver: this.solverAccountAddress, amount: (0, import_viem23.formatEther)(balance) },
|
|
18514
18812
|
"Withdrawing from EntryPoint"
|
|
18515
18813
|
);
|
|
18516
18814
|
const hash = await walletClient.writeContract({
|
|
@@ -18524,7 +18822,7 @@ var ContractInteractionService = class {
|
|
|
18524
18822
|
if (receipt.status !== "success") {
|
|
18525
18823
|
throw new Error(`EntryPoint withdrawal transaction reverted: ${hash}`);
|
|
18526
18824
|
}
|
|
18527
|
-
this.logger.info({ chain, txHash: hash, amount: (0,
|
|
18825
|
+
this.logger.info({ chain, txHash: hash, amount: (0, import_viem23.formatEther)(balance) }, "EntryPoint withdrawal confirmed");
|
|
18528
18826
|
return hash;
|
|
18529
18827
|
}
|
|
18530
18828
|
/**
|
|
@@ -18587,7 +18885,8 @@ var ContractInteractionService = class {
|
|
|
18587
18885
|
if (!cachedEstimate) {
|
|
18588
18886
|
throw new Error(`No cached gas estimate found for order ${order.id}. Call estimateGasFillPost first.`);
|
|
18589
18887
|
}
|
|
18590
|
-
const
|
|
18888
|
+
const budget = this.cacheService.getBidBudget(order.id);
|
|
18889
|
+
const callGasLimit = this.callGasLimitFor(order, cachedEstimate.callGasLimit, budget);
|
|
18591
18890
|
const cachedFillerOutputs = this.cacheService.getFillerOutputs(order.id);
|
|
18592
18891
|
const cachedFillerInputs = this.cacheService.getFillerInputs(order.id);
|
|
18593
18892
|
if (!cachedFillerOutputs || !cachedFillerInputs) {
|
|
@@ -18614,7 +18913,8 @@ var ContractInteractionService = class {
|
|
|
18614
18913
|
order,
|
|
18615
18914
|
cachedFillerOutputs,
|
|
18616
18915
|
fillOptions,
|
|
18617
|
-
cachedEstimate.totalCostInSourceFeeToken + dispatchFeeTokenAmount
|
|
18916
|
+
cachedEstimate.totalCostInSourceFeeToken + dispatchFeeTokenAmount,
|
|
18917
|
+
budget ? { budget, solverAccount: solverAccountAddress } : void 0
|
|
18618
18918
|
);
|
|
18619
18919
|
const commitment = (0, import_sdk7.orderCommitment)(order);
|
|
18620
18920
|
const nonce = await this.clientManager.getPublicClient(order.destination).readContract({
|
|
@@ -18642,20 +18942,32 @@ var ContractInteractionService = class {
|
|
|
18642
18942
|
} else {
|
|
18643
18943
|
this.logger.warn({ reason: pmResult.reason }, "No paymaster for bid UserOp; relying on EntryPoint deposit");
|
|
18644
18944
|
}
|
|
18645
|
-
const
|
|
18646
|
-
order,
|
|
18647
|
-
fillOptions,
|
|
18945
|
+
const bidGas = {
|
|
18648
18946
|
solverAccount: solverAccountAddress,
|
|
18649
|
-
solverSigner: sdkSigningAccount(this.signer),
|
|
18650
18947
|
nonce,
|
|
18651
18948
|
entryPointAddress,
|
|
18652
18949
|
callGasLimit,
|
|
18653
18950
|
verificationGasLimit: cachedEstimate.verificationGasLimit,
|
|
18654
|
-
preVerificationGas: cachedEstimate.preVerificationGas,
|
|
18655
18951
|
maxFeePerGas: cachedEstimate.maxFeePerGas,
|
|
18656
18952
|
maxPriorityFeePerGas: cachedEstimate.maxPriorityFeePerGas,
|
|
18657
18953
|
callData,
|
|
18658
18954
|
paymasterAndData
|
|
18955
|
+
};
|
|
18956
|
+
let preVerificationGas = cachedEstimate.preVerificationGas;
|
|
18957
|
+
try {
|
|
18958
|
+
preVerificationGas = await sdkHelper.estimateBidPreVerificationGas(bidGas);
|
|
18959
|
+
} catch (err) {
|
|
18960
|
+
this.logger.warn(
|
|
18961
|
+
{ orderId: order.id, err, preVerificationGas: preVerificationGas.toString() },
|
|
18962
|
+
"Could not estimate the bid's preVerificationGas; using the fill estimate's"
|
|
18963
|
+
);
|
|
18964
|
+
}
|
|
18965
|
+
const userOp = await sdkHelper.prepareSubmitBid({
|
|
18966
|
+
...bidGas,
|
|
18967
|
+
order,
|
|
18968
|
+
fillOptions,
|
|
18969
|
+
solverSigner: sdkSigningAccount(this.signer),
|
|
18970
|
+
preVerificationGas
|
|
18659
18971
|
});
|
|
18660
18972
|
const encodedUserOp = (0, import_sdk7.encodeUserOpScale)(userOp);
|
|
18661
18973
|
this.logger.info(
|
|
@@ -18664,6 +18976,7 @@ var ContractInteractionService = class {
|
|
|
18664
18976
|
solverAccount: solverAccountAddress,
|
|
18665
18977
|
nonce: nonce.toString(),
|
|
18666
18978
|
callGasLimit: callGasLimit.toString(),
|
|
18979
|
+
preVerificationGas: preVerificationGas.toString(),
|
|
18667
18980
|
maxFeePerGas: cachedEstimate.maxFeePerGas.toString()
|
|
18668
18981
|
},
|
|
18669
18982
|
"Prepared bid UserOp"
|
|
@@ -18747,7 +19060,7 @@ var ContractInteractionService = class {
|
|
|
18747
19060
|
{
|
|
18748
19061
|
target: outputToken,
|
|
18749
19062
|
value: 0n,
|
|
18750
|
-
data: (0,
|
|
19063
|
+
data: (0, import_viem23.encodeFunctionData)({
|
|
18751
19064
|
abi: ERC20_ABI,
|
|
18752
19065
|
functionName: "approve",
|
|
18753
19066
|
args: [gateway, outputAmount]
|
|
@@ -18793,8 +19106,13 @@ var ContractInteractionService = class {
|
|
|
18793
19106
|
* Same-chain fills release escrow locally with no Hyperbridge dispatch, so the
|
|
18794
19107
|
* gateway never pulls the fee token — its approval is skipped. Only cross-chain
|
|
18795
19108
|
* fills, which dispatch a RedeemEscrow message paid in the fee token, need it.
|
|
19109
|
+
*
|
|
19110
|
+
* A bid priced by a limit order ends with `debitOrder`, which takes the budget's id
|
|
19111
|
+
* as its `orderId`, works out what the fill paid from what is left of the allowance
|
|
19112
|
+
* and reverts the whole batch if the order's payouts would pass its size. It has to
|
|
19113
|
+
* come last, after the gateway has drawn on the allowance.
|
|
18796
19114
|
*/
|
|
18797
|
-
async buildApprovalAndFillCalldata(order, fillerOutputs, fillOptions, requiredFeeTokenAmount) {
|
|
19115
|
+
async buildApprovalAndFillCalldata(order, fillerOutputs, fillOptions, requiredFeeTokenAmount, settlement) {
|
|
18798
19116
|
const chain = order.destination;
|
|
18799
19117
|
const intentGatewayV2Address = this.configService.getIntentGatewayAddress(chain);
|
|
18800
19118
|
const perTokenRequired = /* @__PURE__ */ new Map();
|
|
@@ -18804,9 +19122,10 @@ var ContractInteractionService = class {
|
|
|
18804
19122
|
const key = addr.toLowerCase();
|
|
18805
19123
|
perTokenRequired.set(key, (perTokenRequired.get(key) ?? 0n) + output.amount);
|
|
18806
19124
|
}
|
|
19125
|
+
let feeKey;
|
|
18807
19126
|
if (order.source !== order.destination) {
|
|
18808
19127
|
const feeToken = await this.getFeeTokenWithDecimals(chain);
|
|
18809
|
-
|
|
19128
|
+
feeKey = feeToken.address.toLowerCase();
|
|
18810
19129
|
perTokenRequired.set(feeKey, (perTokenRequired.get(feeKey) ?? 0n) + requiredFeeTokenAmount);
|
|
18811
19130
|
}
|
|
18812
19131
|
const fundingPrepends = order.id ? this.cacheService.getFundingPrepends(order.id) : null;
|
|
@@ -18818,7 +19137,7 @@ var ContractInteractionService = class {
|
|
|
18818
19137
|
calls.push({
|
|
18819
19138
|
target: tokenAddress,
|
|
18820
19139
|
value: 0n,
|
|
18821
|
-
data: (0,
|
|
19140
|
+
data: (0, import_viem23.encodeFunctionData)({
|
|
18822
19141
|
abi: ERC20_ABI,
|
|
18823
19142
|
functionName: "approve",
|
|
18824
19143
|
args: [intentGatewayV2Address, amount]
|
|
@@ -18832,17 +19151,37 @@ var ContractInteractionService = class {
|
|
|
18832
19151
|
value: nativeOutputValue,
|
|
18833
19152
|
data: (0, import_sdk7.encodeFillOrder)((0, import_sdk7.transformOrderForContract)(order), fillOptions)
|
|
18834
19153
|
});
|
|
19154
|
+
if (settlement && settles(settlement.budget)) {
|
|
19155
|
+
const { budget, solverAccount } = settlement;
|
|
19156
|
+
const budgetToken = budget.token.toLowerCase();
|
|
19157
|
+
const approved = perTokenRequired.get(budgetToken) ?? 0n;
|
|
19158
|
+
if (approved === 0n) {
|
|
19159
|
+
throw new Error(
|
|
19160
|
+
`Bid on order ${order.id} approves nothing in ${budget.token}, the token budget ${budget.budgetId} is measured in`
|
|
19161
|
+
);
|
|
19162
|
+
}
|
|
19163
|
+
const fee = feeKey === budgetToken ? fillOptions.relayerFee : 0n;
|
|
19164
|
+
calls.push({
|
|
19165
|
+
target: solverAccount,
|
|
19166
|
+
value: 0n,
|
|
19167
|
+
data: (0, import_viem23.encodeFunctionData)({
|
|
19168
|
+
abi: SOLVER_ACCOUNT_ABI,
|
|
19169
|
+
functionName: "debitOrder",
|
|
19170
|
+
args: [budget.budgetId, budget.cap, budgetToken, approved, fee]
|
|
19171
|
+
})
|
|
19172
|
+
});
|
|
19173
|
+
}
|
|
18835
19174
|
return (0, import_sdk7.encodeERC7821ExecuteBatch)(calls);
|
|
18836
19175
|
}
|
|
18837
19176
|
};
|
|
18838
19177
|
|
|
18839
19178
|
// src/services/DelegationService.ts
|
|
18840
|
-
var
|
|
19179
|
+
var import_viem25 = require("viem");
|
|
18841
19180
|
var import_sdk9 = require("@hyperbridge/sdk");
|
|
18842
19181
|
|
|
18843
19182
|
// src/services/UserOpSender.ts
|
|
18844
19183
|
var import_sdk8 = require("@hyperbridge/sdk");
|
|
18845
|
-
var
|
|
19184
|
+
var import_viem24 = require("viem");
|
|
18846
19185
|
var FALLBACK_VERIFICATION_GAS_LIMIT = 250000n;
|
|
18847
19186
|
var FALLBACK_CALL_GAS_LIMIT = 1500000n;
|
|
18848
19187
|
var FALLBACK_PRE_VERIFICATION_GAS = 150000n;
|
|
@@ -18915,11 +19254,11 @@ var UserOpSender = class {
|
|
|
18915
19254
|
});
|
|
18916
19255
|
const formattedAuth = auth ? {
|
|
18917
19256
|
address: auth.address,
|
|
18918
|
-
chainId: (0,
|
|
18919
|
-
nonce: (0,
|
|
19257
|
+
chainId: (0, import_viem24.toHex)(auth.chainId),
|
|
19258
|
+
nonce: (0, import_viem24.toHex)(auth.nonce),
|
|
18920
19259
|
r: auth.r,
|
|
18921
19260
|
s: auth.s,
|
|
18922
|
-
yParity: (0,
|
|
19261
|
+
yParity: (0, import_viem24.toHex)(auth.yParity)
|
|
18923
19262
|
} : void 0;
|
|
18924
19263
|
const { verificationGasLimit, callGasLimit, preVerificationGas } = gas ?? await this.estimateGas({
|
|
18925
19264
|
bundlerUrl,
|
|
@@ -19087,10 +19426,10 @@ function bootstrapCallData(approval) {
|
|
|
19087
19426
|
{
|
|
19088
19427
|
target: approval.token,
|
|
19089
19428
|
value: 0n,
|
|
19090
|
-
data: (0,
|
|
19091
|
-
abi:
|
|
19429
|
+
data: (0, import_viem25.encodeFunctionData)({
|
|
19430
|
+
abi: import_viem25.erc20Abi,
|
|
19092
19431
|
functionName: "approve",
|
|
19093
|
-
args: [approval.spender,
|
|
19432
|
+
args: [approval.spender, import_viem25.maxUint256]
|
|
19094
19433
|
})
|
|
19095
19434
|
}
|
|
19096
19435
|
]);
|
|
@@ -19141,10 +19480,10 @@ var DelegationService = class {
|
|
|
19141
19480
|
return await walletClient.sendTransaction({
|
|
19142
19481
|
to: approval.token,
|
|
19143
19482
|
value: 0n,
|
|
19144
|
-
data: (0,
|
|
19145
|
-
abi:
|
|
19483
|
+
data: (0, import_viem25.encodeFunctionData)({
|
|
19484
|
+
abi: import_viem25.erc20Abi,
|
|
19146
19485
|
functionName: "approve",
|
|
19147
|
-
args: [approval.spender,
|
|
19486
|
+
args: [approval.spender, import_viem25.maxUint256]
|
|
19148
19487
|
}),
|
|
19149
19488
|
authorizationList: [authorization],
|
|
19150
19489
|
chain: walletClient.chain,
|
|
@@ -19378,8 +19717,8 @@ var DelegationService = class {
|
|
|
19378
19717
|
{
|
|
19379
19718
|
chain,
|
|
19380
19719
|
authority,
|
|
19381
|
-
nativeBalance: (0,
|
|
19382
|
-
requiredNative: (0,
|
|
19720
|
+
nativeBalance: (0, import_viem25.formatEther)(native.nativeBalance),
|
|
19721
|
+
requiredNative: (0, import_viem25.formatEther)(native.requiredNative)
|
|
19383
19722
|
},
|
|
19384
19723
|
"Delegation failed: insufficient native balance for direct EIP-7702 tx and paymaster path unavailable"
|
|
19385
19724
|
);
|
|
@@ -19454,7 +19793,7 @@ var DelegationService = class {
|
|
|
19454
19793
|
{ chain, authority: this.signer.address, mode: this.signer.mode ?? "custom" },
|
|
19455
19794
|
"Revoking EIP-7702 delegation"
|
|
19456
19795
|
);
|
|
19457
|
-
const authorization = await this.buildAuthorization(chain,
|
|
19796
|
+
const authorization = await this.buildAuthorization(chain, import_viem25.zeroAddress);
|
|
19458
19797
|
const hash = await this.sendDelegationTransaction(chain, authorization);
|
|
19459
19798
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
19460
19799
|
if (receipt.status === "success") {
|
|
@@ -19470,13 +19809,13 @@ var DelegationService = class {
|
|
|
19470
19809
|
};
|
|
19471
19810
|
|
|
19472
19811
|
// src/services/RebalancingService.ts
|
|
19473
|
-
var
|
|
19812
|
+
var import_viem28 = require("viem");
|
|
19474
19813
|
var import_sdk13 = require("@hyperbridge/sdk");
|
|
19475
19814
|
var import_decimal3 = require("decimal.js");
|
|
19476
19815
|
|
|
19477
19816
|
// src/services/rebalancers/binance.ts
|
|
19478
19817
|
var import_wallet2 = require("@binance/wallet");
|
|
19479
|
-
var
|
|
19818
|
+
var import_viem26 = require("viem");
|
|
19480
19819
|
var import_sdk10 = require("@hyperbridge/sdk");
|
|
19481
19820
|
var DEPOSIT_STATUS_SUCCESS = 1;
|
|
19482
19821
|
var WITHDRAW_STATUS_COMPLETED = 6;
|
|
@@ -19788,7 +20127,7 @@ var BinanceRebalancer = class {
|
|
|
19788
20127
|
abi: ERC20_ABI,
|
|
19789
20128
|
functionName: "decimals"
|
|
19790
20129
|
});
|
|
19791
|
-
const amountWei = (0,
|
|
20130
|
+
const amountWei = (0, import_viem26.parseUnits)(amount, Number(tokenDecimals));
|
|
19792
20131
|
const balance = await publicClient.readContract({
|
|
19793
20132
|
address: tokenAddress,
|
|
19794
20133
|
abi: ERC20_ABI,
|
|
@@ -20012,7 +20351,7 @@ var CctpRebalancer = class {
|
|
|
20012
20351
|
};
|
|
20013
20352
|
|
|
20014
20353
|
// src/services/rebalancers/usdt0.ts
|
|
20015
|
-
var
|
|
20354
|
+
var import_viem27 = require("viem");
|
|
20016
20355
|
var import_sdk12 = require("@hyperbridge/sdk");
|
|
20017
20356
|
|
|
20018
20357
|
// src/config/abis/Oft.ts
|
|
@@ -20175,7 +20514,7 @@ var Usdt0Rebalancer = class {
|
|
|
20175
20514
|
const publicClient = this.chainClientManager.getPublicClient(source);
|
|
20176
20515
|
const walletClient = this.chainClientManager.getWalletClient(source);
|
|
20177
20516
|
const recipient = recipientAddress || this.account.address;
|
|
20178
|
-
const amountWei = (0,
|
|
20517
|
+
const amountWei = (0, import_viem27.parseUnits)(amount, 6);
|
|
20179
20518
|
if (sourceChainId === 1) {
|
|
20180
20519
|
const allowance = await publicClient.readContract({
|
|
20181
20520
|
address: tokenAddress,
|
|
@@ -20188,7 +20527,7 @@ var Usdt0Rebalancer = class {
|
|
|
20188
20527
|
address: tokenAddress,
|
|
20189
20528
|
abi: ERC20_ABI,
|
|
20190
20529
|
functionName: "approve",
|
|
20191
|
-
args: [oftAddress,
|
|
20530
|
+
args: [oftAddress, import_viem27.maxUint256],
|
|
20192
20531
|
chain: walletClient.chain
|
|
20193
20532
|
});
|
|
20194
20533
|
await publicClient.waitForTransactionReceipt({ hash: approveTx, confirmations: 1 });
|
|
@@ -20270,8 +20609,8 @@ var Usdt0Rebalancer = class {
|
|
|
20270
20609
|
if (!oftAddress) throw new Error(`Chain ${source} not supported by USDT0`);
|
|
20271
20610
|
const publicClient = this.chainClientManager.getPublicClient(source);
|
|
20272
20611
|
const recipient = recipientAddress || this.account.address;
|
|
20273
|
-
const amountWei = (0,
|
|
20274
|
-
const recipientBytes32 = (0,
|
|
20612
|
+
const amountWei = (0, import_viem27.parseUnits)(amount, 6);
|
|
20613
|
+
const recipientBytes32 = (0, import_viem27.padHex)(recipient, { size: 32 });
|
|
20275
20614
|
const sendParam = {
|
|
20276
20615
|
dstEid: destEid,
|
|
20277
20616
|
to: recipientBytes32,
|
|
@@ -20623,7 +20962,7 @@ var RebalancingService = class {
|
|
|
20623
20962
|
functionName: "balanceOf",
|
|
20624
20963
|
args: [this.walletAddress]
|
|
20625
20964
|
});
|
|
20626
|
-
const balanceDecimal = new import_decimal3.Decimal((0,
|
|
20965
|
+
const balanceDecimal = new import_decimal3.Decimal((0, import_viem28.formatUnits)(balanceWei, Number(decimals)));
|
|
20627
20966
|
return balanceDecimal.toNumber();
|
|
20628
20967
|
} catch (error) {
|
|
20629
20968
|
this.logger.error({ error, chain, asset }, "Failed to get current balance");
|
|
@@ -20671,11 +21010,11 @@ function byBestRate(a, b) {
|
|
|
20671
21010
|
return left === right ? 0 : left > right ? -1 : 1;
|
|
20672
21011
|
}
|
|
20673
21012
|
function chargedFor(hold, delivered, released) {
|
|
20674
|
-
|
|
20675
|
-
|
|
20676
|
-
|
|
20677
|
-
const atRate = (
|
|
20678
|
-
return atRate > delivered ? atRate : delivered;
|
|
21013
|
+
const take = hold.take === void 0 ? 0n : BigInt(hold.take);
|
|
21014
|
+
if (take === 0n || released === 0n) return toScaledUp(delivered.raw, delivered.decimals);
|
|
21015
|
+
const offered = toRaw(BigInt(hold.amount), delivered.decimals);
|
|
21016
|
+
const atRate = (offered * released + take - 1n) / take;
|
|
21017
|
+
return toScaledUp(atRate > delivered.raw ? atRate : delivered.raw, delivered.decimals);
|
|
20679
21018
|
}
|
|
20680
21019
|
var IntentFiller = class {
|
|
20681
21020
|
constructor(chainConfigs, strategies, config, configService, chainClientManager, contractService, signer, scanners, rebalancingService, bidStorage, limitOrders, assetRegistry) {
|
|
@@ -20752,6 +21091,17 @@ var IntentFiller = class {
|
|
|
20752
21091
|
setLimitOrderService(service) {
|
|
20753
21092
|
this.limitOrderService = service;
|
|
20754
21093
|
}
|
|
21094
|
+
/**
|
|
21095
|
+
* Brings the limit orders' `remaining` in line with their on-chain tallies.
|
|
21096
|
+
*
|
|
21097
|
+
* On the settlement queue, so the pass and a fill never resize the same order at
|
|
21098
|
+
* once. Resolves when the pass has run.
|
|
21099
|
+
*/
|
|
21100
|
+
async reconcileLimitOrderTallies() {
|
|
21101
|
+
await this.settlementQueue.add(async () => {
|
|
21102
|
+
await this.limitOrderService?.reconcileTallies();
|
|
21103
|
+
});
|
|
21104
|
+
}
|
|
20755
21105
|
/** Whether the given chain id is configured for watch-only (monitor, never fill). */
|
|
20756
21106
|
isChainWatchOnly(chainId) {
|
|
20757
21107
|
const watchOnly = this.config.watchOnly;
|
|
@@ -21397,7 +21747,7 @@ var IntentFiller = class {
|
|
|
21397
21747
|
let reservation = [];
|
|
21398
21748
|
if (plan) {
|
|
21399
21749
|
if (order.id) {
|
|
21400
|
-
cache.setFillerOutputs(order.id, plan.fillerOutputs, plan.fillerInputs);
|
|
21750
|
+
cache.setFillerOutputs(order.id, plan.fillerOutputs, plan.fillerInputs, plan.budget);
|
|
21401
21751
|
cache.setPartialFill(order.id, plan.partialFill);
|
|
21402
21752
|
if (plan.fundingCalls.length > 0) cache.setFundingPrepends(order.id, plan.fundingCalls);
|
|
21403
21753
|
else cache.clearFundingPrepends(order.id);
|
|
@@ -21598,7 +21948,7 @@ var IntentFiller = class {
|
|
|
21598
21948
|
await this.releaseAll(claimed);
|
|
21599
21949
|
return;
|
|
21600
21950
|
}
|
|
21601
|
-
const settled = executedHold(claimed, delivered, released);
|
|
21951
|
+
const settled = executedHold(claimed, delivered.scaled, released);
|
|
21602
21952
|
const charged = chargedFor(settled, delivered, released);
|
|
21603
21953
|
const settledBid = rows.find(
|
|
21604
21954
|
(row) => row.reservations.some(
|
|
@@ -21651,15 +22001,15 @@ var IntentFiller = class {
|
|
|
21651
22001
|
if (delivered === null) return [];
|
|
21652
22002
|
const settled = executedHold(
|
|
21653
22003
|
holds.map((entry) => entry.hold),
|
|
21654
|
-
delivered,
|
|
22004
|
+
delivered.scaled,
|
|
21655
22005
|
released
|
|
21656
22006
|
);
|
|
21657
22007
|
const row = holds.find((entry) => entry.hold === settled).row;
|
|
21658
22008
|
return this.bidStorage.claimReservation(commitment, row.bid ?? void 0);
|
|
21659
22009
|
}
|
|
21660
22010
|
/**
|
|
21661
|
-
* What this fill delivered of the token the limit order pays,
|
|
21662
|
-
*
|
|
22011
|
+
* What this fill delivered of the token the limit order pays, or null when the
|
|
22012
|
+
* fill names none of it.
|
|
21663
22013
|
*/
|
|
21664
22014
|
async deliveredAgainst(limitOrderId, chainId, outputs) {
|
|
21665
22015
|
const order = await this.limitOrders?.get(limitOrderId);
|
|
@@ -21675,7 +22025,7 @@ var IntentFiller = class {
|
|
|
21675
22025
|
}
|
|
21676
22026
|
if (total === 0n) return null;
|
|
21677
22027
|
const decimals = await this.contractService.getTokenDecimals(address, chain);
|
|
21678
|
-
return toScaled(total, decimals);
|
|
22028
|
+
return { scaled: toScaled(total, decimals), raw: total, decimals };
|
|
21679
22029
|
}
|
|
21680
22030
|
/**
|
|
21681
22031
|
* Gives back whatever this bid still holds against its limit order.
|
|
@@ -21765,94 +22115,9 @@ var IntentFiller = class {
|
|
|
21765
22115
|
|
|
21766
22116
|
// src/strategies/fx.ts
|
|
21767
22117
|
var import_sdk15 = require("@hyperbridge/sdk");
|
|
21768
|
-
var
|
|
22118
|
+
var import_viem29 = require("viem");
|
|
21769
22119
|
var import_decimal6 = require("decimal.js");
|
|
21770
22120
|
|
|
21771
|
-
// src/orderbook/matching.ts
|
|
21772
|
-
function limitOrderLegs(order) {
|
|
21773
|
-
return order.side === "BID" ? { input: order.base, output: order.quote } : { input: order.quote, output: order.base };
|
|
21774
|
-
}
|
|
21775
|
-
function availableOn(order) {
|
|
21776
|
-
const available = BigInt(order.remaining) - BigInt(order.reserved);
|
|
21777
|
-
return available > 0n ? available : 0n;
|
|
21778
|
-
}
|
|
21779
|
-
function matchLimitOrders(orders, incoming, resolve, now = /* @__PURE__ */ new Date()) {
|
|
21780
|
-
const qualifying = orders.filter((order) => serves(order, incoming, resolve, now)).map((order) => {
|
|
21781
|
-
const offer = offerFor({
|
|
21782
|
-
side: order.side,
|
|
21783
|
-
inputAmount: incoming.inputNet,
|
|
21784
|
-
price: BigInt(order.price),
|
|
21785
|
-
outputDecimals: incoming.outputDecimals
|
|
21786
|
-
});
|
|
21787
|
-
const available = availableOn(order);
|
|
21788
|
-
return { order, offer, available, payout: offer < available ? offer : available };
|
|
21789
|
-
}).filter((candidate) => candidate.offer >= incoming.requestedOutput && candidate.payout > 0n).sort(byBestOfferFirst);
|
|
21790
|
-
return qualifying;
|
|
21791
|
-
}
|
|
21792
|
-
function byBestOfferFirst(a, b) {
|
|
21793
|
-
if (a.offer !== b.offer) return a.offer > b.offer ? -1 : 1;
|
|
21794
|
-
if (a.available !== b.available) return a.available > b.available ? -1 : 1;
|
|
21795
|
-
return a.order.id < b.order.id ? -1 : 1;
|
|
21796
|
-
}
|
|
21797
|
-
function whyUnmatched(orders, incoming, resolve, now = /* @__PURE__ */ new Date()) {
|
|
21798
|
-
let furthest = -1;
|
|
21799
|
-
for (const order of orders) {
|
|
21800
|
-
const failed = firstFailed(order, incoming, resolve, now);
|
|
21801
|
-
furthest = Math.max(furthest, failed === null ? CHECKS.length : CHECKS.indexOf(failed));
|
|
21802
|
-
}
|
|
21803
|
-
const { source, destination, inputSymbol } = incoming;
|
|
21804
|
-
if (furthest < CHECKS.length) {
|
|
21805
|
-
switch (CHECKS[Math.max(furthest, 0)]) {
|
|
21806
|
-
case "open":
|
|
21807
|
-
return "no limit order is open";
|
|
21808
|
-
case "chain":
|
|
21809
|
-
return `no open limit order fills on ${destination}`;
|
|
21810
|
-
case "input":
|
|
21811
|
-
return `no limit order on ${destination} takes ${inputSymbol} in`;
|
|
21812
|
-
case "output":
|
|
21813
|
-
return `no limit order on ${destination} taking ${inputSymbol} in pays out the token this order asks for`;
|
|
21814
|
-
case "source":
|
|
21815
|
-
return `no limit order for this pair on ${destination} accepts swaps from ${source}`;
|
|
21816
|
-
}
|
|
21817
|
-
}
|
|
21818
|
-
const priced = orders.filter((order) => firstFailed(order, incoming, resolve, now) === null).map((order) => ({
|
|
21819
|
-
order,
|
|
21820
|
-
offer: offerFor({
|
|
21821
|
-
side: order.side,
|
|
21822
|
-
inputAmount: incoming.inputNet,
|
|
21823
|
-
price: BigInt(order.price),
|
|
21824
|
-
outputDecimals: incoming.outputDecimals
|
|
21825
|
-
})
|
|
21826
|
-
}));
|
|
21827
|
-
if (priced.some(({ offer }) => offer >= incoming.requestedOutput)) {
|
|
21828
|
-
return "the limit orders that meet its rate have nothing left to pay out";
|
|
21829
|
-
}
|
|
21830
|
-
const best = priced.reduce((a, b) => b.offer > a.offer ? b : a);
|
|
21831
|
-
const symbol = limitOrderLegs(best.order).output;
|
|
21832
|
-
return `it asks for ${readable(incoming.requestedOutput)} ${symbol}; the best limit order offers ${readable(best.offer)} ${symbol}`;
|
|
21833
|
-
}
|
|
21834
|
-
var CHECKS = ["open", "chain", "input", "output", "source"];
|
|
21835
|
-
function serves(order, incoming, resolve, now) {
|
|
21836
|
-
return firstFailed(order, incoming, resolve, now) === null;
|
|
21837
|
-
}
|
|
21838
|
-
function firstFailed(order, incoming, resolve, now) {
|
|
21839
|
-
if (order.status !== "open") return "open";
|
|
21840
|
-
if (order.expiresAt !== null && new Date(order.expiresAt) <= now) return "open";
|
|
21841
|
-
if (order.fillChain !== incoming.destination) return "chain";
|
|
21842
|
-
const legs = limitOrderLegs(order);
|
|
21843
|
-
if (normalizeSymbol(legs.input) !== normalizeSymbol(incoming.inputSymbol)) return "input";
|
|
21844
|
-
const outputToken = resolve(legs.output, order.fillChain);
|
|
21845
|
-
if (!outputToken || outputToken.toLowerCase() !== incoming.outputToken.toLowerCase()) return "output";
|
|
21846
|
-
if (incoming.source === incoming.destination) return null;
|
|
21847
|
-
return order.acceptedSources.includes(incoming.source) ? null : "source";
|
|
21848
|
-
}
|
|
21849
|
-
function readable(amount) {
|
|
21850
|
-
const [whole, fraction] = toHuman(amount).split(".");
|
|
21851
|
-
const grouped = whole.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
21852
|
-
const cut = fraction?.slice(0, 6).replace(/0+$/, "");
|
|
21853
|
-
return cut ? `${grouped}.${cut}` : grouped;
|
|
21854
|
-
}
|
|
21855
|
-
|
|
21856
22121
|
// src/orderbook/usd.ts
|
|
21857
22122
|
var import_decimal5 = require("decimal.js");
|
|
21858
22123
|
function limitOrderUsdEdges(orders) {
|
|
@@ -21907,9 +22172,13 @@ var FXFiller = class _FXFiller {
|
|
|
21907
22172
|
*/
|
|
21908
22173
|
constructor(signer, configService, clientManager, contractService, pairs, registry, options) {
|
|
21909
22174
|
this.name = "FXFiller";
|
|
21910
|
-
/**
|
|
22175
|
+
/**
|
|
22176
|
+
* Consecutive orders where the overfill clamp activated. Dormant: the clamp
|
|
22177
|
+
* existed for venue-priced legs, which went with Uniswap V4 pricing, so every
|
|
22178
|
+
* outcome is now recorded unclamped and this never climbs. See `recordOrderOutcome`.
|
|
22179
|
+
*/
|
|
21911
22180
|
this.consecutiveClamps = 0;
|
|
21912
|
-
/** Once set, the filler refuses all orders until
|
|
22181
|
+
/** Once set, the filler refuses all orders until reset. Unreachable while the clamp is dormant. */
|
|
21913
22182
|
this.halted = false;
|
|
21914
22183
|
this.logger = moduleLogger(configService.loggers, "fx-simplex");
|
|
21915
22184
|
const { confirmationPolicy, fundingVenues = [], limitOrders } = options ?? {};
|
|
@@ -21958,10 +22227,9 @@ var FXFiller = class _FXFiller {
|
|
|
21958
22227
|
this.pairs.push(pair);
|
|
21959
22228
|
}
|
|
21960
22229
|
/**
|
|
21961
|
-
* Removes a market from the running engine.
|
|
21962
|
-
*
|
|
21963
|
-
*
|
|
21964
|
-
* rechecks of orders on the removed pair simply stop matching.
|
|
22230
|
+
* Removes a market from the running engine. At least one market must remain.
|
|
22231
|
+
* Matching reads the limit orders rather than the pairs, so removing a market
|
|
22232
|
+
* changes what the dashboard lists, not what the filler fills.
|
|
21965
22233
|
*/
|
|
21966
22234
|
removePair(pair) {
|
|
21967
22235
|
const index = this.pairs.indexOf(pair);
|
|
@@ -22017,22 +22285,20 @@ var FXFiller = class _FXFiller {
|
|
|
22017
22285
|
}
|
|
22018
22286
|
}
|
|
22019
22287
|
/**
|
|
22020
|
-
* Evaluates whether an order is profitable to fill
|
|
22021
|
-
*
|
|
22288
|
+
* Evaluates whether an order is profitable to fill against the operator's
|
|
22289
|
+
* limit orders and the filler's current token balances.
|
|
22022
22290
|
*
|
|
22023
22291
|
* High-level flow:
|
|
22024
|
-
* -
|
|
22025
|
-
* -
|
|
22026
|
-
*
|
|
22027
|
-
*
|
|
22028
|
-
*
|
|
22029
|
-
*
|
|
22030
|
-
*
|
|
22031
|
-
*
|
|
22032
|
-
*
|
|
22033
|
-
*
|
|
22034
|
-
* Note: we may intentionally overfill relative to the user's requested
|
|
22035
|
-
* outputs if the pair pricing makes that attractive. This is how we stay competitive.
|
|
22292
|
+
* - Match each (input, output) leg against the open limit orders.
|
|
22293
|
+
* - Build one bid per matching order, best offer first, each paying the
|
|
22294
|
+
* order's whole offer for the input at its own rate, capped by what the
|
|
22295
|
+
* order has left. The gateway credits the swapper the ask and splits
|
|
22296
|
+
* anything above it between the swapper and the protocol.
|
|
22297
|
+
* - Treat a payout below the ask as a partial fill, which only orders
|
|
22298
|
+
* without output calldata and without an earlier partial fill allow.
|
|
22299
|
+
* - Cap each payout by the filler's balance plus funding-venue withdrawals,
|
|
22300
|
+
* then gate it on fees (full fills) and the same-asset spread.
|
|
22301
|
+
* - Cache the resulting bids for later use in `executeOrder`.
|
|
22036
22302
|
*/
|
|
22037
22303
|
async calculateProfitability(order) {
|
|
22038
22304
|
if (order.id) this.contractService.cacheService.clearPartialFill(order.id);
|
|
@@ -22050,6 +22316,7 @@ var FXFiller = class _FXFiller {
|
|
|
22050
22316
|
const multiLeg = order.inputs.length > 1;
|
|
22051
22317
|
const plans = [];
|
|
22052
22318
|
const plannedOn = /* @__PURE__ */ new Map();
|
|
22319
|
+
const spentOn = /* @__PURE__ */ new Map();
|
|
22053
22320
|
for (let leg = 0; leg < order.inputs.length; leg++) {
|
|
22054
22321
|
const input = order.inputs[leg];
|
|
22055
22322
|
const output = order.output.assets[leg];
|
|
@@ -22071,7 +22338,30 @@ var FXFiller = class _FXFiller {
|
|
|
22071
22338
|
for (const candidate of matches) {
|
|
22072
22339
|
let partialFill = false;
|
|
22073
22340
|
const fundingCalls = [];
|
|
22074
|
-
const
|
|
22341
|
+
const budget = budgetFor(candidate.order, outputToken, outputDecimals);
|
|
22342
|
+
if (!spentOn.has(candidate.order.id)) {
|
|
22343
|
+
spentOn.set(
|
|
22344
|
+
candidate.order.id,
|
|
22345
|
+
await this.contractService.limitOrderSpent(destChain, budget.budgetId)
|
|
22346
|
+
);
|
|
22347
|
+
}
|
|
22348
|
+
const spent = spentOn.get(candidate.order.id) ?? null;
|
|
22349
|
+
const room = spent === null ? candidate.available : budgetRoom(budget, spent, outputDecimals);
|
|
22350
|
+
if (room === 0n) {
|
|
22351
|
+
this.logger.info(
|
|
22352
|
+
{
|
|
22353
|
+
orderId: order.id,
|
|
22354
|
+
leg,
|
|
22355
|
+
limitOrder: candidate.order.id,
|
|
22356
|
+
spent: spent?.toString(),
|
|
22357
|
+
cap: budget.cap.toString()
|
|
22358
|
+
},
|
|
22359
|
+
"Skipping a bid: the limit order has nothing left on chain"
|
|
22360
|
+
);
|
|
22361
|
+
continue;
|
|
22362
|
+
}
|
|
22363
|
+
const available = room < candidate.available ? room : candidate.available;
|
|
22364
|
+
const left = available - (plannedOn.get(candidate.order.id) ?? 0n);
|
|
22075
22365
|
if (left <= 0n) {
|
|
22076
22366
|
this.logger.info(
|
|
22077
22367
|
{ orderId: order.id, leg, limitOrder: candidate.order.id },
|
|
@@ -22106,10 +22396,7 @@ var FXFiller = class _FXFiller {
|
|
|
22106
22396
|
{
|
|
22107
22397
|
orderId: order.id,
|
|
22108
22398
|
limitOrder: candidate.order.id,
|
|
22109
|
-
available: (0,
|
|
22110
|
-
candidate.available,
|
|
22111
|
-
18
|
|
22112
|
-
),
|
|
22399
|
+
available: (0, import_viem29.formatUnits)(available, 18),
|
|
22113
22400
|
userRequested: output.amount.toString(),
|
|
22114
22401
|
payout: targetOutput.toString(),
|
|
22115
22402
|
crossChain: sourceChain !== destChain
|
|
@@ -22154,7 +22441,10 @@ var FXFiller = class _FXFiller {
|
|
|
22154
22441
|
}
|
|
22155
22442
|
}
|
|
22156
22443
|
const effectiveBalance = walletContribution + credited;
|
|
22157
|
-
const finalOutputAmount =
|
|
22444
|
+
const finalOutputAmount = toRaw(
|
|
22445
|
+
toScaled(effectiveBalance > targetOutput ? targetOutput : effectiveBalance, outputDecimals),
|
|
22446
|
+
outputDecimals
|
|
22447
|
+
);
|
|
22158
22448
|
if (finalOutputAmount === 0n) {
|
|
22159
22449
|
this.logger.info(
|
|
22160
22450
|
{
|
|
@@ -22243,7 +22533,7 @@ var FXFiller = class _FXFiller {
|
|
|
22243
22533
|
const spread = convertedInput - finalOutputAmount;
|
|
22244
22534
|
if (spread <= 0n) sameAssetProfitable = false;
|
|
22245
22535
|
realizedSpreadProfit = adjustDecimalsFloor(spread, outputDecimals, feeTokenDecimals);
|
|
22246
|
-
const spreadUsd = outputSymbol && usdValueOf(usdFactors, outputSymbol, new import_decimal6.Decimal((0,
|
|
22536
|
+
const spreadUsd = outputSymbol && usdValueOf(usdFactors, outputSymbol, new import_decimal6.Decimal((0, import_viem29.formatUnits)(spread, outputDecimals)));
|
|
22247
22537
|
if (spreadUsd) sameAssetEdgeUsd = spreadUsd;
|
|
22248
22538
|
}
|
|
22249
22539
|
const payoutSurplusUsd = new import_decimal6.Decimal(0);
|
|
@@ -22258,9 +22548,9 @@ var FXFiller = class _FXFiller {
|
|
|
22258
22548
|
{
|
|
22259
22549
|
orderId: order.id,
|
|
22260
22550
|
feeToken: feeTokenLower,
|
|
22261
|
-
residual: (0,
|
|
22262
|
-
dispatchFee: (0,
|
|
22263
|
-
required: (0,
|
|
22551
|
+
residual: (0, import_viem29.formatUnits)(residual, feeToken.decimals),
|
|
22552
|
+
dispatchFee: (0, import_viem29.formatUnits)(dispatchFee, feeToken.decimals),
|
|
22553
|
+
required: (0, import_viem29.formatUnits)(required, feeToken.decimals)
|
|
22264
22554
|
},
|
|
22265
22555
|
"Skipping order: fill leaves too little of the fee token to dispatch the escrow release"
|
|
22266
22556
|
);
|
|
@@ -22273,10 +22563,10 @@ var FXFiller = class _FXFiller {
|
|
|
22273
22563
|
this.logger.info(
|
|
22274
22564
|
{
|
|
22275
22565
|
orderId: order.id,
|
|
22276
|
-
orderFees: (0,
|
|
22277
|
-
fillGas: (0,
|
|
22278
|
-
relayerFee: (0,
|
|
22279
|
-
executionCost: (0,
|
|
22566
|
+
orderFees: (0, import_viem29.formatUnits)(order.fees, feeTokenDecimals),
|
|
22567
|
+
fillGas: (0, import_viem29.formatUnits)(totalCostInSourceFeeToken, feeTokenDecimals),
|
|
22568
|
+
relayerFee: (0, import_viem29.formatUnits)(relayerFeeInSourceFeeToken, feeTokenDecimals),
|
|
22569
|
+
executionCost: (0, import_viem29.formatUnits)(executionCost, feeTokenDecimals)
|
|
22280
22570
|
},
|
|
22281
22571
|
"Skipping order: attached fees do not cover execution cost (fill gas + relayer fee)"
|
|
22282
22572
|
);
|
|
@@ -22284,13 +22574,13 @@ var FXFiller = class _FXFiller {
|
|
|
22284
22574
|
}
|
|
22285
22575
|
if (sameAsset && !sameAssetProfitable) {
|
|
22286
22576
|
this.logger.info(
|
|
22287
|
-
{ orderId: order.id, realizedSpreadProfit: (0,
|
|
22577
|
+
{ orderId: order.id, realizedSpreadProfit: (0, import_viem29.formatUnits)(realizedSpreadProfit, feeTokenDecimals) },
|
|
22288
22578
|
"Skipping order: a same-asset fill does not net a positive spread"
|
|
22289
22579
|
);
|
|
22290
22580
|
continue;
|
|
22291
22581
|
}
|
|
22292
22582
|
const feeProfit = order.fees - executionCost;
|
|
22293
|
-
const totalProfit = partialFill ? partialEdgeUsd.toNumber() : Number.parseFloat((0,
|
|
22583
|
+
const totalProfit = partialFill ? partialEdgeUsd.toNumber() : Number.parseFloat((0, import_viem29.formatUnits)(feeProfit + realizedSpreadProfit, feeTokenDecimals));
|
|
22294
22584
|
this.logger.info(
|
|
22295
22585
|
{
|
|
22296
22586
|
orderId: order.id,
|
|
@@ -22306,12 +22596,12 @@ var FXFiller = class _FXFiller {
|
|
|
22306
22596
|
limitOrders: matches.length,
|
|
22307
22597
|
available: candidate.available.toString(),
|
|
22308
22598
|
payout: targetOutput.toString(),
|
|
22309
|
-
orderFees: (0,
|
|
22310
|
-
fillGas: (0,
|
|
22311
|
-
relayerFee: (0,
|
|
22312
|
-
executionCost: (0,
|
|
22313
|
-
feeProfit: (0,
|
|
22314
|
-
realizedSpreadProfit: (0,
|
|
22599
|
+
orderFees: (0, import_viem29.formatUnits)(order.fees, feeTokenDecimals),
|
|
22600
|
+
fillGas: (0, import_viem29.formatUnits)(totalCostInSourceFeeToken, feeTokenDecimals),
|
|
22601
|
+
relayerFee: (0, import_viem29.formatUnits)(relayerFeeInSourceFeeToken, feeTokenDecimals),
|
|
22602
|
+
executionCost: (0, import_viem29.formatUnits)(executionCost, feeTokenDecimals),
|
|
22603
|
+
feeProfit: (0, import_viem29.formatUnits)(feeProfit, feeTokenDecimals),
|
|
22604
|
+
realizedSpreadProfit: (0, import_viem29.formatUnits)(realizedSpreadProfit, feeTokenDecimals),
|
|
22315
22605
|
payoutSurplusUsd: payoutSurplusUsd.toString(),
|
|
22316
22606
|
totalProfit,
|
|
22317
22607
|
profitable: totalProfit > 0
|
|
@@ -22330,7 +22620,8 @@ var FXFiller = class _FXFiller {
|
|
|
22330
22620
|
fillerInputs,
|
|
22331
22621
|
fundingCalls: [...fundingCalls],
|
|
22332
22622
|
partialFill,
|
|
22333
|
-
profit: totalProfit
|
|
22623
|
+
profit: totalProfit,
|
|
22624
|
+
budget
|
|
22334
22625
|
});
|
|
22335
22626
|
if (!partialEligibleCheap) break;
|
|
22336
22627
|
}
|
|
@@ -22338,7 +22629,12 @@ var FXFiller = class _FXFiller {
|
|
|
22338
22629
|
if (plans.length === 0) return 0;
|
|
22339
22630
|
if (order.id) {
|
|
22340
22631
|
this.contractService.cacheService.setBidPlans(order.id, plans);
|
|
22341
|
-
this.contractService.cacheService.setFillerOutputs(
|
|
22632
|
+
this.contractService.cacheService.setFillerOutputs(
|
|
22633
|
+
order.id,
|
|
22634
|
+
plans[0].fillerOutputs,
|
|
22635
|
+
plans[0].fillerInputs,
|
|
22636
|
+
plans[0].budget
|
|
22637
|
+
);
|
|
22342
22638
|
this.contractService.cacheService.setPartialFill(order.id, plans[0].partialFill);
|
|
22343
22639
|
this.contractService.cacheService.setMatchedLimitOrder(order.id, [
|
|
22344
22640
|
{ limitOrderId: plans[0].limitOrderId, payout: plans[0].payout }
|
|
@@ -22433,10 +22729,14 @@ var FXFiller = class _FXFiller {
|
|
|
22433
22729
|
// =========================================================================
|
|
22434
22730
|
/**
|
|
22435
22731
|
* Update consecutive-clamp counter after a successful order evaluation.
|
|
22436
|
-
*
|
|
22437
|
-
*
|
|
22438
|
-
*
|
|
22439
|
-
*
|
|
22732
|
+
*
|
|
22733
|
+
* Dormant. Only venue-priced legs ever fed a clamped outcome here: a streak
|
|
22734
|
+
* of those meant a live market source had gone off (stale pool, manipulated
|
|
22735
|
+
* venue). Venue pricing went with Uniswap V4, and limit orders are prices the
|
|
22736
|
+
* operator set, so the only caller records `false` and the filler never
|
|
22737
|
+
* halts. `isHalted`, `resetHalt` and `[simplex.overfillProtection]
|
|
22738
|
+
* maxConsecutiveClamps` are kept so existing configs and the halt API still
|
|
22739
|
+
* load; they can go once nothing reads them.
|
|
22440
22740
|
*/
|
|
22441
22741
|
recordOrderOutcome(clamped, orderId) {
|
|
22442
22742
|
if (clamped) {
|
|
@@ -22462,18 +22762,6 @@ var FXFiller = class _FXFiller {
|
|
|
22462
22762
|
this.consecutiveClamps = 0;
|
|
22463
22763
|
this.logger.warn("FXFiller halt reset by operator \u2014 resuming order evaluation");
|
|
22464
22764
|
}
|
|
22465
|
-
/**
|
|
22466
|
-
* Given a single (input, output) leg and the remaining token0 budget of its
|
|
22467
|
-
* pair, computes how much token0 notional to allocate to this leg and the
|
|
22468
|
-
* corresponding maximum output amount at the pair's rate.
|
|
22469
|
-
*
|
|
22470
|
-
* `rate` is **token1 per 1 token0**:
|
|
22471
|
-
* - token0 input → token1 output: token0 × rate → token1 amount.
|
|
22472
|
-
* - token1 input → token0 output: token1 ÷ rate → token0 amount.
|
|
22473
|
-
*
|
|
22474
|
-
* Returns `null` when this leg cannot consume any of the pair's remaining
|
|
22475
|
-
* budget (e.g. the cap has already been exhausted).
|
|
22476
|
-
*/
|
|
22477
22765
|
/**
|
|
22478
22766
|
* Whether any solver has already delivered output against this order.
|
|
22479
22767
|
*
|
|
@@ -22613,7 +22901,7 @@ var FXFiller = class _FXFiller {
|
|
|
22613
22901
|
const symbol = this.registry.symbolFor(inputToken, order.source);
|
|
22614
22902
|
if (!symbol) return null;
|
|
22615
22903
|
const decimals = await this.contractService.getTokenDecimals(inputToken, order.source);
|
|
22616
|
-
const legUsd = usdValueOf(factors, symbol, new import_decimal6.Decimal((0,
|
|
22904
|
+
const legUsd = usdValueOf(factors, symbol, new import_decimal6.Decimal((0, import_viem29.formatUnits)(input.amount, decimals)));
|
|
22617
22905
|
if (!legUsd) return null;
|
|
22618
22906
|
inputUsd = inputUsd.plus(legUsd);
|
|
22619
22907
|
}
|
|
@@ -22626,7 +22914,7 @@ function minBigInt(first, ...rest) {
|
|
|
22626
22914
|
|
|
22627
22915
|
// src/services/TokenSender.ts
|
|
22628
22916
|
var import_sdk16 = require("@hyperbridge/sdk");
|
|
22629
|
-
var
|
|
22917
|
+
var import_viem30 = require("viem");
|
|
22630
22918
|
var TokenSender = class {
|
|
22631
22919
|
constructor(clientManager, solver, getVaults, userOpSender, configService) {
|
|
22632
22920
|
this.clientManager = clientManager;
|
|
@@ -22638,18 +22926,18 @@ var TokenSender = class {
|
|
|
22638
22926
|
}
|
|
22639
22927
|
async send(params) {
|
|
22640
22928
|
const { chain, token, amount, to } = params;
|
|
22641
|
-
if (!(0,
|
|
22929
|
+
if (!(0, import_viem30.isAddress)(to)) throw new Error(`Invalid recipient address: ${to}`);
|
|
22642
22930
|
const publicClient = this.clientManager.getPublicClient(chain);
|
|
22643
22931
|
const calls = [];
|
|
22644
22932
|
let redeemed = false;
|
|
22645
22933
|
if (token === "native") {
|
|
22646
|
-
const value = (0,
|
|
22934
|
+
const value = (0, import_viem30.parseUnits)(amount, 18);
|
|
22647
22935
|
if (value <= 0n) throw new Error("Amount must be greater than 0");
|
|
22648
22936
|
const balance = await publicClient.getBalance({ address: this.solver });
|
|
22649
22937
|
if (balance < value) throw new Error(`Insufficient native balance: have ${balance}, need ${value}`);
|
|
22650
22938
|
calls.push({ target: to, value, data: "0x" });
|
|
22651
22939
|
} else {
|
|
22652
|
-
if (!(0,
|
|
22940
|
+
if (!(0, import_viem30.isAddress)(token)) throw new Error(`Invalid token address: ${token}`);
|
|
22653
22941
|
const tokenLower = token.toLowerCase();
|
|
22654
22942
|
const [decimals, balance] = await Promise.all([
|
|
22655
22943
|
publicClient.readContract({ abi: ERC20_ABI, address: token, functionName: "decimals" }),
|
|
@@ -22660,7 +22948,7 @@ var TokenSender = class {
|
|
|
22660
22948
|
args: [this.solver]
|
|
22661
22949
|
})
|
|
22662
22950
|
]);
|
|
22663
|
-
const units = (0,
|
|
22951
|
+
const units = (0, import_viem30.parseUnits)(amount, decimals);
|
|
22664
22952
|
if (units <= 0n) throw new Error("Amount must be greater than 0");
|
|
22665
22953
|
const isVaultShare = this.getVaults().some(
|
|
22666
22954
|
(v) => v.chain === chain && v.vault.toLowerCase() === tokenLower
|
|
@@ -22671,7 +22959,7 @@ var TokenSender = class {
|
|
|
22671
22959
|
calls.push({
|
|
22672
22960
|
target: token,
|
|
22673
22961
|
value: 0n,
|
|
22674
|
-
data: (0,
|
|
22962
|
+
data: (0, import_viem30.encodeFunctionData)({ abi: ERC4626_ABI, functionName: "redeem", args: [units, to, this.solver] })
|
|
22675
22963
|
});
|
|
22676
22964
|
} else {
|
|
22677
22965
|
const reserve = this.userOpSender?.canSponsor(chain) && this.configService ? paymasterReserveForToken(chain, tokenLower, this.configService) : 0n;
|
|
@@ -22694,7 +22982,7 @@ var TokenSender = class {
|
|
|
22694
22982
|
calls.push({
|
|
22695
22983
|
target: token,
|
|
22696
22984
|
value: 0n,
|
|
22697
|
-
data: (0,
|
|
22985
|
+
data: (0, import_viem30.encodeFunctionData)({ abi: ERC20_ABI, functionName: "transfer", args: [to, units] })
|
|
22698
22986
|
});
|
|
22699
22987
|
}
|
|
22700
22988
|
}
|
|
@@ -22737,7 +23025,7 @@ var TokenSender = class {
|
|
|
22737
23025
|
});
|
|
22738
23026
|
const required = shortfall > 0n ? shortfall : 0n;
|
|
22739
23027
|
if (available < required) continue;
|
|
22740
|
-
const floor = vault.minBalance === void 0 ? 0n : (0,
|
|
23028
|
+
const floor = vault.minBalance === void 0 ? 0n : (0, import_viem30.parseUnits)(vault.minBalance, decimals);
|
|
22741
23029
|
const headroom = reserve > floor ? reserve : floor;
|
|
22742
23030
|
const wanted = shortfall + headroom;
|
|
22743
23031
|
const amount = available >= wanted ? wanted : required;
|
|
@@ -22745,7 +23033,7 @@ var TokenSender = class {
|
|
|
22745
23033
|
return {
|
|
22746
23034
|
target: vault.vault,
|
|
22747
23035
|
value: 0n,
|
|
22748
|
-
data: (0,
|
|
23036
|
+
data: (0, import_viem30.encodeFunctionData)({
|
|
22749
23037
|
abi: ERC4626_ABI,
|
|
22750
23038
|
functionName: "withdraw",
|
|
22751
23039
|
args: [amount, this.solver, this.solver]
|
|
@@ -22811,9 +23099,9 @@ var LimitOrderLifecycle = class {
|
|
|
22811
23099
|
const reconcileMs = this.options.reconcileIntervalSecs * 1e3;
|
|
22812
23100
|
this.every(heartbeatMs, "heartbeat", () => this.service.heartbeat());
|
|
22813
23101
|
this.every(EXPIRY_SWEEP_MS, "expiry", () => this.expire());
|
|
22814
|
-
this.every(reconcileMs, "reconciliation", () => this.
|
|
23102
|
+
this.every(reconcileMs, "reconciliation", () => this.upkeep());
|
|
22815
23103
|
this.logger.info({ heartbeatMs, expiryMs: EXPIRY_SWEEP_MS, reconcileMs }, "Orderbook lifecycle started");
|
|
22816
|
-
void this.
|
|
23104
|
+
void this.upkeep();
|
|
22817
23105
|
}
|
|
22818
23106
|
stop() {
|
|
22819
23107
|
this.timers.forEach((timer) => clearInterval(timer));
|
|
@@ -22824,6 +23112,14 @@ var LimitOrderLifecycle = class {
|
|
|
22824
23112
|
const expired = await this.service.expireStale();
|
|
22825
23113
|
if (expired > 0) this.logger.info({ expired }, "Withdrew limit orders that had outlived their expiry");
|
|
22826
23114
|
}
|
|
23115
|
+
/**
|
|
23116
|
+
* The tally pass first, so reconciliation against the orderbook reads the sizes
|
|
23117
|
+
* it left. Each fails on its own.
|
|
23118
|
+
*/
|
|
23119
|
+
async upkeep() {
|
|
23120
|
+
await this.run("tally reconciliation", async () => this.options.reconcileTallies());
|
|
23121
|
+
await this.run("reconciliation", () => this.reconcile());
|
|
23122
|
+
}
|
|
22827
23123
|
async reconcile() {
|
|
22828
23124
|
const report = await this.service.reconcile();
|
|
22829
23125
|
if (report.cancelled || report.reposted || report.underFunded) {
|
|
@@ -22859,7 +23155,7 @@ var LimitOrderLifecycle = class {
|
|
|
22859
23155
|
|
|
22860
23156
|
// src/services/BalanceProvider.ts
|
|
22861
23157
|
var import_node_events = require("node:events");
|
|
22862
|
-
var
|
|
23158
|
+
var import_viem31 = require("viem");
|
|
22863
23159
|
var BalanceProvider = class extends import_node_events.EventEmitter {
|
|
22864
23160
|
constructor(options) {
|
|
22865
23161
|
super();
|
|
@@ -22952,7 +23248,7 @@ var BalanceProvider = class extends import_node_events.EventEmitter {
|
|
|
22952
23248
|
try {
|
|
22953
23249
|
const native = await client.getBalance({ address: fillerAddr });
|
|
22954
23250
|
const symbol = nativeTokenSymbol(chainId);
|
|
22955
|
-
row.native = { symbol, amount: Number.parseFloat((0,
|
|
23251
|
+
row.native = { symbol, amount: Number.parseFloat((0, import_viem31.formatUnits)(native, 18)) };
|
|
22956
23252
|
} catch (err) {
|
|
22957
23253
|
issues.push({ chainId, source: "native", message: errorMessage(err) });
|
|
22958
23254
|
}
|
|
@@ -23023,7 +23319,7 @@ var BalanceProvider = class extends import_node_events.EventEmitter {
|
|
|
23023
23319
|
functionName: "balanceOf",
|
|
23024
23320
|
args: [fillerAddr]
|
|
23025
23321
|
});
|
|
23026
|
-
wallet = Number.parseFloat((0,
|
|
23322
|
+
wallet = Number.parseFloat((0, import_viem31.formatUnits)(balance, definition.decimals));
|
|
23027
23323
|
} catch (err) {
|
|
23028
23324
|
issues.push({
|
|
23029
23325
|
chainId,
|
|
@@ -23051,8 +23347,8 @@ var BalanceProvider = class extends import_node_events.EventEmitter {
|
|
|
23051
23347
|
available: wallet !== null && vaultDataAvailable ? Math.max(wallet - walletReserve, 0) + vaultAvailable : null,
|
|
23052
23348
|
vaults: positions.map((position) => ({
|
|
23053
23349
|
address: position.vault,
|
|
23054
|
-
position: Number.parseFloat((0,
|
|
23055
|
-
available: Number.parseFloat((0,
|
|
23350
|
+
position: Number.parseFloat((0, import_viem31.formatUnits)(position.positionAssets, position.decimals)),
|
|
23351
|
+
available: Number.parseFloat((0, import_viem31.formatUnits)(position.availableAssets, position.decimals)),
|
|
23056
23352
|
acceptsDeposits: position.acceptsDeposits
|
|
23057
23353
|
})),
|
|
23058
23354
|
status: complete ? "fresh" : wallet !== null || positions.length > 0 ? "partial" : "unavailable"
|
|
@@ -23095,8 +23391,8 @@ var BalanceProvider = class extends import_node_events.EventEmitter {
|
|
|
23095
23391
|
const api = await coprocessor.queryApi();
|
|
23096
23392
|
const account = await api.query.system.account(address);
|
|
23097
23393
|
const decimals = api.registry.chainDecimals[0] ?? 12;
|
|
23098
|
-
const free = Number.parseFloat((0,
|
|
23099
|
-
const reserved = Number.parseFloat((0,
|
|
23394
|
+
const free = Number.parseFloat((0, import_viem31.formatUnits)(BigInt(account.data.free.toString()), decimals));
|
|
23395
|
+
const reserved = Number.parseFloat((0, import_viem31.formatUnits)(BigInt(account.data.reserved.toString()), decimals));
|
|
23100
23396
|
this.snapshot = { ...this.snapshot, hyperbridge: { address, free, reserved } };
|
|
23101
23397
|
} catch (err) {
|
|
23102
23398
|
this.logger.warn({ err }, "Failed to fetch Hyperbridge balance");
|
|
@@ -23116,7 +23412,7 @@ function isConfiguredAddress(address) {
|
|
|
23116
23412
|
}
|
|
23117
23413
|
function sumFormatted(positions, field) {
|
|
23118
23414
|
return positions.reduce(
|
|
23119
|
-
(total, position) => total + Number.parseFloat((0,
|
|
23415
|
+
(total, position) => total + Number.parseFloat((0, import_viem31.formatUnits)(position[field], position.decimals)),
|
|
23120
23416
|
0
|
|
23121
23417
|
);
|
|
23122
23418
|
}
|
|
@@ -23624,7 +23920,7 @@ async function bootFiller(config, options) {
|
|
|
23624
23920
|
const sharedCacheService = new CacheService(options.loggers);
|
|
23625
23921
|
if (!options.signer && !globalWatchOnly && !allChainsWatchOnly(watchOnlyConfig, resolvedChains)) {
|
|
23626
23922
|
throw new Error(
|
|
23627
|
-
"A signer is required unless every chain is watch-only: pass `signer` to Simplex.start (privateKeySigner, turnkeySigner, mpcVaultSigner, viemSigner, or your own Signer implementation). Running the binary? Configure it under [simplex.signer]."
|
|
23923
|
+
"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]."
|
|
23628
23924
|
);
|
|
23629
23925
|
}
|
|
23630
23926
|
const chainClientManager = new ChainClientManager(configService, options.signer);
|
|
@@ -23671,48 +23967,42 @@ async function bootFiller(config, options) {
|
|
|
23671
23967
|
assertPairSymbolsResolve(config.pairs, assetRegistry, configuredChainNames);
|
|
23672
23968
|
}
|
|
23673
23969
|
const adminStrategies = [];
|
|
23674
|
-
const
|
|
23675
|
-
|
|
23676
|
-
|
|
23677
|
-
|
|
23678
|
-
|
|
23679
|
-
|
|
23680
|
-
|
|
23681
|
-
|
|
23682
|
-
|
|
23683
|
-
|
|
23684
|
-
|
|
23685
|
-
|
|
23686
|
-
token1: pair.token1,
|
|
23687
|
-
sameToken: normalizeSymbol(pair.token0) === normalizeSymbol(pair.token1)
|
|
23688
|
-
});
|
|
23970
|
+
const tradingPairs = (config.pairs ?? []).map((pair) => ({
|
|
23971
|
+
token0: pair.token0,
|
|
23972
|
+
token1: pair.token1
|
|
23973
|
+
}));
|
|
23974
|
+
tradingPairs.forEach((pair, pairIndex) => {
|
|
23975
|
+
adminStrategies.push({
|
|
23976
|
+
index: pairIndex,
|
|
23977
|
+
pairIndex,
|
|
23978
|
+
exotic: `${pair.token0}/${pair.token1}`,
|
|
23979
|
+
token0: pair.token0,
|
|
23980
|
+
token1: pair.token1,
|
|
23981
|
+
sameToken: normalizeSymbol(pair.token0) === normalizeSymbol(pair.token1)
|
|
23689
23982
|
});
|
|
23690
|
-
|
|
23691
|
-
|
|
23692
|
-
|
|
23693
|
-
)
|
|
23694
|
-
|
|
23695
|
-
|
|
23696
|
-
|
|
23697
|
-
|
|
23698
|
-
|
|
23699
|
-
|
|
23700
|
-
|
|
23701
|
-
|
|
23702
|
-
|
|
23703
|
-
|
|
23704
|
-
|
|
23705
|
-
|
|
23706
|
-
|
|
23707
|
-
|
|
23983
|
+
});
|
|
23984
|
+
const confirmationPolicy = assertConfirmationCoverage(
|
|
23985
|
+
config.confirmationPolicies,
|
|
23986
|
+
resolvedChains.map((c) => c.chainId)
|
|
23987
|
+
);
|
|
23988
|
+
const fundingVenues = vaultVenue ? [vaultVenue] : [];
|
|
23989
|
+
const engine = new FXFiller(
|
|
23990
|
+
runtimeSigner,
|
|
23991
|
+
configService,
|
|
23992
|
+
chainClientManager,
|
|
23993
|
+
contractService,
|
|
23994
|
+
tradingPairs,
|
|
23995
|
+
assetRegistry,
|
|
23996
|
+
{ confirmationPolicy, fundingVenues, limitOrders: options.data.limitOrders }
|
|
23997
|
+
);
|
|
23998
|
+
logger.info("Hydrating funding venue state...");
|
|
23999
|
+
await engine.initialise();
|
|
24000
|
+
const strategies = [engine];
|
|
23708
24001
|
const haltControls = strategies.map((engine2, index) => ({
|
|
23709
24002
|
index,
|
|
23710
24003
|
isHalted: () => engine2.isHalted(),
|
|
23711
24004
|
resetHalt: () => engine2.resetHalt()
|
|
23712
24005
|
}));
|
|
23713
|
-
if (vaultVenue) {
|
|
23714
|
-
await vaultVenue.initialise(runtimeSigner.address);
|
|
23715
|
-
}
|
|
23716
24006
|
let rebalancingService;
|
|
23717
24007
|
const rebalancingConfig = configService.getRebalancingConfig();
|
|
23718
24008
|
if (rebalancingConfig?.triggerPercentage !== void 0 && rebalancingConfig?.baseBalances) {
|
|
@@ -23769,7 +24059,8 @@ async function bootFiller(config, options) {
|
|
|
23769
24059
|
const lifecycle = new LimitOrderLifecycle(
|
|
23770
24060
|
limitOrderService,
|
|
23771
24061
|
{
|
|
23772
|
-
reconcileIntervalSecs: config.orderbook?.reconcileIntervalSecs ?? DEFAULT_RECONCILE_INTERVAL_SECS
|
|
24062
|
+
reconcileIntervalSecs: config.orderbook?.reconcileIntervalSecs ?? DEFAULT_RECONCILE_INTERVAL_SECS,
|
|
24063
|
+
reconcileTallies: () => intentFiller.reconcileLimitOrderTallies()
|
|
23773
24064
|
},
|
|
23774
24065
|
options.loggers
|
|
23775
24066
|
);
|
|
@@ -23807,8 +24098,8 @@ async function bootFiller(config, options) {
|
|
|
23807
24098
|
if (vaultVenue) {
|
|
23808
24099
|
vaultVenue.onTx = ({ chain, kind, txHash, sponsored, movements }) => {
|
|
23809
24100
|
const rows = movements.length > 0 ? movements.map((move) => {
|
|
23810
|
-
const underlying = { token: move.symbol, amount: (0,
|
|
23811
|
-
const shares = move.shares === void 0 ? { token: null, amount: null } : { token: move.shareSymbol, amount: (0,
|
|
24101
|
+
const underlying = { token: move.symbol, amount: (0, import_viem32.formatUnits)(move.amount, move.decimals) };
|
|
24102
|
+
const shares = move.shares === void 0 ? { token: null, amount: null } : { token: move.shareSymbol, amount: (0, import_viem32.formatUnits)(move.shares, move.shareDecimals) };
|
|
23812
24103
|
const out = kind === "sweep" ? underlying : shares;
|
|
23813
24104
|
const back = kind === "sweep" ? shares : underlying;
|
|
23814
24105
|
return { token: out.token, amount: out.amount, to: move.vault, tokenIn: back.token, amountIn: back.amount };
|
|
@@ -24229,8 +24520,8 @@ var MemoryLimitOrderStore = class {
|
|
|
24229
24520
|
async reserve(id, amount) {
|
|
24230
24521
|
const order = this.orders.get(id);
|
|
24231
24522
|
if (!order || order.status !== "open") return false;
|
|
24523
|
+
if (BigInt(amount) > BigInt(order.remaining)) return false;
|
|
24232
24524
|
const reserved = BigInt(order.reserved) + BigInt(amount);
|
|
24233
|
-
if (reserved > BigInt(order.remaining)) return false;
|
|
24234
24525
|
this.patch(id, { reserved: reserved.toString() });
|
|
24235
24526
|
return true;
|
|
24236
24527
|
}
|
|
@@ -24240,6 +24531,14 @@ var MemoryLimitOrderStore = class {
|
|
|
24240
24531
|
const remaining = BigInt(order.remaining) - BigInt(amount);
|
|
24241
24532
|
return this.patch(id, { remaining: (remaining > 0n ? remaining : 0n).toString() });
|
|
24242
24533
|
}
|
|
24534
|
+
async clampRemaining(id, room) {
|
|
24535
|
+
const order = this.orders.get(id);
|
|
24536
|
+
if (!order || order.status !== "open") return null;
|
|
24537
|
+
const sum = BigInt(room) + BigInt(order.reserved);
|
|
24538
|
+
const target = sum > 0n ? sum : 0n;
|
|
24539
|
+
if (target >= BigInt(order.remaining)) return null;
|
|
24540
|
+
return this.patch(id, { remaining: target.toString() });
|
|
24541
|
+
}
|
|
24243
24542
|
async release(id, amount) {
|
|
24244
24543
|
const order = this.orders.get(id);
|
|
24245
24544
|
if (!order) return;
|
|
@@ -24748,7 +25047,8 @@ var OrderScanner = class _OrderScanner {
|
|
|
24748
25047
|
* back from its own endpoints, which also proves they answer for one chain
|
|
24749
25048
|
* before a filler ever depends on them.
|
|
24750
25049
|
*
|
|
24751
|
-
* @throws if an endpoint set is invalid
|
|
25050
|
+
* @throws if an endpoint set is invalid, if none of a chain's endpoints answer,
|
|
25051
|
+
* if its endpoints disagree on the chain, if two entries resolve
|
|
24752
25052
|
* to the same chain, or if `scanIntervalSecs` is below the minimum.
|
|
24753
25053
|
*/
|
|
24754
25054
|
static async create(options) {
|
|
@@ -24808,7 +25108,10 @@ var OrderScanner = class _OrderScanner {
|
|
|
24808
25108
|
async addChainNow(chain) {
|
|
24809
25109
|
if (this.closed) throw new Error("This OrderScanner is closed");
|
|
24810
25110
|
const rpcUrls = validateRpcUrls(chain.rpcUrls);
|
|
24811
|
-
const chainId = chain.chainId ?? await
|
|
25111
|
+
const chainId = chain.chainId ?? (await resolveChainConfigs([{ rpcUrls, bundlerUrl: chain.bundlerUrl ?? "" }], {
|
|
25112
|
+
loggers: this.loggers,
|
|
25113
|
+
tolerateUnreachable: true
|
|
25114
|
+
}))[0].chainId;
|
|
24812
25115
|
if (this.closed) throw new Error("This OrderScanner is closed");
|
|
24813
25116
|
if (this.scanners.has(chainId)) {
|
|
24814
25117
|
throw new Error(`Chain ${chainId} is already in this scanner`);
|
|
@@ -24908,7 +25211,7 @@ var LimitOrderController = class {
|
|
|
24908
25211
|
get service() {
|
|
24909
25212
|
const service = this.runtime.limitOrders;
|
|
24910
25213
|
if (!service) {
|
|
24911
|
-
throw new Error("No orderbook is configured \u2014 set [orderbook]
|
|
25214
|
+
throw new Error("No orderbook is configured \u2014 set [orderbook] url to use limit orders");
|
|
24912
25215
|
}
|
|
24913
25216
|
return service;
|
|
24914
25217
|
}
|
|
@@ -24982,10 +25285,7 @@ var PairController = class {
|
|
|
24982
25285
|
* the pair's symbols have to resolve before the market can be hydrated.
|
|
24983
25286
|
*/
|
|
24984
25287
|
async add(pair, options) {
|
|
24985
|
-
const { engine,
|
|
24986
|
-
if (!engine || !tradingPairs) {
|
|
24987
|
-
throw new Error("No trading engine is running \u2014 this filler was started without pairs");
|
|
24988
|
-
}
|
|
25288
|
+
const { engine, assetRegistry, adminStrategies, balanceTokens, config } = this.runtime;
|
|
24989
25289
|
const assets = options?.assets;
|
|
24990
25290
|
const nextAssets = { ...config.assets ?? {}, ...assets ?? {} };
|
|
24991
25291
|
const nextPairs = [...this.pairs, pair];
|
|
@@ -25024,7 +25324,6 @@ var PairController = class {
|
|
|
25024
25324
|
/** Closes a market. Orders already in flight against it still complete. */
|
|
25025
25325
|
async remove(index) {
|
|
25026
25326
|
const { engine, tradingPairs, adminStrategies, config } = this.runtime;
|
|
25027
|
-
if (!engine || !tradingPairs) throw new Error("No trading engine is running");
|
|
25028
25327
|
if (index < 0 || index >= this.pairs.length) throw new Error(`Unknown pair ${index}`);
|
|
25029
25328
|
engine.removePair(tradingPairs[index]);
|
|
25030
25329
|
config.pairs = this.pairs.filter((_, i) => i !== index);
|
|
@@ -25036,7 +25335,7 @@ var PairController = class {
|
|
|
25036
25335
|
await this.persist();
|
|
25037
25336
|
}
|
|
25038
25337
|
livePair(index) {
|
|
25039
|
-
const pair = this.runtime.tradingPairs
|
|
25338
|
+
const pair = this.runtime.tradingPairs[index];
|
|
25040
25339
|
if (!pair) throw new Error(`Unknown pair ${index}`);
|
|
25041
25340
|
return pair;
|
|
25042
25341
|
}
|
|
@@ -25128,9 +25427,9 @@ var ChainController = class {
|
|
|
25128
25427
|
throw error;
|
|
25129
25428
|
}
|
|
25130
25429
|
const curve = confirmationPolicies[String(chainId)];
|
|
25131
|
-
if (curve) this.runtime.confirmationPolicy
|
|
25132
|
-
else if (!this.runtime.confirmationPolicy
|
|
25133
|
-
this.runtime.confirmationPolicy
|
|
25430
|
+
if (curve) this.runtime.confirmationPolicy.add(chainId, curve);
|
|
25431
|
+
else if (!this.runtime.confirmationPolicy.has(chainId)) {
|
|
25432
|
+
this.runtime.confirmationPolicy.adopt(chainId, resolvedPolicy);
|
|
25134
25433
|
}
|
|
25135
25434
|
this.runtime.resolvedChains.push(resolved);
|
|
25136
25435
|
config.chains = [...config.chains, { rpcUrls: chain.rpcUrls, bundlerUrl: chain.bundlerUrl }];
|
|
@@ -25158,7 +25457,7 @@ var ChainController = class {
|
|
|
25158
25457
|
await intentFiller.removeChain(chainId);
|
|
25159
25458
|
if (this.ownsScanner) await this.scanner.removeChain(chainId);
|
|
25160
25459
|
configService.removeChain(chainId);
|
|
25161
|
-
this.runtime.confirmationPolicy
|
|
25460
|
+
this.runtime.confirmationPolicy.remove(chainId);
|
|
25162
25461
|
this.runtime.chainClientManager.invalidate(chainKey);
|
|
25163
25462
|
this.runtime.resolvedChains.splice(index, 1);
|
|
25164
25463
|
config.chains = config.chains.filter((_, i) => i !== index);
|
|
@@ -25377,7 +25676,7 @@ var Simplex = class _Simplex extends import_node_events3.EventEmitter {
|
|
|
25377
25676
|
const tomlSigner = options.config.simplex?.signer;
|
|
25378
25677
|
if (tomlSigner && !options.signer) {
|
|
25379
25678
|
throw new Error(
|
|
25380
|
-
"config.simplex.signer describes a signer but none was passed. Simplex.start takes a Signer instance: `signer: await createSigner(config.simplex.signer)`, or build one directly with privateKeySigner/turnkeySigner/mpcVaultSigner/viemSigner."
|
|
25679
|
+
"config.simplex.signer describes a signer but none was passed. Simplex.start takes a Signer instance: `signer: await createSigner(config.simplex.signer)`, or build one directly with privateKeySigner/secretPhraseSigner/turnkeySigner/mpcVaultSigner/viemSigner."
|
|
25381
25680
|
);
|
|
25382
25681
|
}
|
|
25383
25682
|
const loggers = new LoggerContext({
|
|
@@ -25581,6 +25880,8 @@ var Simplex = class _Simplex extends import_node_events3.EventEmitter {
|
|
|
25581
25880
|
0 && (module.exports = {
|
|
25582
25881
|
AssetController,
|
|
25583
25882
|
ChainController,
|
|
25883
|
+
LimitOrderController,
|
|
25884
|
+
LimitOrderValidationError,
|
|
25584
25885
|
LoggerContext,
|
|
25585
25886
|
MemoryDataStore,
|
|
25586
25887
|
OrderScanner,
|
|
@@ -25593,6 +25894,7 @@ var Simplex = class _Simplex extends import_node_events3.EventEmitter {
|
|
|
25593
25894
|
WalletController,
|
|
25594
25895
|
addLogSink,
|
|
25595
25896
|
assertConfirmationCoverage,
|
|
25897
|
+
canDerive,
|
|
25596
25898
|
configureLogger,
|
|
25597
25899
|
createSigner,
|
|
25598
25900
|
digestSigner,
|
|
@@ -25605,10 +25907,12 @@ var Simplex = class _Simplex extends import_node_events3.EventEmitter {
|
|
|
25605
25907
|
pickAnchorStable,
|
|
25606
25908
|
privateKeySigner,
|
|
25607
25909
|
registrySymbols,
|
|
25910
|
+
secretPhraseSigner,
|
|
25608
25911
|
turnkeySigner,
|
|
25609
25912
|
validateAssetDefinitions,
|
|
25610
25913
|
validateConfig,
|
|
25611
25914
|
validatePairConfigs,
|
|
25915
|
+
validateSecretPhrase,
|
|
25612
25916
|
validateSignerConfig,
|
|
25613
25917
|
validateVaultToml,
|
|
25614
25918
|
viemSigner
|