@hyperbridge/simplex 0.16.11 → 0.16.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/simplex.js +168 -34
- package/dist/bin/simplex.js.map +1 -1
- package/dist/index.cjs +120 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -1
- package/dist/index.d.ts +46 -1
- package/dist/index.js +120 -16
- package/dist/index.js.map +1 -1
- package/dist/ui/assets/{index-CD2NZXpG.js → index-QU1Ik7IX.js} +20 -20
- package/dist/ui/assets/{index-B_p2oQkc.css → index-XKHMOpY2.css} +1 -1
- package/dist/ui/index.html +2 -2
- package/package.json +4 -3
package/dist/index.d.cts
CHANGED
|
@@ -158,6 +158,11 @@ interface FillerConfig {
|
|
|
158
158
|
* See `bidValiditySeconds` in filler-toml for why this bound matters.
|
|
159
159
|
*/
|
|
160
160
|
bidValiditySeconds?: number;
|
|
161
|
+
/**
|
|
162
|
+
* Order size in USD below which `order.fees` must cover the execution cost.
|
|
163
|
+
* Defaults to 20. See `minOrderSizeUsd` in filler-toml.
|
|
164
|
+
*/
|
|
165
|
+
minOrderSizeUsd?: number;
|
|
161
166
|
/**
|
|
162
167
|
* Overfill protection knobs. If omitted, defaults are used
|
|
163
168
|
* (maxOverfillBps=500, maxConsecutiveClamps=3).
|
|
@@ -343,6 +348,11 @@ declare class FillerConfigService {
|
|
|
343
348
|
* Hyperbridge-side retraction does not reach the destination chain.
|
|
344
349
|
*/
|
|
345
350
|
getBidValiditySeconds(): number;
|
|
351
|
+
/**
|
|
352
|
+
* The order size, in USD, below which `order.fees` must cover the execution cost.
|
|
353
|
+
* Defaults to 20. An order at or above it is filled whatever fees it carries.
|
|
354
|
+
*/
|
|
355
|
+
getMinOrderSizeUsd(): number;
|
|
346
356
|
/** Ceiling bps above user-requested output. Default 500 (5%). */
|
|
347
357
|
getMaxOverfillBps(): bigint;
|
|
348
358
|
/** Consecutive clamped evaluations before the strategy halts. Default 3. */
|
|
@@ -1822,6 +1832,16 @@ interface FillerTomlConfig {
|
|
|
1822
1832
|
* Configured in seconds but written on-chain in blocks, converted per destination chain.
|
|
1823
1833
|
*/
|
|
1824
1834
|
bidValiditySeconds?: number;
|
|
1835
|
+
/**
|
|
1836
|
+
* The order size, in USD, below which `order.fees` must cover the execution cost
|
|
1837
|
+
* (fill gas plus, cross-chain, the relayer fee). Defaults to 20.
|
|
1838
|
+
*
|
|
1839
|
+
* An order worth at least this much is filled whatever fees it carries: the margin
|
|
1840
|
+
* in the limit order that prices it pays for the gas. On a smaller order that
|
|
1841
|
+
* margin is too thin to, so the fees have to. Set to 0 to never check fees. An
|
|
1842
|
+
* order nothing can put a dollar figure on is treated as below it.
|
|
1843
|
+
*/
|
|
1844
|
+
minOrderSizeUsd?: number;
|
|
1825
1845
|
/**
|
|
1826
1846
|
* Remote access: an outbound SSH tunnel to a rendezvous relay so a phone's
|
|
1827
1847
|
* SSH client can reach the local web UI. Off unless `enabled = true`; the
|
|
@@ -2113,6 +2133,17 @@ declare class CacheService {
|
|
|
2113
2133
|
isPartialFill(orderId: string): boolean;
|
|
2114
2134
|
setPartialFill(orderId: string, partial: boolean): void;
|
|
2115
2135
|
clearPartialFill(orderId: string): void;
|
|
2136
|
+
/**
|
|
2137
|
+
* Whether the strategy's completed evaluation let a bid through without holding
|
|
2138
|
+
* `order.fees` to the execution cost, because the order is at or above the
|
|
2139
|
+
* operator's minimum size.
|
|
2140
|
+
*
|
|
2141
|
+
* Written and cleared the way {@link isPartialFill} is, for the same reason: the
|
|
2142
|
+
* caller exempts such an order from its profit floor.
|
|
2143
|
+
*/
|
|
2144
|
+
isFeeCheckWaived(orderId: string): boolean;
|
|
2145
|
+
setFeeCheckWaived(orderId: string, waived: boolean): void;
|
|
2146
|
+
clearFeeCheckWaived(orderId: string): void;
|
|
2116
2147
|
getFeeTokenWithDecimals(chain: string): {
|
|
2117
2148
|
address: HexString;
|
|
2118
2149
|
decimals: number;
|
|
@@ -3320,6 +3351,13 @@ declare class IntentFiller {
|
|
|
3320
3351
|
private verifyOrderOnSource;
|
|
3321
3352
|
private handleNewOrder;
|
|
3322
3353
|
private evaluateOrder;
|
|
3354
|
+
/**
|
|
3355
|
+
* The destination chain's latest block number, the clock `order.deadline` is read on.
|
|
3356
|
+
*
|
|
3357
|
+
* Null when the read fails. That is not an answer about the order, so evaluation goes
|
|
3358
|
+
* on: the gas estimate simulates the same deadline check and refuses an expired order.
|
|
3359
|
+
*/
|
|
3360
|
+
private destinationHead;
|
|
3323
3361
|
/**
|
|
3324
3362
|
* Whether the strategy's evaluation concluded in a deliberate partial fill.
|
|
3325
3363
|
*
|
|
@@ -3330,6 +3368,12 @@ declare class IntentFiller {
|
|
|
3330
3368
|
* and filled them.
|
|
3331
3369
|
*/
|
|
3332
3370
|
private fillsPartially;
|
|
3371
|
+
/**
|
|
3372
|
+
* Whether the strategy's evaluation let a bid through without the fee check, because
|
|
3373
|
+
* the order is at or above the operator's minimum size. Read from a flag the strategy
|
|
3374
|
+
* sets only once it has an answer, as {@link fillsPartially} is.
|
|
3375
|
+
*/
|
|
3376
|
+
private feeCheckWaived;
|
|
3333
3377
|
private executeOrder;
|
|
3334
3378
|
private handleOrderFilledOnChain;
|
|
3335
3379
|
/**
|
|
@@ -3521,7 +3565,8 @@ declare class FXFiller implements FillerStrategy {
|
|
|
3521
3565
|
* - Treat a payout below the ask as a partial fill, which only orders
|
|
3522
3566
|
* without output calldata and without an earlier partial fill allow.
|
|
3523
3567
|
* - Cap each payout by the filler's balance plus funding-venue withdrawals,
|
|
3524
|
-
* then gate it on fees (full fills) and the
|
|
3568
|
+
* then gate it on fees (full fills of orders below `minOrderSizeUsd`) and the
|
|
3569
|
+
* same-asset spread.
|
|
3525
3570
|
* - Cache the resulting bids for later use in `executeOrder`.
|
|
3526
3571
|
*/
|
|
3527
3572
|
calculateProfitability(order: Order): Promise<number>;
|
package/dist/index.d.ts
CHANGED
|
@@ -158,6 +158,11 @@ interface FillerConfig {
|
|
|
158
158
|
* See `bidValiditySeconds` in filler-toml for why this bound matters.
|
|
159
159
|
*/
|
|
160
160
|
bidValiditySeconds?: number;
|
|
161
|
+
/**
|
|
162
|
+
* Order size in USD below which `order.fees` must cover the execution cost.
|
|
163
|
+
* Defaults to 20. See `minOrderSizeUsd` in filler-toml.
|
|
164
|
+
*/
|
|
165
|
+
minOrderSizeUsd?: number;
|
|
161
166
|
/**
|
|
162
167
|
* Overfill protection knobs. If omitted, defaults are used
|
|
163
168
|
* (maxOverfillBps=500, maxConsecutiveClamps=3).
|
|
@@ -343,6 +348,11 @@ declare class FillerConfigService {
|
|
|
343
348
|
* Hyperbridge-side retraction does not reach the destination chain.
|
|
344
349
|
*/
|
|
345
350
|
getBidValiditySeconds(): number;
|
|
351
|
+
/**
|
|
352
|
+
* The order size, in USD, below which `order.fees` must cover the execution cost.
|
|
353
|
+
* Defaults to 20. An order at or above it is filled whatever fees it carries.
|
|
354
|
+
*/
|
|
355
|
+
getMinOrderSizeUsd(): number;
|
|
346
356
|
/** Ceiling bps above user-requested output. Default 500 (5%). */
|
|
347
357
|
getMaxOverfillBps(): bigint;
|
|
348
358
|
/** Consecutive clamped evaluations before the strategy halts. Default 3. */
|
|
@@ -1822,6 +1832,16 @@ interface FillerTomlConfig {
|
|
|
1822
1832
|
* Configured in seconds but written on-chain in blocks, converted per destination chain.
|
|
1823
1833
|
*/
|
|
1824
1834
|
bidValiditySeconds?: number;
|
|
1835
|
+
/**
|
|
1836
|
+
* The order size, in USD, below which `order.fees` must cover the execution cost
|
|
1837
|
+
* (fill gas plus, cross-chain, the relayer fee). Defaults to 20.
|
|
1838
|
+
*
|
|
1839
|
+
* An order worth at least this much is filled whatever fees it carries: the margin
|
|
1840
|
+
* in the limit order that prices it pays for the gas. On a smaller order that
|
|
1841
|
+
* margin is too thin to, so the fees have to. Set to 0 to never check fees. An
|
|
1842
|
+
* order nothing can put a dollar figure on is treated as below it.
|
|
1843
|
+
*/
|
|
1844
|
+
minOrderSizeUsd?: number;
|
|
1825
1845
|
/**
|
|
1826
1846
|
* Remote access: an outbound SSH tunnel to a rendezvous relay so a phone's
|
|
1827
1847
|
* SSH client can reach the local web UI. Off unless `enabled = true`; the
|
|
@@ -2113,6 +2133,17 @@ declare class CacheService {
|
|
|
2113
2133
|
isPartialFill(orderId: string): boolean;
|
|
2114
2134
|
setPartialFill(orderId: string, partial: boolean): void;
|
|
2115
2135
|
clearPartialFill(orderId: string): void;
|
|
2136
|
+
/**
|
|
2137
|
+
* Whether the strategy's completed evaluation let a bid through without holding
|
|
2138
|
+
* `order.fees` to the execution cost, because the order is at or above the
|
|
2139
|
+
* operator's minimum size.
|
|
2140
|
+
*
|
|
2141
|
+
* Written and cleared the way {@link isPartialFill} is, for the same reason: the
|
|
2142
|
+
* caller exempts such an order from its profit floor.
|
|
2143
|
+
*/
|
|
2144
|
+
isFeeCheckWaived(orderId: string): boolean;
|
|
2145
|
+
setFeeCheckWaived(orderId: string, waived: boolean): void;
|
|
2146
|
+
clearFeeCheckWaived(orderId: string): void;
|
|
2116
2147
|
getFeeTokenWithDecimals(chain: string): {
|
|
2117
2148
|
address: HexString;
|
|
2118
2149
|
decimals: number;
|
|
@@ -3320,6 +3351,13 @@ declare class IntentFiller {
|
|
|
3320
3351
|
private verifyOrderOnSource;
|
|
3321
3352
|
private handleNewOrder;
|
|
3322
3353
|
private evaluateOrder;
|
|
3354
|
+
/**
|
|
3355
|
+
* The destination chain's latest block number, the clock `order.deadline` is read on.
|
|
3356
|
+
*
|
|
3357
|
+
* Null when the read fails. That is not an answer about the order, so evaluation goes
|
|
3358
|
+
* on: the gas estimate simulates the same deadline check and refuses an expired order.
|
|
3359
|
+
*/
|
|
3360
|
+
private destinationHead;
|
|
3323
3361
|
/**
|
|
3324
3362
|
* Whether the strategy's evaluation concluded in a deliberate partial fill.
|
|
3325
3363
|
*
|
|
@@ -3330,6 +3368,12 @@ declare class IntentFiller {
|
|
|
3330
3368
|
* and filled them.
|
|
3331
3369
|
*/
|
|
3332
3370
|
private fillsPartially;
|
|
3371
|
+
/**
|
|
3372
|
+
* Whether the strategy's evaluation let a bid through without the fee check, because
|
|
3373
|
+
* the order is at or above the operator's minimum size. Read from a flag the strategy
|
|
3374
|
+
* sets only once it has an answer, as {@link fillsPartially} is.
|
|
3375
|
+
*/
|
|
3376
|
+
private feeCheckWaived;
|
|
3333
3377
|
private executeOrder;
|
|
3334
3378
|
private handleOrderFilledOnChain;
|
|
3335
3379
|
/**
|
|
@@ -3521,7 +3565,8 @@ declare class FXFiller implements FillerStrategy {
|
|
|
3521
3565
|
* - Treat a payout below the ask as a partial fill, which only orders
|
|
3522
3566
|
* without output calldata and without an earlier partial fill allow.
|
|
3523
3567
|
* - Cap each payout by the filler's balance plus funding-venue withdrawals,
|
|
3524
|
-
* then gate it on fees (full fills) and the
|
|
3568
|
+
* then gate it on fees (full fills of orders below `minOrderSizeUsd`) and the
|
|
3569
|
+
* same-asset spread.
|
|
3525
3570
|
* - Cache the resulting bids for later use in `executeOrder`.
|
|
3526
3571
|
*/
|
|
3527
3572
|
calculateProfitability(order: Order): Promise<number>;
|
package/dist/index.js
CHANGED
|
@@ -1103,6 +1103,7 @@ import { isAddress as isAddress2 } from "viem";
|
|
|
1103
1103
|
import { ChainConfigService, bytes32ToBytes20 } from "@hyperbridge/sdk";
|
|
1104
1104
|
var DEFAULT_BLOCK_SCAN_INTERVAL_SECONDS = 3;
|
|
1105
1105
|
var MIN_BLOCK_SCAN_INTERVAL_SECONDS = 0.1;
|
|
1106
|
+
var DEFAULT_MIN_ORDER_SIZE_USD = 20;
|
|
1106
1107
|
function validateRpcUrls(rpcUrls) {
|
|
1107
1108
|
if (rpcUrls.length === 0) {
|
|
1108
1109
|
throw new Error("rpcUrls must contain at least one URL");
|
|
@@ -1525,6 +1526,13 @@ var FillerConfigService = class {
|
|
|
1525
1526
|
getBidValiditySeconds() {
|
|
1526
1527
|
return this.fillerConfig?.bidValiditySeconds ?? 300;
|
|
1527
1528
|
}
|
|
1529
|
+
/**
|
|
1530
|
+
* The order size, in USD, below which `order.fees` must cover the execution cost.
|
|
1531
|
+
* Defaults to 20. An order at or above it is filled whatever fees it carries.
|
|
1532
|
+
*/
|
|
1533
|
+
getMinOrderSizeUsd() {
|
|
1534
|
+
return this.fillerConfig?.minOrderSizeUsd ?? DEFAULT_MIN_ORDER_SIZE_USD;
|
|
1535
|
+
}
|
|
1528
1536
|
/** Ceiling bps above user-requested output. Default 500 (5%). */
|
|
1529
1537
|
getMaxOverfillBps() {
|
|
1530
1538
|
return BigInt(this.fillerConfig?.overfillProtection?.maxOverfillBps ?? 500);
|
|
@@ -1628,6 +1636,12 @@ function validateConfig(config, cliWatchOnly = false) {
|
|
|
1628
1636
|
);
|
|
1629
1637
|
}
|
|
1630
1638
|
}
|
|
1639
|
+
const minOrderSize = config.simplex.minOrderSizeUsd;
|
|
1640
|
+
if (minOrderSize !== void 0) {
|
|
1641
|
+
if (!Number.isFinite(minOrderSize) || minOrderSize < 0) {
|
|
1642
|
+
throw new Error(`simplex.minOrderSizeUsd must be a number >= 0 (USD); got ${minOrderSize}`);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1631
1645
|
if (config.allowlist) {
|
|
1632
1646
|
for (const user of config.allowlist.users ?? []) {
|
|
1633
1647
|
if (!isAddress2(user)) {
|
|
@@ -12806,6 +12820,7 @@ var CacheService = class {
|
|
|
12806
12820
|
matchedLimitOrders: {},
|
|
12807
12821
|
bidPlans: {},
|
|
12808
12822
|
partialFills: {},
|
|
12823
|
+
feeChecksWaived: {},
|
|
12809
12824
|
feeTokens: {},
|
|
12810
12825
|
tokenDecimals: {},
|
|
12811
12826
|
solverSelection: {}
|
|
@@ -12845,6 +12860,10 @@ var CacheService = class {
|
|
|
12845
12860
|
stalePartialIds.forEach((orderId) => {
|
|
12846
12861
|
delete this.cacheData.partialFills[orderId];
|
|
12847
12862
|
});
|
|
12863
|
+
const staleWaivedIds = Object.entries(this.cacheData.feeChecksWaived).filter(([_, data]) => !this.isCacheValid(data.timestamp)).map(([orderId]) => orderId);
|
|
12864
|
+
staleWaivedIds.forEach((orderId) => {
|
|
12865
|
+
delete this.cacheData.feeChecksWaived[orderId];
|
|
12866
|
+
});
|
|
12848
12867
|
}
|
|
12849
12868
|
getGasEstimate(orderId) {
|
|
12850
12869
|
try {
|
|
@@ -13157,6 +13176,24 @@ var CacheService = class {
|
|
|
13157
13176
|
clearPartialFill(orderId) {
|
|
13158
13177
|
delete this.cacheData.partialFills[orderId];
|
|
13159
13178
|
}
|
|
13179
|
+
/**
|
|
13180
|
+
* Whether the strategy's completed evaluation let a bid through without holding
|
|
13181
|
+
* `order.fees` to the execution cost, because the order is at or above the
|
|
13182
|
+
* operator's minimum size.
|
|
13183
|
+
*
|
|
13184
|
+
* Written and cleared the way {@link isPartialFill} is, for the same reason: the
|
|
13185
|
+
* caller exempts such an order from its profit floor.
|
|
13186
|
+
*/
|
|
13187
|
+
isFeeCheckWaived(orderId) {
|
|
13188
|
+
const cache = this.cacheData.feeChecksWaived[orderId];
|
|
13189
|
+
return cache !== void 0 && this.isCacheValid(cache.timestamp) && cache.waived;
|
|
13190
|
+
}
|
|
13191
|
+
setFeeCheckWaived(orderId, waived) {
|
|
13192
|
+
this.cacheData.feeChecksWaived[orderId] = { waived, timestamp: Date.now() };
|
|
13193
|
+
}
|
|
13194
|
+
clearFeeCheckWaived(orderId) {
|
|
13195
|
+
delete this.cacheData.feeChecksWaived[orderId];
|
|
13196
|
+
}
|
|
13160
13197
|
getFeeTokenWithDecimals(chain) {
|
|
13161
13198
|
try {
|
|
13162
13199
|
const cache = this.cacheData.feeTokens[chain];
|
|
@@ -18484,7 +18521,10 @@ var ContractInteractionService = class {
|
|
|
18484
18521
|
order,
|
|
18485
18522
|
prependCalls: void 0,
|
|
18486
18523
|
maxPriorityFeePerGasBumpPercent: gasFeeBumpConfig?.maxPriorityFeePerGasBumpPercent,
|
|
18487
|
-
maxFeePerGasBumpPercent: gasFeeBumpConfig?.maxFeePerGasBumpPercent
|
|
18524
|
+
maxFeePerGasBumpPercent: gasFeeBumpConfig?.maxFeePerGasBumpPercent,
|
|
18525
|
+
// No bid without the bundler's own figures: an op signed with the SDK's
|
|
18526
|
+
// fixed fallback limits does not execute, so a failed estimate skips the order.
|
|
18527
|
+
requireBundlerEstimate: true
|
|
18488
18528
|
});
|
|
18489
18529
|
this.logger.info({ orderId: order.id }, "Caching gas estimate");
|
|
18490
18530
|
this.logger.info({ estimate }, "Estimate");
|
|
@@ -18507,7 +18547,7 @@ var ContractInteractionService = class {
|
|
|
18507
18547
|
callGasLimit: estimate.callGasLimit
|
|
18508
18548
|
};
|
|
18509
18549
|
} catch (error) {
|
|
18510
|
-
this.logger.error({ err: error }, "Error estimating gas,
|
|
18550
|
+
this.logger.error({ err: error, orderId: order.id }, "Error estimating gas, not bidding on this order");
|
|
18511
18551
|
throw new Error(`Failed to estimate gas: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
18512
18552
|
}
|
|
18513
18553
|
}
|
|
@@ -21675,6 +21715,23 @@ var IntentFiller = class {
|
|
|
21675
21715
|
this.monitor.emit("orderSkipped", { orderId: order.id, reason: "watch-only" });
|
|
21676
21716
|
return null;
|
|
21677
21717
|
}
|
|
21718
|
+
const destinationHead = await this.destinationHead(order);
|
|
21719
|
+
if (destinationHead !== null && order.deadline <= destinationHead) {
|
|
21720
|
+
this.logger.info(
|
|
21721
|
+
{
|
|
21722
|
+
orderId: order.id,
|
|
21723
|
+
destChain: order.destination,
|
|
21724
|
+
deadline: order.deadline.toString(),
|
|
21725
|
+
head: destinationHead.toString()
|
|
21726
|
+
},
|
|
21727
|
+
"Skipping order: its deadline has passed on the destination chain"
|
|
21728
|
+
);
|
|
21729
|
+
this.monitor.emit("orderSkipped", {
|
|
21730
|
+
orderId: order.id,
|
|
21731
|
+
reason: "Order deadline has passed on the destination chain"
|
|
21732
|
+
});
|
|
21733
|
+
return null;
|
|
21734
|
+
}
|
|
21678
21735
|
const evalStartMs = Date.now();
|
|
21679
21736
|
const eligibleStrategies = await Promise.all(
|
|
21680
21737
|
this.strategies.map(async (strategy) => {
|
|
@@ -21684,7 +21741,10 @@ var IntentFiller = class {
|
|
|
21684
21741
|
})
|
|
21685
21742
|
);
|
|
21686
21743
|
const fillsPartially = this.fillsPartially(order);
|
|
21687
|
-
const
|
|
21744
|
+
const feeCheckWaived = this.feeCheckWaived(order);
|
|
21745
|
+
const validStrategies = eligibleStrategies.filter(
|
|
21746
|
+
(s) => s !== null && (s.profitability > 0 || fillsPartially || feeCheckWaived)
|
|
21747
|
+
).sort((a, b) => b.profitability - a.profitability);
|
|
21688
21748
|
const evalDurationSec = (Date.now() - evalStartMs) / 1e3;
|
|
21689
21749
|
this.monitor.emit("orderTiming", { orderId: order.id, phase: "evaluation", durationSec: evalDurationSec });
|
|
21690
21750
|
if (validStrategies.length === 0) {
|
|
@@ -21709,6 +21769,23 @@ var IntentFiller = class {
|
|
|
21709
21769
|
);
|
|
21710
21770
|
return validStrategies[0];
|
|
21711
21771
|
}
|
|
21772
|
+
/**
|
|
21773
|
+
* The destination chain's latest block number, the clock `order.deadline` is read on.
|
|
21774
|
+
*
|
|
21775
|
+
* Null when the read fails. That is not an answer about the order, so evaluation goes
|
|
21776
|
+
* on: the gas estimate simulates the same deadline check and refuses an expired order.
|
|
21777
|
+
*/
|
|
21778
|
+
async destinationHead(order) {
|
|
21779
|
+
try {
|
|
21780
|
+
return await this.chainClientManager.getPublicClient(order.destination).getBlockNumber();
|
|
21781
|
+
} catch (err) {
|
|
21782
|
+
this.logger.warn(
|
|
21783
|
+
{ orderId: order.id, destChain: order.destination, err },
|
|
21784
|
+
"Could not read the destination head to check the order's deadline"
|
|
21785
|
+
);
|
|
21786
|
+
return null;
|
|
21787
|
+
}
|
|
21788
|
+
}
|
|
21712
21789
|
/**
|
|
21713
21790
|
* Whether the strategy's evaluation concluded in a deliberate partial fill.
|
|
21714
21791
|
*
|
|
@@ -21722,6 +21799,15 @@ var IntentFiller = class {
|
|
|
21722
21799
|
if (!order.id) return false;
|
|
21723
21800
|
return this.contractService.cacheService.isPartialFill(order.id);
|
|
21724
21801
|
}
|
|
21802
|
+
/**
|
|
21803
|
+
* Whether the strategy's evaluation let a bid through without the fee check, because
|
|
21804
|
+
* the order is at or above the operator's minimum size. Read from a flag the strategy
|
|
21805
|
+
* sets only once it has an answer, as {@link fillsPartially} is.
|
|
21806
|
+
*/
|
|
21807
|
+
feeCheckWaived(order) {
|
|
21808
|
+
if (!order.id) return false;
|
|
21809
|
+
return this.contractService.cacheService.isFeeCheckWaived(order.id);
|
|
21810
|
+
}
|
|
21725
21811
|
executeOrder(order, bestStrategy, solverSelectionActive, inputUsdValue, profitUsd) {
|
|
21726
21812
|
const chainQueue = this.chainQueues.get(getChainId3(order.destination));
|
|
21727
21813
|
if (!chainQueue) {
|
|
@@ -22296,11 +22382,13 @@ var FXFiller = class _FXFiller {
|
|
|
22296
22382
|
* - Treat a payout below the ask as a partial fill, which only orders
|
|
22297
22383
|
* without output calldata and without an earlier partial fill allow.
|
|
22298
22384
|
* - Cap each payout by the filler's balance plus funding-venue withdrawals,
|
|
22299
|
-
* then gate it on fees (full fills) and the
|
|
22385
|
+
* then gate it on fees (full fills of orders below `minOrderSizeUsd`) and the
|
|
22386
|
+
* same-asset spread.
|
|
22300
22387
|
* - Cache the resulting bids for later use in `executeOrder`.
|
|
22301
22388
|
*/
|
|
22302
22389
|
async calculateProfitability(order) {
|
|
22303
22390
|
if (order.id) this.contractService.cacheService.clearPartialFill(order.id);
|
|
22391
|
+
if (order.id) this.contractService.cacheService.clearFeeCheckWaived(order.id);
|
|
22304
22392
|
if (this.halted) {
|
|
22305
22393
|
this.logger.warn({ orderId: order.id }, "FXFiller halted \u2014 rejecting order");
|
|
22306
22394
|
return 0;
|
|
@@ -22316,6 +22404,15 @@ var FXFiller = class _FXFiller {
|
|
|
22316
22404
|
const plans = [];
|
|
22317
22405
|
const plannedOn = /* @__PURE__ */ new Map();
|
|
22318
22406
|
const spentOn = /* @__PURE__ */ new Map();
|
|
22407
|
+
let atOrAboveMinSize;
|
|
22408
|
+
const feeCheckWaived = async () => {
|
|
22409
|
+
if (atOrAboveMinSize === void 0) {
|
|
22410
|
+
const usd = await this.getOrderUsdValue(order);
|
|
22411
|
+
atOrAboveMinSize = usd !== null && usd.inputUsd.gte(this.configService.getMinOrderSizeUsd());
|
|
22412
|
+
}
|
|
22413
|
+
return atOrAboveMinSize;
|
|
22414
|
+
};
|
|
22415
|
+
let feesWaived = false;
|
|
22319
22416
|
for (let leg = 0; leg < order.inputs.length; leg++) {
|
|
22320
22417
|
const input = order.inputs[leg];
|
|
22321
22418
|
const output = order.output.assets[leg];
|
|
@@ -22559,17 +22656,21 @@ var FXFiller = class _FXFiller {
|
|
|
22559
22656
|
const executionCost = totalCostInSourceFeeToken + relayerFeeInSourceFeeToken;
|
|
22560
22657
|
const partialEdgeUsd = sameAssetEdgeUsd.plus(payoutSurplusUsd);
|
|
22561
22658
|
if (!partialFill && order.fees < executionCost) {
|
|
22562
|
-
|
|
22563
|
-
|
|
22564
|
-
|
|
22565
|
-
|
|
22566
|
-
|
|
22567
|
-
|
|
22568
|
-
|
|
22569
|
-
|
|
22570
|
-
|
|
22571
|
-
|
|
22572
|
-
|
|
22659
|
+
if (!await feeCheckWaived()) {
|
|
22660
|
+
this.logger.info(
|
|
22661
|
+
{
|
|
22662
|
+
orderId: order.id,
|
|
22663
|
+
orderFees: formatUnits4(order.fees, feeTokenDecimals),
|
|
22664
|
+
fillGas: formatUnits4(totalCostInSourceFeeToken, feeTokenDecimals),
|
|
22665
|
+
relayerFee: formatUnits4(relayerFeeInSourceFeeToken, feeTokenDecimals),
|
|
22666
|
+
executionCost: formatUnits4(executionCost, feeTokenDecimals),
|
|
22667
|
+
minOrderSizeUsd: this.configService.getMinOrderSizeUsd()
|
|
22668
|
+
},
|
|
22669
|
+
"Skipping order: attached fees do not cover execution cost (fill gas + relayer fee)"
|
|
22670
|
+
);
|
|
22671
|
+
continue;
|
|
22672
|
+
}
|
|
22673
|
+
feesWaived = true;
|
|
22573
22674
|
}
|
|
22574
22675
|
if (sameAsset && !sameAssetProfitable) {
|
|
22575
22676
|
this.logger.info(
|
|
@@ -22603,7 +22704,8 @@ var FXFiller = class _FXFiller {
|
|
|
22603
22704
|
realizedSpreadProfit: formatUnits4(realizedSpreadProfit, feeTokenDecimals),
|
|
22604
22705
|
payoutSurplusUsd: payoutSurplusUsd.toString(),
|
|
22605
22706
|
totalProfit,
|
|
22606
|
-
profitable: totalProfit > 0
|
|
22707
|
+
profitable: totalProfit > 0,
|
|
22708
|
+
feeCheckWaived: !partialFill && order.fees < executionCost
|
|
22607
22709
|
},
|
|
22608
22710
|
"FX swap profitability evaluation"
|
|
22609
22711
|
);
|
|
@@ -22635,6 +22737,7 @@ var FXFiller = class _FXFiller {
|
|
|
22635
22737
|
plans[0].budget
|
|
22636
22738
|
);
|
|
22637
22739
|
this.contractService.cacheService.setPartialFill(order.id, plans[0].partialFill);
|
|
22740
|
+
this.contractService.cacheService.setFeeCheckWaived(order.id, feesWaived);
|
|
22638
22741
|
this.contractService.cacheService.setMatchedLimitOrder(order.id, [
|
|
22639
22742
|
{ limitOrderId: plans[0].limitOrderId, payout: plans[0].payout }
|
|
22640
22743
|
]);
|
|
@@ -23876,6 +23979,7 @@ async function bootFiller(config, options) {
|
|
|
23876
23979
|
rebalancing: config.rebalancing,
|
|
23877
23980
|
targetGasUnits: config.simplex.targetGasUnits,
|
|
23878
23981
|
bidValiditySeconds: config.simplex.bidValiditySeconds,
|
|
23982
|
+
minOrderSizeUsd: config.simplex.minOrderSizeUsd,
|
|
23879
23983
|
blockScanIntervalSeconds: config.simplex.blockScanIntervalSeconds,
|
|
23880
23984
|
gasFeeBump: config.simplex.gasFeeBump,
|
|
23881
23985
|
overfillProtection: config.simplex.overfillProtection,
|