@hyperbridge/sdk 1.8.8 → 1.9.1
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/browser/index.d.ts +46 -15
- package/dist/browser/index.js +300 -178
- package/dist/browser/index.js.map +1 -1
- package/dist/node/index.d.ts +46 -15
- package/dist/node/index.js +300 -178
- package/dist/node/index.js.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.js
CHANGED
|
@@ -606,7 +606,6 @@ var IntentOrderStatus = Object.freeze({
|
|
|
606
606
|
AWAITING_BIDS: "AWAITING_BIDS",
|
|
607
607
|
BIDS_RECEIVED: "BIDS_RECEIVED",
|
|
608
608
|
BID_SELECTED: "BID_SELECTED",
|
|
609
|
-
USEROP_SUBMITTED: "USEROP_SUBMITTED",
|
|
610
609
|
FILLED: "FILLED",
|
|
611
610
|
PARTIAL_FILL: "PARTIAL_FILL",
|
|
612
611
|
EXPIRED: "EXPIRED",
|
|
@@ -5583,6 +5582,7 @@ var EvmChain = class _EvmChain {
|
|
|
5583
5582
|
});
|
|
5584
5583
|
this.chainConfigService = new ChainConfigService();
|
|
5585
5584
|
}
|
|
5585
|
+
params;
|
|
5586
5586
|
publicClient;
|
|
5587
5587
|
chainConfigService;
|
|
5588
5588
|
/**
|
|
@@ -6183,6 +6183,7 @@ var HttpRpcClient = class {
|
|
|
6183
6183
|
constructor(url) {
|
|
6184
6184
|
this.url = url;
|
|
6185
6185
|
}
|
|
6186
|
+
url;
|
|
6186
6187
|
/**
|
|
6187
6188
|
* Make an RPC call over HTTP
|
|
6188
6189
|
* @param method - The RPC method name
|
|
@@ -6220,6 +6221,7 @@ var SubstrateChain = class _SubstrateChain {
|
|
|
6220
6221
|
const httpUrl = replaceWebsocketWithHttp(url);
|
|
6221
6222
|
this.rpcClient = new HttpRpcClient(httpUrl);
|
|
6222
6223
|
}
|
|
6224
|
+
params;
|
|
6223
6225
|
/*
|
|
6224
6226
|
* api: The Polkadot API instance for the Substrate chain.
|
|
6225
6227
|
*/
|
|
@@ -6737,6 +6739,9 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
6737
6739
|
this.substratePrivateKey = substratePrivateKey;
|
|
6738
6740
|
this.ownsConnection = ownsConnection;
|
|
6739
6741
|
}
|
|
6742
|
+
api;
|
|
6743
|
+
substratePrivateKey;
|
|
6744
|
+
ownsConnection;
|
|
6740
6745
|
/** Cached result of whether the node exposes intents_* RPC methods */
|
|
6741
6746
|
hasIntentsRpc = null;
|
|
6742
6747
|
/**
|
|
@@ -7014,6 +7019,7 @@ var TronChain = class _TronChain {
|
|
|
7014
7019
|
this.evm = evm;
|
|
7015
7020
|
this.tronWebInstance = new TronWeb({ fullHost: params.rpcUrl });
|
|
7016
7021
|
}
|
|
7022
|
+
params;
|
|
7017
7023
|
evm;
|
|
7018
7024
|
tronWebInstance;
|
|
7019
7025
|
/**
|
|
@@ -7554,6 +7560,7 @@ var SubstrateHttpRpc = class {
|
|
|
7554
7560
|
constructor(url) {
|
|
7555
7561
|
this.url = url;
|
|
7556
7562
|
}
|
|
7563
|
+
url;
|
|
7557
7564
|
async call(method, params = []) {
|
|
7558
7565
|
const body = JSON.stringify({
|
|
7559
7566
|
jsonrpc: "2.0",
|
|
@@ -7598,6 +7605,7 @@ var PolkadotHubChain = class _PolkadotHubChain {
|
|
|
7598
7605
|
this.evm = evm;
|
|
7599
7606
|
this.substrateRpc = new SubstrateHttpRpc(replaceWebsocketWithHttp(params.substrateRpcUrl));
|
|
7600
7607
|
}
|
|
7608
|
+
params;
|
|
7601
7609
|
evm;
|
|
7602
7610
|
substrateRpc;
|
|
7603
7611
|
static fromParams(params) {
|
|
@@ -12689,6 +12697,7 @@ var CryptoUtils = class {
|
|
|
12689
12697
|
constructor(ctx) {
|
|
12690
12698
|
this.ctx = ctx;
|
|
12691
12699
|
}
|
|
12700
|
+
ctx;
|
|
12692
12701
|
/**
|
|
12693
12702
|
* Computes an EIP-712 domain separator for a given contract.
|
|
12694
12703
|
*
|
|
@@ -15224,6 +15233,7 @@ var OrderPlacer = class {
|
|
|
15224
15233
|
constructor(ctx) {
|
|
15225
15234
|
this.ctx = ctx;
|
|
15226
15235
|
}
|
|
15236
|
+
ctx;
|
|
15227
15237
|
/**
|
|
15228
15238
|
* Bidirectional async generator that orchestrates order placement.
|
|
15229
15239
|
*
|
|
@@ -15301,67 +15311,40 @@ var OrderPlacer = class {
|
|
|
15301
15311
|
return { order, receipt };
|
|
15302
15312
|
}
|
|
15303
15313
|
};
|
|
15304
|
-
|
|
15305
|
-
// src/protocols/intents/OrderExecutor.ts
|
|
15306
15314
|
var USED_USEROPS_STORAGE_KEY = (commitment) => `used-userops:${commitment.toLowerCase()}`;
|
|
15307
15315
|
var OrderExecutor = class {
|
|
15308
|
-
/**
|
|
15309
|
-
* @param ctx - Shared IntentsV2 context providing the destination chain
|
|
15310
|
-
* client, coprocessor, bundler URL, and storage adapters.
|
|
15311
|
-
* @param bidManager - Handles bid validation, sorting, simulation, and
|
|
15312
|
-
* UserOperation submission.
|
|
15313
|
-
* @param crypto - Crypto utilities used to compute UserOperation hashes for
|
|
15314
|
-
* deduplication.
|
|
15315
|
-
*/
|
|
15316
15316
|
constructor(ctx, bidManager, crypto) {
|
|
15317
15317
|
this.ctx = ctx;
|
|
15318
15318
|
this.bidManager = bidManager;
|
|
15319
15319
|
this.crypto = crypto;
|
|
15320
15320
|
}
|
|
15321
|
+
ctx;
|
|
15322
|
+
bidManager;
|
|
15323
|
+
crypto;
|
|
15321
15324
|
/**
|
|
15322
|
-
*
|
|
15323
|
-
*
|
|
15324
|
-
* or an unrecoverable error occurs.
|
|
15325
|
-
*
|
|
15326
|
-
* **Status progression (cross-chain orders):**
|
|
15327
|
-
* `AWAITING_BIDS` → `BIDS_RECEIVED` → `BID_SELECTED` → `USEROP_SUBMITTED`
|
|
15328
|
-
* then terminates (settlement is confirmed off-chain via Hyperbridge).
|
|
15329
|
-
*
|
|
15330
|
-
* **Status progression (same-chain orders):**
|
|
15331
|
-
* `AWAITING_BIDS` → `BIDS_RECEIVED` → `BID_SELECTED` → `USEROP_SUBMITTED`
|
|
15332
|
-
* → (`FILLED` | `PARTIAL_FILL`)* → (`FILLED` | `EXPIRED`)
|
|
15333
|
-
*
|
|
15334
|
-
* **Error statuses:** `FAILED` (retryable error during bid selection/submission,
|
|
15335
|
-
* triggers automatic retry) or `EXPIRED` (deadline reached or no new bids —
|
|
15336
|
-
* terminal, no further retries).
|
|
15337
|
-
*
|
|
15338
|
-
* @param options - Execution parameters including the placed order, its
|
|
15339
|
-
* session private key, bid collection settings, and poll interval.
|
|
15340
|
-
* @yields {@link IntentOrderStatusUpdate} objects describing each stage.
|
|
15341
|
-
* @throws Never throws directly; all errors are reported as `FAILED` yields.
|
|
15325
|
+
* Sleeps until the order's block deadline is reached, then yields EXPIRED.
|
|
15326
|
+
* Uses the chain's block time to calculate the sleep duration.
|
|
15342
15327
|
*/
|
|
15343
|
-
async *
|
|
15344
|
-
const
|
|
15345
|
-
|
|
15346
|
-
|
|
15347
|
-
|
|
15348
|
-
|
|
15349
|
-
|
|
15350
|
-
|
|
15351
|
-
|
|
15352
|
-
const commitment = order.id;
|
|
15353
|
-
const isSameChain = order.source === order.destination;
|
|
15354
|
-
if (!this.ctx.intentsCoprocessor) {
|
|
15355
|
-
yield { status: "FAILED", error: "IntentsCoprocessor required for order execution" };
|
|
15356
|
-
return;
|
|
15357
|
-
}
|
|
15358
|
-
if (!this.ctx.bundlerUrl) {
|
|
15359
|
-
yield { status: "FAILED", error: "Bundler URL not configured" };
|
|
15360
|
-
return;
|
|
15328
|
+
async *deadlineStream(deadline, commitment) {
|
|
15329
|
+
const client = this.ctx.dest.client;
|
|
15330
|
+
const blockTimeMs = client.chain?.blockTime ?? 2e3;
|
|
15331
|
+
while (true) {
|
|
15332
|
+
const currentBlock = await client.getBlockNumber();
|
|
15333
|
+
if (currentBlock >= deadline) break;
|
|
15334
|
+
const blocksRemaining = Number(deadline - currentBlock);
|
|
15335
|
+
const sleepMs = blocksRemaining * blockTimeMs;
|
|
15336
|
+
await sleep(sleepMs);
|
|
15361
15337
|
}
|
|
15338
|
+
yield {
|
|
15339
|
+
status: "EXPIRED",
|
|
15340
|
+
commitment,
|
|
15341
|
+
error: "Order deadline reached"
|
|
15342
|
+
};
|
|
15343
|
+
}
|
|
15344
|
+
/** Loads the persisted deduplication set of already-submitted UserOp hashes for a given order commitment. */
|
|
15345
|
+
async loadUsedUserOps(commitment) {
|
|
15362
15346
|
const usedUserOps = /* @__PURE__ */ new Set();
|
|
15363
|
-
const
|
|
15364
|
-
const persisted = await this.ctx.usedUserOpsStorage.getItem(storageKey);
|
|
15347
|
+
const persisted = await this.ctx.usedUserOpsStorage.getItem(USED_USEROPS_STORAGE_KEY(commitment));
|
|
15365
15348
|
if (persisted) {
|
|
15366
15349
|
try {
|
|
15367
15350
|
const parsed = JSON.parse(persisted);
|
|
@@ -15371,74 +15354,214 @@ var OrderExecutor = class {
|
|
|
15371
15354
|
} catch {
|
|
15372
15355
|
}
|
|
15373
15356
|
}
|
|
15374
|
-
|
|
15375
|
-
|
|
15376
|
-
|
|
15357
|
+
return usedUserOps;
|
|
15358
|
+
}
|
|
15359
|
+
/** Persists the deduplication set of UserOp hashes to storage. */
|
|
15360
|
+
async persistUsedUserOps(commitment, usedUserOps) {
|
|
15361
|
+
await this.ctx.usedUserOpsStorage.setItem(
|
|
15362
|
+
USED_USEROPS_STORAGE_KEY(commitment),
|
|
15363
|
+
JSON.stringify([...usedUserOps])
|
|
15364
|
+
);
|
|
15365
|
+
}
|
|
15366
|
+
/**
|
|
15367
|
+
* Creates a closure that computes the deduplication hash key for a
|
|
15368
|
+
* UserOperation, pre-bound to the order's destination chain and entry point.
|
|
15369
|
+
*/
|
|
15370
|
+
createUserOpHasher(order) {
|
|
15377
15371
|
const entryPointAddress = this.ctx.dest.configService.getEntryPointV08Address(hexToString(order.destination));
|
|
15378
15372
|
const chainId = BigInt(
|
|
15379
15373
|
this.ctx.dest.client.chain?.id ?? Number.parseInt(this.ctx.dest.config.stateMachineId.split("-")[1])
|
|
15380
15374
|
);
|
|
15381
|
-
|
|
15375
|
+
return (userOp) => this.crypto.computeUserOpHash(userOp, entryPointAddress, chainId);
|
|
15376
|
+
}
|
|
15377
|
+
/**
|
|
15378
|
+
* Fetches bids from the coprocessor for a given order commitment.
|
|
15379
|
+
* If a preferred solver is configured and the solver lock has not expired,
|
|
15380
|
+
* only bids from that solver are returned.
|
|
15381
|
+
*/
|
|
15382
|
+
async fetchBids(params) {
|
|
15383
|
+
const { commitment, solver, solverLockStartTime } = params;
|
|
15384
|
+
const fetchedBids = await this.ctx.intentsCoprocessor.getBidsForOrder(commitment);
|
|
15385
|
+
if (solver) {
|
|
15386
|
+
const { address, timeoutMs } = solver;
|
|
15387
|
+
const solverLockActive = Date.now() - solverLockStartTime < timeoutMs;
|
|
15388
|
+
return solverLockActive ? fetchedBids.filter((bid) => bid.userOp.sender.toLowerCase() === address.toLowerCase()) : fetchedBids;
|
|
15389
|
+
}
|
|
15390
|
+
return fetchedBids;
|
|
15391
|
+
}
|
|
15392
|
+
/**
|
|
15393
|
+
* Selects the best bid from the provided candidates, submits the
|
|
15394
|
+
* UserOperation, and persists the dedup entry to prevent resubmission.
|
|
15395
|
+
*/
|
|
15396
|
+
async submitBid(params) {
|
|
15397
|
+
const { order, freshBids, sessionPrivateKey, commitment, usedUserOps, userOpHashKey } = params;
|
|
15398
|
+
const result = await this.bidManager.selectBid(order, freshBids, sessionPrivateKey);
|
|
15399
|
+
usedUserOps.add(userOpHashKey(result.userOp));
|
|
15400
|
+
await this.persistUsedUserOps(commitment, usedUserOps);
|
|
15401
|
+
return result;
|
|
15402
|
+
}
|
|
15403
|
+
/**
|
|
15404
|
+
* Processes a fill result and returns updated fill accumulators,
|
|
15405
|
+
* the status update to yield (if any), and whether the order is
|
|
15406
|
+
* fully satisfied.
|
|
15407
|
+
*/
|
|
15408
|
+
processFillResult(result, commitment, targetAssets, totalFilledAssets, remainingAssets) {
|
|
15409
|
+
if (result.fillStatus === "full") {
|
|
15410
|
+
totalFilledAssets = targetAssets.map((a) => ({ token: a.token, amount: a.amount }));
|
|
15411
|
+
remainingAssets = targetAssets.map((a) => ({ token: a.token, amount: 0n }));
|
|
15412
|
+
return {
|
|
15413
|
+
update: {
|
|
15414
|
+
status: "FILLED",
|
|
15415
|
+
commitment,
|
|
15416
|
+
userOpHash: result.userOpHash,
|
|
15417
|
+
selectedSolver: result.solverAddress,
|
|
15418
|
+
transactionHash: result.txnHash,
|
|
15419
|
+
totalFilledAssets,
|
|
15420
|
+
remainingAssets
|
|
15421
|
+
},
|
|
15422
|
+
done: true,
|
|
15423
|
+
totalFilledAssets,
|
|
15424
|
+
remainingAssets
|
|
15425
|
+
};
|
|
15426
|
+
}
|
|
15427
|
+
if (result.fillStatus === "partial") {
|
|
15428
|
+
const filledAssets = result.filledAssets ?? [];
|
|
15429
|
+
totalFilledAssets = totalFilledAssets.map((a) => {
|
|
15430
|
+
const filled = filledAssets.find((f) => f.token === a.token);
|
|
15431
|
+
return filled ? { token: a.token, amount: a.amount + filled.amount } : { ...a };
|
|
15432
|
+
});
|
|
15433
|
+
remainingAssets = targetAssets.map((target) => {
|
|
15434
|
+
const filled = totalFilledAssets.find((a) => a.token === target.token);
|
|
15435
|
+
const filledAmt = filled?.amount ?? 0n;
|
|
15436
|
+
return {
|
|
15437
|
+
token: target.token,
|
|
15438
|
+
amount: filledAmt >= target.amount ? 0n : target.amount - filledAmt
|
|
15439
|
+
};
|
|
15440
|
+
});
|
|
15441
|
+
const fullyFilled = remainingAssets.every((a) => a.amount === 0n);
|
|
15442
|
+
return {
|
|
15443
|
+
update: fullyFilled ? {
|
|
15444
|
+
status: "FILLED",
|
|
15445
|
+
commitment,
|
|
15446
|
+
userOpHash: result.userOpHash,
|
|
15447
|
+
selectedSolver: result.solverAddress,
|
|
15448
|
+
transactionHash: result.txnHash,
|
|
15449
|
+
totalFilledAssets,
|
|
15450
|
+
remainingAssets
|
|
15451
|
+
} : {
|
|
15452
|
+
status: "PARTIAL_FILL",
|
|
15453
|
+
commitment,
|
|
15454
|
+
userOpHash: result.userOpHash,
|
|
15455
|
+
selectedSolver: result.solverAddress,
|
|
15456
|
+
transactionHash: result.txnHash,
|
|
15457
|
+
filledAssets,
|
|
15458
|
+
totalFilledAssets,
|
|
15459
|
+
remainingAssets
|
|
15460
|
+
},
|
|
15461
|
+
done: fullyFilled,
|
|
15462
|
+
totalFilledAssets,
|
|
15463
|
+
remainingAssets
|
|
15464
|
+
};
|
|
15465
|
+
}
|
|
15466
|
+
return { update: null, done: false, totalFilledAssets, remainingAssets };
|
|
15467
|
+
}
|
|
15468
|
+
/**
|
|
15469
|
+
* Executes an intent order by racing bid polling against the order's
|
|
15470
|
+
* block deadline. Yields status updates at each lifecycle stage.
|
|
15471
|
+
*
|
|
15472
|
+
* **Same-chain:** `AWAITING_BIDS` → `BIDS_RECEIVED` → `BID_SELECTED`
|
|
15473
|
+
* → (`FILLED` | `PARTIAL_FILL`)* → (`FILLED` | `EXPIRED`)
|
|
15474
|
+
*
|
|
15475
|
+
* **Cross-chain:** `AWAITING_BIDS` → `BIDS_RECEIVED` → `BID_SELECTED`
|
|
15476
|
+
* (terminates — settlement is confirmed async via Hyperbridge)
|
|
15477
|
+
*/
|
|
15478
|
+
async *executeOrder(options) {
|
|
15479
|
+
const { order, sessionPrivateKey, auctionTimeMs, pollIntervalMs = DEFAULT_POLL_INTERVAL, solver } = options;
|
|
15480
|
+
const commitment = order.id;
|
|
15481
|
+
const isSameChain = order.source === order.destination;
|
|
15482
|
+
if (!this.ctx.intentsCoprocessor) {
|
|
15483
|
+
yield { status: "FAILED", error: "IntentsCoprocessor required for order execution" };
|
|
15484
|
+
return;
|
|
15485
|
+
}
|
|
15486
|
+
if (!this.ctx.bundlerUrl) {
|
|
15487
|
+
yield { status: "FAILED", error: "Bundler URL not configured" };
|
|
15488
|
+
return;
|
|
15489
|
+
}
|
|
15490
|
+
const usedUserOps = await this.loadUsedUserOps(commitment);
|
|
15491
|
+
const userOpHashKey = this.createUserOpHasher(order);
|
|
15382
15492
|
const targetAssets = order.output.assets.map((a) => ({ token: a.token, amount: a.amount }));
|
|
15383
15493
|
let totalFilledAssets = order.output.assets.map((a) => ({ token: a.token, amount: 0n }));
|
|
15384
15494
|
let remainingAssets = order.output.assets.map((a) => ({ token: a.token, amount: a.amount }));
|
|
15495
|
+
const executionStream = this.executionStream({
|
|
15496
|
+
order,
|
|
15497
|
+
sessionPrivateKey,
|
|
15498
|
+
commitment,
|
|
15499
|
+
auctionTimeMs,
|
|
15500
|
+
pollIntervalMs,
|
|
15501
|
+
solver,
|
|
15502
|
+
usedUserOps,
|
|
15503
|
+
userOpHashKey,
|
|
15504
|
+
targetAssets,
|
|
15505
|
+
totalFilledAssets,
|
|
15506
|
+
remainingAssets
|
|
15507
|
+
});
|
|
15508
|
+
const deadlineTimeout = this.deadlineStream(order.deadline, commitment);
|
|
15509
|
+
const combined = mergeRace(deadlineTimeout, executionStream);
|
|
15510
|
+
for await (const update of combined) {
|
|
15511
|
+
yield update;
|
|
15512
|
+
if (update.status === "EXPIRED" || update.status === "FILLED") return;
|
|
15513
|
+
if (update.status === "BID_SELECTED" && !isSameChain) return;
|
|
15514
|
+
}
|
|
15515
|
+
}
|
|
15516
|
+
/**
|
|
15517
|
+
* Core execution loop that polls for bids, submits UserOperations,
|
|
15518
|
+
* and tracks fill progress. Yields between each poll iteration so
|
|
15519
|
+
* that `mergeRace` can interleave the deadline stream.
|
|
15520
|
+
*/
|
|
15521
|
+
async *executionStream(params) {
|
|
15522
|
+
const {
|
|
15523
|
+
order,
|
|
15524
|
+
sessionPrivateKey,
|
|
15525
|
+
commitment,
|
|
15526
|
+
auctionTimeMs,
|
|
15527
|
+
pollIntervalMs,
|
|
15528
|
+
solver,
|
|
15529
|
+
usedUserOps,
|
|
15530
|
+
userOpHashKey,
|
|
15531
|
+
targetAssets
|
|
15532
|
+
} = params;
|
|
15533
|
+
let { totalFilledAssets, remainingAssets } = params;
|
|
15534
|
+
const solverLockStartTime = Date.now();
|
|
15535
|
+
yield { status: "AWAITING_BIDS", commitment, totalFilledAssets, remainingAssets };
|
|
15385
15536
|
try {
|
|
15537
|
+
const auctionEnd = Date.now() + auctionTimeMs;
|
|
15538
|
+
while (Date.now() < auctionEnd) {
|
|
15539
|
+
await sleep(Math.min(pollIntervalMs, auctionEnd - Date.now()));
|
|
15540
|
+
}
|
|
15386
15541
|
while (true) {
|
|
15387
|
-
|
|
15388
|
-
|
|
15389
|
-
const
|
|
15390
|
-
|
|
15391
|
-
|
|
15392
|
-
commitment,
|
|
15393
|
-
totalFilledAssets,
|
|
15394
|
-
remainingAssets,
|
|
15395
|
-
error: deadlineError
|
|
15396
|
-
};
|
|
15397
|
-
return;
|
|
15398
|
-
}
|
|
15399
|
-
yield { status: "AWAITING_BIDS", commitment, totalFilledAssets, remainingAssets };
|
|
15400
|
-
const startTime = Date.now();
|
|
15401
|
-
let bids = [];
|
|
15402
|
-
let solverLockExpired = false;
|
|
15403
|
-
while (Date.now() - startTime < bidTimeoutMs) {
|
|
15404
|
-
try {
|
|
15405
|
-
const fetchedBids = await this.ctx.intentsCoprocessor.getBidsForOrder(commitment);
|
|
15406
|
-
if (solver) {
|
|
15407
|
-
const { address, timeoutMs } = solver;
|
|
15408
|
-
const solverLockActive = Date.now() - startTime < timeoutMs;
|
|
15409
|
-
if (!solverLockActive) solverLockExpired = true;
|
|
15410
|
-
bids = solverLockActive ? fetchedBids.filter((bid) => bid.userOp.sender.toLowerCase() === address.toLowerCase()) : fetchedBids;
|
|
15411
|
-
} else {
|
|
15412
|
-
bids = fetchedBids;
|
|
15413
|
-
}
|
|
15414
|
-
if (bids.length >= minBids) {
|
|
15415
|
-
break;
|
|
15416
|
-
}
|
|
15417
|
-
} catch {
|
|
15418
|
-
}
|
|
15542
|
+
let freshBids;
|
|
15543
|
+
try {
|
|
15544
|
+
const bids = await this.fetchBids({ commitment, solver, solverLockStartTime });
|
|
15545
|
+
freshBids = bids.filter((bid) => !usedUserOps.has(userOpHashKey(bid.userOp)));
|
|
15546
|
+
} catch {
|
|
15419
15547
|
await sleep(pollIntervalMs);
|
|
15548
|
+
continue;
|
|
15420
15549
|
}
|
|
15421
|
-
const freshBids = bids.filter((bid) => {
|
|
15422
|
-
const key = userOpHashKey(bid.userOp);
|
|
15423
|
-
return !usedUserOps.has(key);
|
|
15424
|
-
});
|
|
15425
15550
|
if (freshBids.length === 0) {
|
|
15426
|
-
|
|
15427
|
-
|
|
15428
|
-
const noBidsError = isPartiallyFilled ? `No new bids${solverClause} after partial fill` : `No new bids${solverClause} available within ${bidTimeoutMs}ms timeout`;
|
|
15429
|
-
yield {
|
|
15430
|
-
status: "EXPIRED",
|
|
15431
|
-
commitment,
|
|
15432
|
-
totalFilledAssets,
|
|
15433
|
-
remainingAssets,
|
|
15434
|
-
error: noBidsError
|
|
15435
|
-
};
|
|
15436
|
-
return;
|
|
15551
|
+
await sleep(pollIntervalMs);
|
|
15552
|
+
continue;
|
|
15437
15553
|
}
|
|
15438
15554
|
yield { status: "BIDS_RECEIVED", commitment, bidCount: freshBids.length, bids: freshBids };
|
|
15439
|
-
let
|
|
15555
|
+
let submitResult;
|
|
15440
15556
|
try {
|
|
15441
|
-
|
|
15557
|
+
submitResult = await this.submitBid({
|
|
15558
|
+
order,
|
|
15559
|
+
freshBids,
|
|
15560
|
+
sessionPrivateKey,
|
|
15561
|
+
commitment,
|
|
15562
|
+
usedUserOps,
|
|
15563
|
+
userOpHashKey
|
|
15564
|
+
});
|
|
15442
15565
|
} catch (err) {
|
|
15443
15566
|
yield {
|
|
15444
15567
|
status: "FAILED",
|
|
@@ -15450,77 +15573,26 @@ var OrderExecutor = class {
|
|
|
15450
15573
|
await sleep(pollIntervalMs);
|
|
15451
15574
|
continue;
|
|
15452
15575
|
}
|
|
15453
|
-
const usedKey = userOpHashKey(result.userOp);
|
|
15454
|
-
usedUserOps.add(usedKey);
|
|
15455
|
-
await persistUsedUserOps();
|
|
15456
15576
|
yield {
|
|
15457
15577
|
status: "BID_SELECTED",
|
|
15458
15578
|
commitment,
|
|
15459
|
-
selectedSolver:
|
|
15460
|
-
userOpHash:
|
|
15461
|
-
userOp:
|
|
15579
|
+
selectedSolver: submitResult.solverAddress,
|
|
15580
|
+
userOpHash: submitResult.userOpHash,
|
|
15581
|
+
userOp: submitResult.userOp,
|
|
15582
|
+
transactionHash: submitResult.txnHash
|
|
15462
15583
|
};
|
|
15463
|
-
|
|
15464
|
-
|
|
15584
|
+
const fill = this.processFillResult(
|
|
15585
|
+
submitResult,
|
|
15465
15586
|
commitment,
|
|
15466
|
-
|
|
15467
|
-
|
|
15468
|
-
|
|
15469
|
-
|
|
15470
|
-
|
|
15471
|
-
|
|
15472
|
-
|
|
15473
|
-
|
|
15474
|
-
|
|
15475
|
-
remainingAssets = targetAssets.map((a) => ({ token: a.token, amount: 0n }));
|
|
15476
|
-
yield {
|
|
15477
|
-
status: "FILLED",
|
|
15478
|
-
commitment,
|
|
15479
|
-
userOpHash: result.userOpHash,
|
|
15480
|
-
selectedSolver: result.solverAddress,
|
|
15481
|
-
transactionHash: result.txnHash,
|
|
15482
|
-
totalFilledAssets,
|
|
15483
|
-
remainingAssets
|
|
15484
|
-
};
|
|
15485
|
-
return;
|
|
15486
|
-
}
|
|
15487
|
-
if (result.fillStatus === "partial") {
|
|
15488
|
-
const filledAssets = result.filledAssets ?? [];
|
|
15489
|
-
for (const filled of filledAssets) {
|
|
15490
|
-
const entry = totalFilledAssets.find((a) => a.token === filled.token);
|
|
15491
|
-
if (entry) entry.amount += filled.amount;
|
|
15492
|
-
}
|
|
15493
|
-
remainingAssets = targetAssets.map((target) => {
|
|
15494
|
-
const filled = totalFilledAssets.find((a) => a.token === target.token);
|
|
15495
|
-
const filledAmt = filled?.amount ?? 0n;
|
|
15496
|
-
return {
|
|
15497
|
-
token: target.token,
|
|
15498
|
-
amount: filledAmt >= target.amount ? 0n : target.amount - filledAmt
|
|
15499
|
-
};
|
|
15500
|
-
});
|
|
15501
|
-
const fullyFilled = remainingAssets.every((a) => a.amount === 0n);
|
|
15502
|
-
if (fullyFilled) {
|
|
15503
|
-
yield {
|
|
15504
|
-
status: "FILLED",
|
|
15505
|
-
commitment,
|
|
15506
|
-
userOpHash: result.userOpHash,
|
|
15507
|
-
selectedSolver: result.solverAddress,
|
|
15508
|
-
transactionHash: result.txnHash,
|
|
15509
|
-
totalFilledAssets,
|
|
15510
|
-
remainingAssets
|
|
15511
|
-
};
|
|
15512
|
-
return;
|
|
15513
|
-
}
|
|
15514
|
-
yield {
|
|
15515
|
-
status: "PARTIAL_FILL",
|
|
15516
|
-
commitment,
|
|
15517
|
-
userOpHash: result.userOpHash,
|
|
15518
|
-
selectedSolver: result.solverAddress,
|
|
15519
|
-
transactionHash: result.txnHash,
|
|
15520
|
-
filledAssets,
|
|
15521
|
-
totalFilledAssets,
|
|
15522
|
-
remainingAssets
|
|
15523
|
-
};
|
|
15587
|
+
targetAssets,
|
|
15588
|
+
totalFilledAssets,
|
|
15589
|
+
remainingAssets
|
|
15590
|
+
);
|
|
15591
|
+
totalFilledAssets = fill.totalFilledAssets;
|
|
15592
|
+
remainingAssets = fill.remainingAssets;
|
|
15593
|
+
if (fill.update) {
|
|
15594
|
+
yield fill.update;
|
|
15595
|
+
if (fill.done) return;
|
|
15524
15596
|
}
|
|
15525
15597
|
}
|
|
15526
15598
|
} catch (err) {
|
|
@@ -15540,6 +15612,7 @@ var OrderCanceller = class {
|
|
|
15540
15612
|
constructor(ctx) {
|
|
15541
15613
|
this.ctx = ctx;
|
|
15542
15614
|
}
|
|
15615
|
+
ctx;
|
|
15543
15616
|
/**
|
|
15544
15617
|
* Returns both the native token cost and the relayer fee for cancelling an
|
|
15545
15618
|
* order. Frontends can use `relayerFee` to approve the ERC-20 spend before
|
|
@@ -16009,6 +16082,8 @@ var BidManager = class {
|
|
|
16009
16082
|
this.ctx = ctx;
|
|
16010
16083
|
this.crypto = crypto;
|
|
16011
16084
|
}
|
|
16085
|
+
ctx;
|
|
16086
|
+
crypto;
|
|
16012
16087
|
/**
|
|
16013
16088
|
* Constructs a signed `PackedUserOperation` that a solver can submit to the
|
|
16014
16089
|
* Hyperbridge coprocessor as a bid to fill an order.
|
|
@@ -18088,6 +18163,8 @@ var GasEstimator = class {
|
|
|
18088
18163
|
this.ctx = ctx;
|
|
18089
18164
|
this.crypto = crypto;
|
|
18090
18165
|
}
|
|
18166
|
+
ctx;
|
|
18167
|
+
crypto;
|
|
18091
18168
|
/**
|
|
18092
18169
|
* Estimates the gas cost for a solver to fill the given order and returns
|
|
18093
18170
|
* a structured estimate with individual gas components and total costs in
|
|
@@ -18509,6 +18586,7 @@ var OrderStatusChecker = class {
|
|
|
18509
18586
|
constructor(ctx) {
|
|
18510
18587
|
this.ctx = ctx;
|
|
18511
18588
|
}
|
|
18589
|
+
ctx;
|
|
18512
18590
|
/**
|
|
18513
18591
|
* Checks if a V2 order has been filled by reading the commitment storage slot on the destination chain.
|
|
18514
18592
|
*
|
|
@@ -18690,7 +18768,7 @@ var IntentGateway = class _IntentGateway {
|
|
|
18690
18768
|
* The caller must sign the transaction and pass it back via `gen.next(signedTx)`.
|
|
18691
18769
|
* 3. Yields `ORDER_PLACED` with the finalised order and transaction hash once
|
|
18692
18770
|
* the `OrderPlaced` event is confirmed.
|
|
18693
|
-
* 4. Delegates to {@link OrderExecutor.
|
|
18771
|
+
* 4. Delegates to {@link OrderExecutor.executeOrder} and forwards all
|
|
18694
18772
|
* subsequent status updates until the order is filled, exhausted, or fails.
|
|
18695
18773
|
*
|
|
18696
18774
|
* @param order - The order to place and execute. `order.fees` may be 0; it
|
|
@@ -18700,8 +18778,7 @@ var IntentGateway = class _IntentGateway {
|
|
|
18700
18778
|
* @param options - Optional tuning parameters:
|
|
18701
18779
|
* - `maxPriorityFeePerGasBumpPercent` — bump % for the priority fee estimate (default 8).
|
|
18702
18780
|
* - `maxFeePerGasBumpPercent` — bump % for the max fee estimate (default 10).
|
|
18703
|
-
* - `
|
|
18704
|
-
* - `bidTimeoutMs` — how long to poll for bids before giving up (default 60 000 ms).
|
|
18781
|
+
* - `auctionTimeMs` — duration in ms to collect bids before selecting the best one.
|
|
18705
18782
|
* - `pollIntervalMs` — interval between bid-polling attempts.
|
|
18706
18783
|
* @yields {@link IntentOrderStatusUpdate} at each lifecycle stage.
|
|
18707
18784
|
* @throws If the `placeOrder` generator behaves unexpectedly, or if gas
|
|
@@ -18734,18 +18811,63 @@ var IntentGateway = class _IntentGateway {
|
|
|
18734
18811
|
}
|
|
18735
18812
|
const { order: finalizedOrder, receipt: placementReceipt } = placeOrderSecond.value;
|
|
18736
18813
|
yield { status: "ORDER_PLACED", order: finalizedOrder, receipt: placementReceipt };
|
|
18737
|
-
for await (const status of this.orderExecutor.
|
|
18814
|
+
for await (const status of this.orderExecutor.executeOrder({
|
|
18738
18815
|
order: finalizedOrder,
|
|
18739
18816
|
sessionPrivateKey,
|
|
18740
|
-
|
|
18741
|
-
|
|
18742
|
-
|
|
18743
|
-
solver: options?.solver
|
|
18817
|
+
auctionTimeMs: options.auctionTimeMs,
|
|
18818
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
18819
|
+
solver: options.solver
|
|
18744
18820
|
})) {
|
|
18745
18821
|
yield status;
|
|
18746
18822
|
}
|
|
18747
18823
|
return;
|
|
18748
18824
|
}
|
|
18825
|
+
/**
|
|
18826
|
+
* Validates that an order has the minimum fields required for post-placement
|
|
18827
|
+
* resume (i.e. it was previously placed and has an on-chain identity).
|
|
18828
|
+
*
|
|
18829
|
+
* @throws If `order.id` or `order.session` is missing or zero-valued.
|
|
18830
|
+
*/
|
|
18831
|
+
assertOrderCanResume(order) {
|
|
18832
|
+
if (!order.id) {
|
|
18833
|
+
throw new Error("Cannot resume execution without order.id");
|
|
18834
|
+
}
|
|
18835
|
+
if (!order.session || order.session === ADDRESS_ZERO2) {
|
|
18836
|
+
throw new Error("Cannot resume execution without order.session");
|
|
18837
|
+
}
|
|
18838
|
+
}
|
|
18839
|
+
/**
|
|
18840
|
+
* Resumes execution of a previously placed order.
|
|
18841
|
+
*
|
|
18842
|
+
* Use this method after an app restart or crash to pick up where
|
|
18843
|
+
* {@link execute} left off. The order must already be placed on-chain
|
|
18844
|
+
* (i.e. it must have a valid `id` and `session`).
|
|
18845
|
+
*
|
|
18846
|
+
* Internally delegates to {@link OrderExecutor.executeOrder} and
|
|
18847
|
+
* yields the same status updates as the execution phase of {@link execute}:
|
|
18848
|
+
* `AWAITING_BIDS`, `BIDS_RECEIVED`, `BID_SELECTED`,
|
|
18849
|
+
* `FILLED`, `PARTIAL_FILL`, `EXPIRED`, or `FAILED`.
|
|
18850
|
+
*
|
|
18851
|
+
* Callers may check {@link isOrderFilled} or {@link isOrderRefunded} before
|
|
18852
|
+
* calling this method to avoid resuming an already-terminal order.
|
|
18853
|
+
*
|
|
18854
|
+
* @param order - A previously placed order with a valid `id` and `session`.
|
|
18855
|
+
* @param options - Optional tuning parameters for bid collection and execution.
|
|
18856
|
+
* @yields {@link IntentOrderStatusUpdate} at each execution stage.
|
|
18857
|
+
* @throws If the order is missing required fields for resumption.
|
|
18858
|
+
*/
|
|
18859
|
+
async *resume(order, options) {
|
|
18860
|
+
this.assertOrderCanResume(order);
|
|
18861
|
+
for await (const status of this.orderExecutor.executeOrder({
|
|
18862
|
+
order,
|
|
18863
|
+
sessionPrivateKey: options.sessionPrivateKey,
|
|
18864
|
+
auctionTimeMs: options.auctionTimeMs,
|
|
18865
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
18866
|
+
solver: options.solver
|
|
18867
|
+
})) {
|
|
18868
|
+
yield status;
|
|
18869
|
+
}
|
|
18870
|
+
}
|
|
18749
18871
|
/**
|
|
18750
18872
|
* Returns both the native token cost and the relayer fee for cancelling an
|
|
18751
18873
|
* order. Use `relayerFee` to approve the ERC-20 spend before submitting.
|