Skip to content

Commit caa550d

Browse files
authored
[simplex]: fix docker build (#728)
1 parent c9b2186 commit caa550d

File tree

11 files changed

+217
-59
lines changed

11 files changed

+217
-59
lines changed

sdk/packages/simplex/scripts/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
FROM node:22
33

44
# Install dependencies required for builds
5-
RUN apt-get update && apt-get install -y python3 make g++ git curl protobuf-compiler --no-install-recommends && rm -rf /var/lib/apt/lists/*
5+
RUN apt-get update && apt-get install -y python3 make g++ git curl protobuf-compiler libprotobuf-dev --no-install-recommends && rm -rf /var/lib/apt/lists/*
66

77
# Create non-root user
88
RUN groupadd -g 1001 simplex && \

sdk/packages/simplex/scripts/docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ services:
3737
command:
3838
- "--config.file=/etc/prometheus/prometheus.yml"
3939
- "--storage.tsdb.retention.time=30d"
40+
- "--web.enable-admin-api"
4041
networks:
4142
- simplex-network
4243
ports:

sdk/packages/simplex/scripts/generate-proto.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,19 @@ rm -rf "$OUT_DIR"
6969
mkdir -p "$OUT_DIR"
7070

7171
echo "Generating TypeScript from proto files..."
72+
# Locate well-known proto includes (wrappers.proto, descriptor.proto, etc.)
73+
WELL_KNOWN_PROTOS=""
74+
for candidate in "$(dirname "$(command -v protoc)" 2>/dev/null)/../include" /usr/include /usr/local/include; do
75+
if [ -f "$candidate/google/protobuf/wrappers.proto" ]; then
76+
WELL_KNOWN_PROTOS="$candidate"
77+
break
78+
fi
79+
done
80+
if [ -z "$WELL_KNOWN_PROTOS" ]; then
81+
echo "Error: Could not find Google well-known proto includes (google/protobuf/wrappers.proto)"
82+
exit 1
83+
fi
84+
7285
protoc \
7386
--plugin="protoc-gen-ts_proto=$PLUGIN" \
7487
--ts_proto_out="$OUT_DIR" \
@@ -78,6 +91,7 @@ protoc \
7891
--ts_proto_opt=useExactTypes=false \
7992
--ts_proto_opt=forceLong=string \
8093
-I="$PROTO_DIR" \
94+
-I="$WELL_KNOWN_PROTOS" \
8195
"$PROTO_DIR"/mpcvault/platform/v1/api.proto \
8296
"$PROTO_DIR"/mpcvault/platform/v1/error.proto
8397

sdk/packages/simplex/scripts/monitoring/grafana/dashboards/simplex.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@
191191
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 12 },
192192
"targets": [
193193
{
194-
"expr": "sum by (chain_id) (increase(simplex_order_volume_usd_total[$__range]))",
194+
"expr": "sum by (chain_id) (simplex_order_volume_usd_total)",
195195
"legendFormat": "chain {{chain_id}}",
196196
"instant": false,
197197
"range": true,
@@ -214,7 +214,7 @@
214214
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 20 },
215215
"targets": [
216216
{
217-
"expr": "sum by (chain_id) (increase(simplex_order_profit_usd_total[$__range]))",
217+
"expr": "sum by (chain_id) (simplex_order_profit_usd_total)",
218218
"legendFormat": "chain {{chain_id}}",
219219
"instant": false,
220220
"range": true,

sdk/packages/simplex/scripts/monitoring/prometheus.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ global:
55
scrape_configs:
66
- job_name: "simplex"
77
static_configs:
8-
- targets: ["simplex:9090"]
8+
- targets: ["hyperbridge-simplex:9090"]

sdk/packages/simplex/src/bin/simplex.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,10 +248,12 @@ interface FillerTomlConfig {
248248
pendingQueue: PendingQueueConfig
249249
logging?: LoggingConfig
250250
watchOnly?: boolean | Record<string, boolean>
251-
substratePrivateKey?: string
252-
hyperbridgeWsUrl?: string
251+
substratePrivateKey: string
252+
hyperbridgeWsUrl: string
253253
entryPointAddress?: string
254254
solverAccountContractAddress?: string
255+
/** Target gas units for EntryPoint deposits per chain. Defaults to 3,000,000. */
256+
targetGasUnits?: number
255257
}
256258
strategies: StrategyConfig[]
257259
chains: (UserProvidedChainConfig & { bundlerUrl?: string })[]
@@ -317,6 +319,7 @@ program
317319
entryPointAddress: config.simplex.entryPointAddress,
318320
dataDir: options.dataDir,
319321
rebalancing: config.rebalancing,
322+
targetGasUnits: config.simplex.targetGasUnits,
320323
}
321324

322325
const configService = new FillerConfigService(fillerChainConfigs, fillerConfigForService)
@@ -489,6 +492,7 @@ program
489492
exoticTokenAddresses,
490493
hyperbridgeWsUrl: config.simplex.hyperbridgeWsUrl,
491494
substratePrivateKey: config.simplex.substratePrivateKey,
495+
dataDir: options.dataDir,
492496
})
493497
metrics.start(metricsPort, metricsHost)
494498
}
@@ -557,6 +561,14 @@ function validateConfig(config: FillerTomlConfig): void {
557561
throw new Error("Signer configuration is required via [simplex.signer]")
558562
}
559563

564+
if (!config.simplex?.substratePrivateKey) {
565+
throw new Error("simplex.substratePrivateKey is required")
566+
}
567+
568+
if (!config.simplex?.hyperbridgeWsUrl) {
569+
throw new Error("simplex.hyperbridgeWsUrl is required")
570+
}
571+
560572
if ((!config.strategies || config.strategies.length === 0) && !allChainsWatchOnly) {
561573
throw new Error("At least one strategy must be configured (unless all chains are in watchOnly mode)")
562574
}

sdk/packages/simplex/src/core/filler.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,9 @@ export class IntentFiller {
9595
}
9696

9797
/**
98-
* Initializes the filler, including setting up EIP-7702 delegation if solver selection is active on any chain.
99-
* This should be called before start().
98+
* Initializes the filler, including setting up EIP-7702 delegation and
99+
* depositing the target amount to the EntryPoint on chains where solver
100+
* selection is active. This should be called before start().
100101
*/
101102
public async initialize(): Promise<void> {
102103
// Check which chains have solver selection active
@@ -123,6 +124,16 @@ export class IntentFiller {
123124
if (!result.success) {
124125
this.logger.warn({ results: result.results }, "Some chains failed EIP-7702 delegation setup")
125126
}
127+
128+
// Ensure EntryPoint deposit covers target gas units on each chain
129+
const targetGasUnits = this.configService.getTargetGasUnits()
130+
for (const chain of chainsWithSolverSelection) {
131+
try {
132+
await this.contractService.topUpEntryPointDeposit(chain, targetGasUnits)
133+
} catch (err) {
134+
this.logger.error({ chain, err }, "Failed to deposit to EntryPoint at startup")
135+
}
136+
}
126137
}
127138
}
128139

@@ -257,9 +268,6 @@ export class IntentFiller {
257268

258269
await Promise.all(promises)
259270

260-
// Withdraw any remaining EntryPoint deposits back to the solver EOA
261-
await this.contractService.withdrawAllEntryPointDeposits()
262-
263271
// Disconnect shared Hyperbridge connection
264272
if (this.hyperbridge) {
265273
const service = await this.hyperbridge.catch(() => null)
@@ -500,18 +508,13 @@ export class IntentFiller {
500508
)
501509

502510
try {
503-
if (solverSelectionActive) {
504-
this.contractService.ensureEntryPointDeposit(order).catch((err) => {
505-
this.logger.error({ orderId: order.id, err }, "Background EntryPoint deposit top-up failed")
506-
})
507-
}
508-
509511
const execStartMs = Date.now()
510512
const hyperbridgeService = solverSelectionActive ? await this.hyperbridge : undefined
511513
const result = await bestStrategy.executeOrder(order, hyperbridgeService)
512514
const execDurationSec = (Date.now() - execStartMs) / 1000
513515
this.monitor.emit("orderTiming", { orderId: order.id, phase: "execution", durationSec: execDurationSec })
514516
this.logger.info({ orderId: order.id, result }, "Order execution completed")
517+
515518
if (result.success) {
516519
this.monitor.emit("orderFilled", { orderId: order.id, hash: result.txHash, volumeUsd: inputUsdValue.toNumber(), profitUsd, chainId: getChainId(order.source) })
517520
}
@@ -548,6 +551,15 @@ export class IntentFiller {
548551
}
549552

550553
private handleOrderFilledOnChain(commitment: HexString, filler: string, chainId: number): void {
554+
// Top up EntryPoint deposit if we were the filler
555+
if (filler.toLowerCase() === this.fillerAddress.toLowerCase()) {
556+
const chain = `EVM-${chainId}`
557+
const targetGasUnits = this.configService.getTargetGasUnits()
558+
this.contractService.topUpEntryPointDeposit(chain, targetGasUnits, 1_000_000n).catch((err) => {
559+
this.logger.error({ commitment, chain, err }, "Post-fill EntryPoint deposit top-up failed")
560+
})
561+
}
562+
551563
if (!this.bidStorage || !this.hyperbridge) {
552564
return
553565
}

sdk/packages/simplex/src/services/ContractInteractionService.ts

Lines changed: 62 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -282,75 +282,97 @@ export class ContractInteractionService {
282282
}
283283

284284
/**
285-
* Ensures the solver's EntryPoint deposit has enough native token to cover
286-
* the estimated gas cost for a given order.
285+
* Tops up the solver's EntryPoint deposit so it covers at least
286+
* `targetGasUnits` at the current gas price. Skips if the wallet
287+
* balance cannot afford at least 1M gas units (not enough to send txs).
287288
*
288-
* Uses cached gas estimates (from estimateGasFillPost) and, if the current
289-
* deposit is insufficient, tops up by depositing 10% of the solver's EOA
290-
* native balance on the destination chain.
289+
* @param chain - The chain identifier
290+
* @param targetGasUnits - Gas units the deposit should cover (default 3M)
291+
* @param thresholdGasUnits - Only top up if deposit is below this many gas units (defaults to targetGasUnits)
291292
*/
292-
async ensureEntryPointDeposit(order: Order): Promise<void> {
293-
if (!order.id) {
294-
this.logger.warn({ destination: order.destination }, "Order has no ID, skipping EntryPoint deposit check")
293+
async topUpEntryPointDeposit(chain: string, targetGasUnits: bigint = 3_000_000n, thresholdGasUnits?: bigint): Promise<void> {
294+
const effectiveThreshold = thresholdGasUnits ?? targetGasUnits
295+
const entryPointAddress = this.configService.getEntryPointAddress(chain)
296+
if (!entryPointAddress) {
295297
return
296298
}
297299

298-
const gasEstimate = this.cacheService.getGasEstimate(order.id)
299-
if (!gasEstimate) {
300-
this.logger.warn(
301-
{ orderId: order.id, destination: order.destination },
302-
"No cached gas estimate found, skipping EntryPoint deposit check",
303-
)
300+
const publicClient = this.clientManager.getPublicClient(chain)
301+
const [currentDeposit, solverBalance, gasPrice] = await Promise.all([
302+
this.getSolverEntryPointBalance(chain),
303+
publicClient.getBalance({ address: this.solverAccountAddress }),
304+
publicClient.getGasPrice(),
305+
])
306+
307+
if (gasPrice === 0n) {
308+
this.logger.warn({ chain }, "Gas price is zero, skipping EntryPoint top-up")
304309
return
305310
}
306311

307-
const requiredNative = 3n * gasEstimate.totalGasCostWei
312+
// Skip if wallet can't afford at least 1M gas units
313+
const walletGasUnits = solverBalance / gasPrice
314+
const minWalletGasUnits = 1_000_000n
308315

309-
const currentDeposit = await this.getSolverEntryPointBalance(order.destination)
316+
if (walletGasUnits < minWalletGasUnits) {
317+
this.logger.warn(
318+
{
319+
chain,
320+
walletBalance: formatEther(solverBalance),
321+
walletGasUnits: walletGasUnits.toString(),
322+
gasPrice: gasPrice.toString(),
323+
},
324+
"Wallet balance too low to afford minimum gas, skipping EntryPoint top-up",
325+
)
326+
return
327+
}
310328

311-
this.logger.debug(
312-
{
313-
orderId: order.id,
314-
destination: order.destination,
315-
currentDeposit: formatEther(currentDeposit),
316-
requiredNative: formatEther(requiredNative),
317-
},
318-
"EntryPoint deposit gas coverage check",
319-
)
329+
const targetDeposit = targetGasUnits * gasPrice
330+
const thresholdDeposit = effectiveThreshold * gasPrice
331+
const depositGasUnits = currentDeposit / gasPrice
320332

321-
if (currentDeposit >= requiredNative) {
333+
if (currentDeposit >= thresholdDeposit) {
334+
this.logger.info(
335+
{
336+
chain,
337+
currentDeposit: formatEther(currentDeposit),
338+
depositGasUnits: depositGasUnits.toString(),
339+
targetGasUnits: targetGasUnits.toString(),
340+
walletBalance: formatEther(solverBalance),
341+
},
342+
"EntryPoint deposit covers target gas units, no top-up needed",
343+
)
322344
return
323345
}
324346

325-
const publicClient = this.clientManager.getPublicClient(order.destination)
326-
const solverBalance = await publicClient.getBalance({ address: this.solverAccountAddress })
327-
const depositAmount = solverBalance / 10n
347+
const deficit = targetDeposit - currentDeposit
328348

329-
if (depositAmount === 0n) {
349+
if (solverBalance < deficit) {
330350
this.logger.warn(
331351
{
332-
orderId: order.id,
333-
destination: order.destination,
352+
chain,
353+
deficit: formatEther(deficit),
334354
solverBalance: formatEther(solverBalance),
355+
depositGasUnits: depositGasUnits.toString(),
356+
targetGasUnits: targetGasUnits.toString(),
335357
},
336-
"Solver EOA balance too low to top up EntryPoint deposit",
358+
"Solver EOA balance insufficient to reach target deposit, depositing available balance",
337359
)
360+
await this.depositToEntryPoint(chain, solverBalance)
338361
return
339362
}
340363

341364
this.logger.info(
342365
{
343-
orderId: order.id,
344-
destination: order.destination,
345-
requiredNative: formatEther(requiredNative),
366+
chain,
346367
currentDeposit: formatEther(currentDeposit),
347-
solverBalance: formatEther(solverBalance),
348-
depositAmount: formatEther(depositAmount),
368+
depositGasUnits: depositGasUnits.toString(),
369+
targetGasUnits: targetGasUnits.toString(),
370+
topUpAmount: formatEther(deficit),
349371
},
350-
"Top up EntryPoint deposit by 10% of solver EOA balance",
372+
"Topping up EntryPoint deposit to cover target gas units",
351373
)
352374

353-
await this.depositToEntryPoint(order.destination, depositAmount)
375+
await this.depositToEntryPoint(chain, deficit)
354376
}
355377

356378
/**

sdk/packages/simplex/src/services/FillerConfigService.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ export interface FillerConfig {
3838
*/
3939
gasFeeBump?: GasFeeBumpConfig
4040
rebalancing?: RebalancingConfig
41+
/**
42+
* Target gas units the EntryPoint deposit should cover per chain.
43+
* Defaults to 3,000,000 if not set.
44+
*/
45+
targetGasUnits?: number
4146
}
4247

4348
/**
@@ -303,4 +308,12 @@ export class FillerConfigService {
303308
getTriggerPercentage(): number | undefined {
304309
return this.fillerConfig?.rebalancing?.triggerPercentage
305310
}
311+
312+
/**
313+
* Get target gas units for EntryPoint deposits.
314+
* Defaults to 3,000,000 if not configured.
315+
*/
316+
getTargetGasUnits(): bigint {
317+
return BigInt(this.fillerConfig?.targetGasUnits ?? 3_000_000)
318+
}
306319
}

0 commit comments

Comments
 (0)