@hyperbridge/sdk 2.8.2 → 2.8.4

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.
@@ -1,4 +1,4 @@
1
- export { br as AggregationLogger, bs as BidNonceKeyFn, bt as BidSignature, bu as ENTRY_POINT_V08_ADDRESS, bv as FILL_ORDER_ABI, bw as FetchLike, bx as FillData, by as HexString, bz as IntentGatewayV2, bA as LpBalance, bB as OrderCommitmentFn, bC as PhantomAggregation, bD as PhantomLegAggregation, bE as PhantomLegBidder, bF as RecoverBidSigner, bG as RpcBidInfo, bH as SolverBalanceReader, bI as YieldVaultMap, bJ as aggregatePhantomBids, ba as decodeAcceptedSourceChains, bb as decodeERC7821ExecuteBatch, bc as decodeUserOpScale, bd as encodeAcceptedSourceChains, be as encodeERC7821ExecuteBatch, bg as encodeUserOpScale, bK as extractFillData, bL as fetchBidsForOrder, bM as memoizedSolverBalance, bN as orderCommitmentFromDecoded, bO as recoverBidSignerViem, bP as setAggregationFetch, bQ as splitBidSignature, bR as weightedMedian, bS as zipFillLegs } from './intents-helpers-D_km9I2f.cjs';
1
+ export { bE as AggregationLogger, bF as BidNonceKeyFn, bG as BidSignature, bH as ENTRY_POINT_V08_ADDRESS, bI as FILL_ORDER_ABI, bJ as FetchLike, bK as FillData, bL as HexString, bM as IntentGatewayV2, bN as LpBalance, bO as OrderCommitmentFn, bP as PhantomAggregation, bQ as PhantomLegAggregation, bR as PhantomLegBidder, bS as RecoverBidSigner, bT as RpcBidInfo, bU as SolverBalanceReader, bV as YieldVaultMap, bW as aggregatePhantomBids, bi as decodeAcceptedSourceChains, bj as decodeERC7821ExecuteBatch, bk as decodePhantomBidDeclaration, bl as decodeUserOpScale, bn as encodeAcceptedSourceChains, bo as encodeERC7821ExecuteBatch, bq as encodePhantomBidDeclaration, br as encodeUserOpScale, bX as extractFillData, bY as fetchBidsForOrder, bZ as memoizedSolverBalance, b_ as orderCommitmentFromDecoded, bA as poolSlug, b$ as recoverBidSignerViem, c0 as setAggregationFetch, bB as sortPoolSymbols, c1 as splitBidSignature, c2 as weightedMedian, c3 as zipFillLegs } from './intents-helpers-CkPAsLHB.cjs';
2
2
  import 'consola';
3
3
  import 'decimal.js';
4
4
  import 'graphql-request';
@@ -1,4 +1,4 @@
1
- export { br as AggregationLogger, bs as BidNonceKeyFn, bt as BidSignature, bu as ENTRY_POINT_V08_ADDRESS, bv as FILL_ORDER_ABI, bw as FetchLike, bx as FillData, by as HexString, bz as IntentGatewayV2, bA as LpBalance, bB as OrderCommitmentFn, bC as PhantomAggregation, bD as PhantomLegAggregation, bE as PhantomLegBidder, bF as RecoverBidSigner, bG as RpcBidInfo, bH as SolverBalanceReader, bI as YieldVaultMap, bJ as aggregatePhantomBids, ba as decodeAcceptedSourceChains, bb as decodeERC7821ExecuteBatch, bc as decodeUserOpScale, bd as encodeAcceptedSourceChains, be as encodeERC7821ExecuteBatch, bg as encodeUserOpScale, bK as extractFillData, bL as fetchBidsForOrder, bM as memoizedSolverBalance, bN as orderCommitmentFromDecoded, bO as recoverBidSignerViem, bP as setAggregationFetch, bQ as splitBidSignature, bR as weightedMedian, bS as zipFillLegs } from './intents-helpers-D_km9I2f.js';
1
+ export { bE as AggregationLogger, bF as BidNonceKeyFn, bG as BidSignature, bH as ENTRY_POINT_V08_ADDRESS, bI as FILL_ORDER_ABI, bJ as FetchLike, bK as FillData, bL as HexString, bM as IntentGatewayV2, bN as LpBalance, bO as OrderCommitmentFn, bP as PhantomAggregation, bQ as PhantomLegAggregation, bR as PhantomLegBidder, bS as RecoverBidSigner, bT as RpcBidInfo, bU as SolverBalanceReader, bV as YieldVaultMap, bW as aggregatePhantomBids, bi as decodeAcceptedSourceChains, bj as decodeERC7821ExecuteBatch, bk as decodePhantomBidDeclaration, bl as decodeUserOpScale, bn as encodeAcceptedSourceChains, bo as encodeERC7821ExecuteBatch, bq as encodePhantomBidDeclaration, br as encodeUserOpScale, bX as extractFillData, bY as fetchBidsForOrder, bZ as memoizedSolverBalance, b_ as orderCommitmentFromDecoded, bA as poolSlug, b$ as recoverBidSignerViem, c0 as setAggregationFetch, bB as sortPoolSymbols, c1 as splitBidSignature, c2 as weightedMedian, c3 as zipFillLegs } from './intents-helpers-CkPAsLHB.js';
2
2
  import 'consola';
3
3
  import 'decimal.js';
4
4
  import 'graphql-request';
@@ -1937,6 +1937,14 @@ function decodeUserOpScale(hex) {
1937
1937
  signature: u8aToHex(new Uint8Array(decoded.signature))
1938
1938
  };
1939
1939
  }
1940
+
1941
+ // src/protocols/intents/liquidity-pool.ts
1942
+ function sortPoolSymbols(symbolA, symbolB) {
1943
+ return symbolA.toLowerCase() <= symbolB.toLowerCase() ? [symbolA, symbolB] : [symbolB, symbolA];
1944
+ }
1945
+ function poolSlug(symbolA, symbolB) {
1946
+ return sortPoolSymbols(symbolA, symbolB).join("-");
1947
+ }
1940
1948
  var SELECT_SOLVER_TYPEHASH = keccak256(toHex("SelectSolver(bytes32 commitment,address solver)"));
1941
1949
  var PACKED_USEROP_TYPEHASH = keccak256(
1942
1950
  toHex(
@@ -2039,6 +2047,12 @@ var CryptoUtils = class _CryptoUtils {
2039
2047
  * signing infrastructure (hardware wallets, MPC/TEE policy engines) instead
2040
2048
  * of an opaque 32-byte digest.
2041
2049
  *
2050
+ * The payload must be a standard self-describing `eth_signTypedData_v4`
2051
+ * payload — `EIP712Domain` listed in `types`, `chainId` as a JSON number —
2052
+ * because some signing backends (e.g. MPC Vault) hash it server-side from
2053
+ * the JSON rather than locally via viem. viem ignores both details when
2054
+ * hashing, so the digest is unchanged for local signers.
2055
+ *
2042
2056
  * @param userOp - The packed UserOperation to sign (signature field ignored).
2043
2057
  * @param entryPoint - Address of the EntryPoint v0.8 contract.
2044
2058
  * @param chainId - Chain ID of the network on which the operation will execute.
@@ -2049,10 +2063,22 @@ var CryptoUtils = class _CryptoUtils {
2049
2063
  domain: {
2050
2064
  name: "ERC4337",
2051
2065
  version: "1",
2052
- chainId,
2066
+ // Runtime number so JSON.stringify emits a canonical v4 numeric chainId for
2067
+ // server-side hashers; viem's uint256 type mapping wants bigint but its
2068
+ // runtime accepts numbers, hence the cast.
2069
+ chainId: Number(chainId),
2053
2070
  verifyingContract: entryPoint
2054
2071
  },
2072
+ // `as const`: viem derives the domain's TYPE from `types.EIP712Domain`, so the
2073
+ // entries must stay string literals — widened `string` fields make viem's
2074
+ // typed-data generics reject the payload at every call site.
2055
2075
  types: {
2076
+ EIP712Domain: [
2077
+ { name: "name", type: "string" },
2078
+ { name: "version", type: "string" },
2079
+ { name: "chainId", type: "uint256" },
2080
+ { name: "verifyingContract", type: "address" }
2081
+ ],
2056
2082
  PackedUserOperation: [
2057
2083
  { name: "sender", type: "address" },
2058
2084
  { name: "nonce", type: "uint256" },
@@ -2316,6 +2342,107 @@ var CryptoUtils = class _CryptoUtils {
2316
2342
  }
2317
2343
  };
2318
2344
 
2345
+ // src/protocols/intents/uniswap-v4-position.ts
2346
+ var Q96 = 1n << 96n;
2347
+ var MIN_TICK = -887272;
2348
+ var MAX_TICK = 887272;
2349
+ function word(data, index) {
2350
+ const hex = data.startsWith("0x") ? data.slice(2) : data;
2351
+ const start = index * 64;
2352
+ if (hex.length < start + 64) throw new Error(`return data too short for word ${index}`);
2353
+ return BigInt(`0x${hex.slice(start, start + 64)}`);
2354
+ }
2355
+ function asSigned(value, bits) {
2356
+ const width = 1n << BigInt(bits);
2357
+ const masked = value & width - 1n;
2358
+ return masked >= width >> 1n ? masked - width : masked;
2359
+ }
2360
+ function addressFromWord(data, index) {
2361
+ const hex = data.startsWith("0x") ? data.slice(2) : data;
2362
+ return `0x${hex.slice(index * 64 + 24, (index + 1) * 64)}`.toLowerCase();
2363
+ }
2364
+ function getSqrtRatioAtTick(tick) {
2365
+ if (!Number.isInteger(tick) || tick < MIN_TICK || tick > MAX_TICK) {
2366
+ throw new Error(`tick out of range: ${tick}`);
2367
+ }
2368
+ const absTick = BigInt(Math.abs(tick));
2369
+ let ratio = (absTick & 0x1n) !== 0n ? 0xfffcb933bd6fad37aa2d162d1a594001n : 0x100000000000000000000000000000000n;
2370
+ const factors = [
2371
+ [0x2n, 0xfff97272373d413259a46990580e213an],
2372
+ [0x4n, 0xfff2e50f5f656932ef12357cf3c7fdccn],
2373
+ [0x8n, 0xffe5caca7e10e4e61c3624eaa0941cd0n],
2374
+ [0x10n, 0xffcb9843d60f6159c9db58835c926644n],
2375
+ [0x20n, 0xff973b41fa98c081472e6896dfb254c0n],
2376
+ [0x40n, 0xff2ea16466c96a3843ec78b326b52861n],
2377
+ [0x80n, 0xfe5dee046a99a2a811c461f1969c3053n],
2378
+ [0x100n, 0xfcbe86c7900a88aedcffc83b479aa3a4n],
2379
+ [0x200n, 0xf987a7253ac413176f2b074cf7815e54n],
2380
+ [0x400n, 0xf3392b0822b70005940c7a398e4b70f3n],
2381
+ [0x800n, 0xe7159475a2c29b7443b29c7fa6e889d9n],
2382
+ [0x1000n, 0xd097f3bdfd2022b8845ad8f792aa5825n],
2383
+ [0x2000n, 0xa9f746462d870fdf8a65dc1f90e061e5n],
2384
+ [0x4000n, 0x70d869a156d2a1b890bb3df62baf32f7n],
2385
+ [0x8000n, 0x31be135f97d08fd981231505542fcfa6n],
2386
+ [0x10000n, 0x9aa508b5b7a84e1c677de54f3e99bc9n],
2387
+ [0x20000n, 0x5d6af8dedb81196699c329225ee604n],
2388
+ [0x40000n, 0x2216e584f5fa1ea926041bedfe98n],
2389
+ [0x80000n, 0x48a170391f7dc42444e8fa2n]
2390
+ ];
2391
+ for (const [bit, factor] of factors) {
2392
+ if ((absTick & bit) !== 0n) ratio = ratio * factor >> 128n;
2393
+ }
2394
+ if (tick > 0) ratio = (1n << 256n) / ratio;
2395
+ return (ratio >> 32n) + (ratio % (1n << 32n) === 0n ? 0n : 1n);
2396
+ }
2397
+ function positionTicks(info) {
2398
+ return {
2399
+ tickLower: Number(asSigned(info >> 8n, 24)),
2400
+ tickUpper: Number(asSigned(info >> 32n, 24))
2401
+ };
2402
+ }
2403
+ function getAmountsForLiquidity(params) {
2404
+ let { sqrtRatioAX96, sqrtRatioBX96 } = params;
2405
+ const { sqrtPriceX96, liquidity } = params;
2406
+ if (sqrtRatioAX96 > sqrtRatioBX96) [sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96];
2407
+ const amount0For = (from, to) => liquidity * Q96 * (to - from) / to / from;
2408
+ const amount1For = (from, to) => liquidity * (to - from) / Q96;
2409
+ if (sqrtPriceX96 <= sqrtRatioAX96) {
2410
+ return { amount0: amount0For(sqrtRatioAX96, sqrtRatioBX96), amount1: 0n };
2411
+ }
2412
+ if (sqrtPriceX96 < sqrtRatioBX96) {
2413
+ return {
2414
+ amount0: amount0For(sqrtPriceX96, sqrtRatioBX96),
2415
+ amount1: amount1For(sqrtRatioAX96, sqrtPriceX96)
2416
+ };
2417
+ }
2418
+ return { amount0: 0n, amount1: amount1For(sqrtRatioAX96, sqrtRatioBX96) };
2419
+ }
2420
+ function decodePoolAndPositionInfo(data) {
2421
+ const hex = data.startsWith("0x") ? data.slice(2) : data;
2422
+ if (hex.length < 6 * 64) throw new Error("getPoolAndPositionInfo returned too little data");
2423
+ const { tickLower, tickUpper } = positionTicks(word(data, 5));
2424
+ return {
2425
+ currency0: addressFromWord(data, 0),
2426
+ currency1: addressFromWord(data, 1),
2427
+ poolKeyEncoded: `0x${hex.slice(0, 5 * 64)}`,
2428
+ tickLower,
2429
+ tickUpper
2430
+ };
2431
+ }
2432
+ function positionAmountOfToken(params) {
2433
+ const { info, liquidity, sqrtPriceX96 } = params;
2434
+ const outputToken = params.outputToken.toLowerCase();
2435
+ if (outputToken !== info.currency0 && outputToken !== info.currency1) return 0n;
2436
+ if (liquidity <= 0n || sqrtPriceX96 <= 0n) return 0n;
2437
+ const { amount0, amount1 } = getAmountsForLiquidity({
2438
+ sqrtPriceX96,
2439
+ sqrtRatioAX96: getSqrtRatioAtTick(info.tickLower),
2440
+ sqrtRatioBX96: getSqrtRatioAtTick(info.tickUpper),
2441
+ liquidity
2442
+ });
2443
+ return outputToken === info.currency0 ? amount0 : amount1;
2444
+ }
2445
+
2319
2446
  // src/protocols/intents/phantom-aggregation.ts
2320
2447
  var ENTRY_POINT_V08_ADDRESS = "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108";
2321
2448
  var injectedFetch;
@@ -2329,6 +2456,14 @@ function rpcFetch() {
2329
2456
  }
2330
2457
  return f;
2331
2458
  }
2459
+ var PhantomRpcError = class extends Error {
2460
+ constructor(message, cause) {
2461
+ super(message);
2462
+ this.cause = cause;
2463
+ this.name = "PhantomRpcError";
2464
+ }
2465
+ cause;
2466
+ };
2332
2467
  async function rpcCall(url, payload) {
2333
2468
  let lastErr;
2334
2469
  for (let attempt = 0; attempt < 4; attempt++) {
@@ -2346,23 +2481,46 @@ async function rpcCall(url, payload) {
2346
2481
  }),
2347
2482
  timeout
2348
2483
  ]);
2349
- return await response.json();
2484
+ const body = await response.json();
2485
+ if (body?.error) {
2486
+ lastErr = new Error(`rpc error: ${JSON.stringify(body.error).slice(0, 200)}`);
2487
+ continue;
2488
+ }
2489
+ return body;
2350
2490
  } catch (err) {
2351
2491
  lastErr = err;
2352
2492
  } finally {
2353
2493
  if (timer) clearTimeout(timer);
2354
2494
  }
2355
2495
  }
2356
- throw lastErr;
2496
+ throw new PhantomRpcError(`RPC call failed after 4 attempts: ${url}`, lastErr);
2357
2497
  }
2358
2498
  var FILL_ORDER_ABI = IntentGatewayV2_default.ABI;
2359
- var DECLARATION_VERSION = 1;
2360
- var MAX_DECLARED_CHAINS = 255;
2361
- function encodeAcceptedSourceChains(chains) {
2362
- if (chains.length > MAX_DECLARED_CHAINS) {
2363
- throw new Error(`Cannot declare more than ${MAX_DECLARED_CHAINS} source chains`);
2499
+ var DECLARATION_V1 = 1;
2500
+ var DECLARATION_V2 = 2;
2501
+ var MAX_DECLARED_ENTRIES = 255;
2502
+ var MAX_TOKEN_ID_BYTES = 32;
2503
+ function tokenIdToBytes(tokenId) {
2504
+ if (tokenId < 0n) throw new Error(`Uniswap V4 tokenId cannot be negative: ${tokenId}`);
2505
+ const bytes = [];
2506
+ let rest = tokenId;
2507
+ while (rest > 0n) {
2508
+ bytes.unshift(Number(rest & 0xffn));
2509
+ rest >>= 8n;
2510
+ }
2511
+ return bytes.length > 0 ? bytes : [0];
2512
+ }
2513
+ function encodePhantomBidDeclaration(declaration) {
2514
+ const chains = declaration.acceptedSourceChains ?? [];
2515
+ const positions = declaration.uniswapV4Positions ?? [];
2516
+ if (chains.length > MAX_DECLARED_ENTRIES) {
2517
+ throw new Error(`Cannot declare more than ${MAX_DECLARED_ENTRIES} source chains`);
2518
+ }
2519
+ if (positions.length > MAX_DECLARED_ENTRIES) {
2520
+ throw new Error(`Cannot declare more than ${MAX_DECLARED_ENTRIES} Uniswap V4 positions`);
2364
2521
  }
2365
- const bytes = [DECLARATION_VERSION, chains.length];
2522
+ const version = positions.length > 0 ? DECLARATION_V2 : DECLARATION_V1;
2523
+ const bytes = [version, chains.length];
2366
2524
  for (const chain of chains) {
2367
2525
  const encoded = stringToU8a(chain);
2368
2526
  if (encoded.length === 0 || encoded.length > 255) {
@@ -2370,25 +2528,59 @@ function encodeAcceptedSourceChains(chains) {
2370
2528
  }
2371
2529
  bytes.push(encoded.length, ...encoded);
2372
2530
  }
2531
+ if (version === DECLARATION_V2) {
2532
+ bytes.push(positions.length);
2533
+ for (const tokenId of positions) {
2534
+ const encoded = tokenIdToBytes(tokenId);
2535
+ if (encoded.length > MAX_TOKEN_ID_BYTES) {
2536
+ throw new Error(`Uniswap V4 tokenId exceeds uint256: ${tokenId}`);
2537
+ }
2538
+ bytes.push(encoded.length, ...encoded);
2539
+ }
2540
+ }
2373
2541
  return u8aToHex(new Uint8Array(bytes));
2374
2542
  }
2375
- function decodeAcceptedSourceChains(paymasterAndData) {
2376
- if (!paymasterAndData || !isHex(paymasterAndData)) return null;
2543
+ function decodePhantomBidDeclaration(paymasterAndData) {
2544
+ const absent = { acceptedSources: null, uniswapV4Positions: [] };
2545
+ if (!paymasterAndData || !isHex(paymasterAndData)) return absent;
2377
2546
  const bytes = hexToU8a(paymasterAndData);
2378
- if (bytes.length < 2 || bytes[0] !== DECLARATION_VERSION) return null;
2379
- const count = bytes[1];
2547
+ if (bytes.length < 2) return absent;
2548
+ const version = bytes[0];
2549
+ if (version !== DECLARATION_V1 && version !== DECLARATION_V2) return absent;
2380
2550
  const chains = [];
2381
2551
  let offset = 2;
2382
- for (let entry = 0; entry < count; entry++) {
2383
- if (offset >= bytes.length) return null;
2552
+ for (let entry = 0; entry < bytes[1]; entry++) {
2553
+ if (offset >= bytes.length) return absent;
2384
2554
  const length = bytes[offset];
2385
2555
  offset += 1;
2386
- if (length === 0 || offset + length > bytes.length) return null;
2556
+ if (length === 0 || offset + length > bytes.length) return absent;
2387
2557
  chains.push(u8aToString(bytes.subarray(offset, offset + length)));
2388
2558
  offset += length;
2389
2559
  }
2390
- if (offset !== bytes.length) return null;
2391
- return chains;
2560
+ const positions = [];
2561
+ if (version === DECLARATION_V2) {
2562
+ if (offset >= bytes.length) return absent;
2563
+ const count = bytes[offset];
2564
+ offset += 1;
2565
+ for (let entry = 0; entry < count; entry++) {
2566
+ if (offset >= bytes.length) return absent;
2567
+ const length = bytes[offset];
2568
+ offset += 1;
2569
+ if (length === 0 || length > MAX_TOKEN_ID_BYTES || offset + length > bytes.length) return absent;
2570
+ let tokenId = 0n;
2571
+ for (const byte of bytes.subarray(offset, offset + length)) tokenId = tokenId << 8n | BigInt(byte);
2572
+ positions.push(tokenId);
2573
+ offset += length;
2574
+ }
2575
+ }
2576
+ if (offset !== bytes.length) return absent;
2577
+ return { acceptedSources: chains, uniswapV4Positions: positions };
2578
+ }
2579
+ function encodeAcceptedSourceChains(chains) {
2580
+ return encodePhantomBidDeclaration({ acceptedSourceChains: chains });
2581
+ }
2582
+ function decodeAcceptedSourceChains(paymasterAndData) {
2583
+ return decodePhantomBidDeclaration(paymasterAndData).acceptedSources;
2392
2584
  }
2393
2585
  function zipFillLegs(assets, outputs) {
2394
2586
  return assets.map((asset, index) => {
@@ -2473,17 +2665,46 @@ async function isDelegatedToSolverAccount(evmRpcUrl, account, solverAccount) {
2473
2665
  method: "eth_getCode",
2474
2666
  params: [account, "latest"]
2475
2667
  });
2476
- const code = typeof response.result === "string" ? response.result.toLowerCase() : "";
2668
+ if (typeof response.result !== "string") {
2669
+ throw new PhantomRpcError(`eth_getCode returned no code for ${account} on ${evmRpcUrl}`);
2670
+ }
2671
+ const code = response.result.toLowerCase();
2477
2672
  if (!code.startsWith(DELEGATION_INDICATOR_PREFIX)) return false;
2478
2673
  return `0x${code.slice(DELEGATION_INDICATOR_PREFIX.length)}` === solverAccount.toLowerCase();
2479
2674
  }
2675
+ function memoizedDelegationCheck() {
2676
+ const cache = /* @__PURE__ */ new Map();
2677
+ return (evmRpcUrl, account, solverAccount) => {
2678
+ const key = `${evmRpcUrl}|${account.toLowerCase()}|${solverAccount.toLowerCase()}`;
2679
+ let pending = cache.get(key);
2680
+ if (!pending) {
2681
+ pending = isDelegatedToSolverAccount(evmRpcUrl, account, solverAccount).catch((err) => {
2682
+ cache.delete(key);
2683
+ throw err;
2684
+ });
2685
+ cache.set(key, pending);
2686
+ }
2687
+ return pending;
2688
+ };
2689
+ }
2480
2690
  function evmChainId(chain) {
2481
2691
  const [prefix, id] = chain.split("-");
2482
2692
  if (prefix !== "EVM" || !id || !/^\d+$/.test(id)) return null;
2483
2693
  return BigInt(id);
2484
2694
  }
2485
2695
  async function isVerifiedSolverBid(params) {
2486
- const { userOp, commitment, sessionKey, chainId, solverAccount, evmRpcUrl, recoverSigner, bidNonceKey, logger } = params;
2696
+ const {
2697
+ userOp,
2698
+ commitment,
2699
+ sessionKey,
2700
+ chainId,
2701
+ solverAccount,
2702
+ evmRpcUrl,
2703
+ recoverSigner,
2704
+ bidNonceKey,
2705
+ isDelegated,
2706
+ logger
2707
+ } = params;
2487
2708
  const solver = userOp.sender;
2488
2709
  const parsed = splitBidSignature(userOp.signature);
2489
2710
  if (!parsed) {
@@ -2506,7 +2727,7 @@ async function isVerifiedSolverBid(params) {
2506
2727
  logger?.warn({ solver, commitment, signer }, "Rejecting phantom bid: signature does not recover to the sender");
2507
2728
  return false;
2508
2729
  }
2509
- if (!await isDelegatedToSolverAccount(evmRpcUrl, solver, solverAccount)) {
2730
+ if (!await isDelegated(evmRpcUrl, solver, solverAccount)) {
2510
2731
  logger?.warn({ solver, commitment, solverAccount }, "Rejecting phantom bid: sender is not a delegated solver");
2511
2732
  return false;
2512
2733
  }
@@ -2522,18 +2743,17 @@ async function fetchBidsForOrder(nodeUrl, commitment) {
2522
2743
  return Array.isArray(data.result) ? data.result : [];
2523
2744
  }
2524
2745
  async function ethCallUint(evmRpcUrl, to, data) {
2525
- try {
2526
- const result = await rpcCall(evmRpcUrl, {
2527
- id: 1,
2528
- jsonrpc: "2.0",
2529
- method: "eth_call",
2530
- params: [{ to, data }, "latest"]
2531
- });
2532
- if (result.error || !result.result || result.result === "0x") return 0n;
2533
- return BigInt(result.result);
2534
- } catch {
2535
- return 0n;
2746
+ const result = await rpcCall(evmRpcUrl, {
2747
+ id: 1,
2748
+ jsonrpc: "2.0",
2749
+ method: "eth_call",
2750
+ params: [{ to, data }, "latest"]
2751
+ });
2752
+ if (result.result === "0x") return 0n;
2753
+ if (typeof result.result !== "string") {
2754
+ throw new PhantomRpcError(`eth_call returned no result for ${to} on ${evmRpcUrl}`);
2536
2755
  }
2756
+ return BigInt(result.result);
2537
2757
  }
2538
2758
  async function getTotalSolverBalance(evmRpcUrl, chain, token, solver, yieldVaults) {
2539
2759
  const padded = solver.replace("0x", "").padStart(64, "0");
@@ -2545,27 +2765,104 @@ async function getTotalSolverBalance(evmRpcUrl, chain, token, solver, yieldVault
2545
2765
  );
2546
2766
  return vaultBalances.reduce((acc, b) => acc + b, raw);
2547
2767
  }
2768
+ var SELECTOR_OWNER_OF = "0x6352211e";
2769
+ var SELECTOR_POSITION_LIQUIDITY = "0x1efeed33";
2770
+ var SELECTOR_POOL_AND_POSITION_INFO = "0x7ba03aad";
2771
+ var SELECTOR_GET_SLOT0 = "0xc815641c";
2772
+ var uint256Arg = (value) => value.toString(16).padStart(64, "0");
2773
+ async function readV4Position(evmRpcUrl, contracts, tokenId, keccak, logger) {
2774
+ const call = async (to, data) => {
2775
+ const result = await rpcCall(evmRpcUrl, {
2776
+ id: 1,
2777
+ jsonrpc: "2.0",
2778
+ method: "eth_call",
2779
+ params: [{ to, data }, "latest"]
2780
+ });
2781
+ if (result.result === "0x") return null;
2782
+ if (typeof result.result !== "string") {
2783
+ throw new PhantomRpcError(`eth_call returned no result for ${to} on ${evmRpcUrl}`);
2784
+ }
2785
+ return result.result;
2786
+ };
2787
+ const arg = uint256Arg(tokenId);
2788
+ const [ownerData, infoData, liquidityData] = await Promise.all([
2789
+ call(contracts.positionManager, `${SELECTOR_OWNER_OF}${arg}`),
2790
+ call(contracts.positionManager, `${SELECTOR_POOL_AND_POSITION_INFO}${arg}`),
2791
+ call(contracts.positionManager, `${SELECTOR_POSITION_LIQUIDITY}${arg}`)
2792
+ ]);
2793
+ if (!ownerData) return null;
2794
+ if (!infoData || !liquidityData) {
2795
+ logger?.warn(
2796
+ { tokenId: tokenId.toString(), positionManager: contracts.positionManager },
2797
+ "Uniswap V4 position exists but its pool info or liquidity did not read back \u2014 check the configured PositionManager"
2798
+ );
2799
+ return null;
2800
+ }
2801
+ const info = decodePoolAndPositionInfo(infoData);
2802
+ const slot0Data = await call(contracts.stateView, `${SELECTOR_GET_SLOT0}${keccak(info.poolKeyEncoded).slice(2)}`);
2803
+ if (!slot0Data) {
2804
+ logger?.warn(
2805
+ { tokenId: tokenId.toString(), stateView: contracts.stateView, evmRpcUrl },
2806
+ "Uniswap V4 slot0 read returned nothing for a live position \u2014 the configured StateView address is wrong"
2807
+ );
2808
+ return null;
2809
+ }
2810
+ return {
2811
+ owner: `0x${ownerData.slice(-40)}`.toLowerCase(),
2812
+ info,
2813
+ liquidity: word(liquidityData, 0),
2814
+ sqrtPriceX96: word(slot0Data, 0)
2815
+ };
2816
+ }
2817
+ function memoizedV4Position(keccak, logger) {
2818
+ const cache = /* @__PURE__ */ new Map();
2819
+ return (evmRpcUrl, chain, contracts, tokenId) => {
2820
+ const key = `${chain}|${tokenId}`;
2821
+ let pending = cache.get(key);
2822
+ if (!pending) {
2823
+ pending = readV4Position(evmRpcUrl, contracts, tokenId, keccak, logger).catch((err) => {
2824
+ cache.delete(key);
2825
+ throw err;
2826
+ });
2827
+ cache.set(key, pending);
2828
+ }
2829
+ return pending;
2830
+ };
2831
+ }
2548
2832
  function memoizedSolverBalance(yieldVaults) {
2549
2833
  const cache = /* @__PURE__ */ new Map();
2550
2834
  return (evmRpcUrl, chain, token, solver) => {
2551
2835
  const key = `${chain}|${token.toLowerCase()}|${solver.toLowerCase()}`;
2552
2836
  let pending = cache.get(key);
2553
2837
  if (!pending) {
2554
- pending = getTotalSolverBalance(evmRpcUrl, chain, token, solver, yieldVaults);
2838
+ pending = getTotalSolverBalance(evmRpcUrl, chain, token, solver, yieldVaults).catch((err) => {
2839
+ cache.delete(key);
2840
+ throw err;
2841
+ });
2555
2842
  cache.set(key, pending);
2556
2843
  }
2557
2844
  return pending;
2558
2845
  };
2559
2846
  }
2560
- async function sweepSolverLiquidity(evmRpcUrls, yieldVaults, solver, getBalance) {
2847
+ async function sweepSolverLiquidity(evmRpcUrls, yieldVaults, solver, getBalance, positions) {
2561
2848
  const balances = [];
2562
2849
  for (const [chain, tokens] of Object.entries(yieldVaults)) {
2563
2850
  const url = evmRpcUrls[chain];
2564
2851
  if (!url) continue;
2565
2852
  for (const token of Object.keys(tokens)) {
2566
2853
  const balance = await getBalance(url, chain, token, solver);
2567
- if (balance === 0n) continue;
2568
- balances.push({ solver, chain, tokenAddress: token, balance });
2854
+ const uniswapV4Balance = chain === positions.chain ? positions.states.reduce(
2855
+ (total2, state) => total2 + positionAmountOfToken({
2856
+ info: state.info,
2857
+ liquidity: state.liquidity,
2858
+ sqrtPriceX96: state.sqrtPriceX96,
2859
+ outputToken: token
2860
+ }),
2861
+ 0n
2862
+ ) : 0n;
2863
+ const total = balance + uniswapV4Balance;
2864
+ if (total === 0n) continue;
2865
+ balances.push({ solver, chain, tokenAddress: token, balance: total });
2569
2866
  }
2570
2867
  }
2571
2868
  return balances;
@@ -2576,6 +2873,24 @@ function toAddress(token) {
2576
2873
  return `0x${addr}`;
2577
2874
  }
2578
2875
  async function aggregatePhantomBids(params) {
2876
+ const isDelegated = memoizedDelegationCheck();
2877
+ let lastErr;
2878
+ for (let attempt = 1; attempt <= AGGREGATION_ATTEMPTS; attempt++) {
2879
+ try {
2880
+ return await runAggregation(params, isDelegated);
2881
+ } catch (err) {
2882
+ lastErr = err;
2883
+ params.logger?.warn(
2884
+ { err, commitment: params.commitment, chain: params.chain, attempt, of: AGGREGATION_ATTEMPTS },
2885
+ "Phantom aggregation attempt failed"
2886
+ );
2887
+ if (attempt < AGGREGATION_ATTEMPTS) await new Promise((resolve) => setTimeout(resolve, 250 * attempt));
2888
+ }
2889
+ }
2890
+ throw lastErr;
2891
+ }
2892
+ var AGGREGATION_ATTEMPTS = 5;
2893
+ async function runAggregation(params, isDelegated) {
2579
2894
  const { nodeUrl, evmRpcUrls, chain, gatewayAddress, commitment, yieldVaults, solverAccount, logger } = params;
2580
2895
  const extractFill = params.extractFill ?? extractFillData;
2581
2896
  const recoverSigner = params.recoverSigner ?? recoverBidSignerViem;
@@ -2590,6 +2905,8 @@ async function aggregatePhantomBids(params) {
2590
2905
  }
2591
2906
  const bids = await fetchBidsForOrder(nodeUrl, commitment);
2592
2907
  if (bids.length === 0) return null;
2908
+ const v4Contracts = params.uniswapV4?.[chain];
2909
+ const readPosition = memoizedV4Position(params.keccak ?? keccak256, logger);
2593
2910
  const getBalance = params.getBalance ?? memoizedSolverBalance(yieldVaults);
2594
2911
  const quotesByLeg = /* @__PURE__ */ new Map();
2595
2912
  const lpBalances = [];
@@ -2620,6 +2937,7 @@ async function aggregatePhantomBids(params) {
2620
2937
  evmRpcUrl: destUrl,
2621
2938
  recoverSigner,
2622
2939
  bidNonceKey,
2940
+ isDelegated,
2623
2941
  logger
2624
2942
  });
2625
2943
  if (!verified) continue;
@@ -2629,12 +2947,39 @@ async function aggregatePhantomBids(params) {
2629
2947
  continue;
2630
2948
  }
2631
2949
  countedSolvers.add(normalizedSolver);
2632
- const acceptedSources = decodeAcceptedSourceChains(decoded.paymasterAndData);
2950
+ const declaration = decodePhantomBidDeclaration(decoded.paymasterAndData);
2951
+ const acceptedSources = declaration.acceptedSources;
2633
2952
  const quotedLegs = [...fillData.legs.entries()].filter(([, leg]) => leg.solverAmount !== 0n);
2953
+ const declaredPositions = v4Contracts ? declaration.uniswapV4Positions : [];
2954
+ const positions = (await Promise.all(
2955
+ declaredPositions.map((tokenId) => readPosition(destUrl, chain, v4Contracts, tokenId))
2956
+ )).filter((state, index) => {
2957
+ if (!state) return false;
2958
+ if (state.owner !== normalizedSolver) {
2959
+ logger?.warn(
2960
+ { solver, commitment, tokenId: declaredPositions[index].toString(), owner: state.owner },
2961
+ "Ignoring declared Uniswap V4 position: not owned by the bidding solver"
2962
+ );
2963
+ return false;
2964
+ }
2965
+ return true;
2966
+ });
2634
2967
  const weights = await Promise.all(
2635
2968
  // Price influence: the solver's liquidity in THIS leg's output token on the destination
2636
2969
  // chain, so a leg is weighted by the inventory that actually backs it.
2637
- quotedLegs.map(([, leg]) => getBalance(destUrl, chain, toAddress(leg.outputToken), solver))
2970
+ quotedLegs.map(async ([, leg]) => {
2971
+ const outputToken = toAddress(leg.outputToken);
2972
+ const balance = await getBalance(destUrl, chain, outputToken, solver);
2973
+ return positions.reduce(
2974
+ (total, state) => total + positionAmountOfToken({
2975
+ info: state.info,
2976
+ liquidity: state.liquidity,
2977
+ sqrtPriceX96: state.sqrtPriceX96,
2978
+ outputToken
2979
+ }),
2980
+ balance
2981
+ );
2982
+ })
2638
2983
  );
2639
2984
  for (const [position, [legIndex, leg]] of quotedLegs.entries()) {
2640
2985
  const weight = weights[position];
@@ -2643,8 +2988,14 @@ async function aggregatePhantomBids(params) {
2643
2988
  entry.bidders.push({ solver: normalizedSolver, weight, acceptedSources });
2644
2989
  quotesByLeg.set(legIndex, entry);
2645
2990
  }
2646
- lpBalances.push(...await sweepSolverLiquidity(evmRpcUrls, yieldVaults, solver, getBalance));
2991
+ lpBalances.push(
2992
+ ...await sweepSolverLiquidity(evmRpcUrls, yieldVaults, solver, getBalance, {
2993
+ chain,
2994
+ states: positions
2995
+ })
2996
+ );
2647
2997
  } catch (err) {
2998
+ if (err instanceof PhantomRpcError) throw err;
2648
2999
  logger?.warn({ err, filler: bid.filler }, "Failed to process bid for price snapshot");
2649
3000
  }
2650
3001
  }
@@ -2675,6 +3026,6 @@ async function aggregatePhantomBids(params) {
2675
3026
  return { legs, lpBalances };
2676
3027
  }
2677
3028
 
2678
- export { ENTRY_POINT_V08_ADDRESS, FILL_ORDER_ABI, IntentGatewayV2_default as IntentGatewayV2, aggregatePhantomBids, decodeAcceptedSourceChains, decodeERC7821ExecuteBatch, decodeUserOpScale, encodeAcceptedSourceChains, encodeERC7821ExecuteBatch, encodeUserOpScale, extractFillData, fetchBidsForOrder, memoizedSolverBalance, orderCommitmentFromDecoded, recoverBidSignerViem, setAggregationFetch, splitBidSignature, weightedMedian, zipFillLegs };
3029
+ export { ENTRY_POINT_V08_ADDRESS, FILL_ORDER_ABI, IntentGatewayV2_default as IntentGatewayV2, aggregatePhantomBids, decodeAcceptedSourceChains, decodeERC7821ExecuteBatch, decodePhantomBidDeclaration, decodeUserOpScale, encodeAcceptedSourceChains, encodeERC7821ExecuteBatch, encodePhantomBidDeclaration, encodeUserOpScale, extractFillData, fetchBidsForOrder, memoizedSolverBalance, orderCommitmentFromDecoded, poolSlug, recoverBidSignerViem, setAggregationFetch, sortPoolSymbols, splitBidSignature, weightedMedian, zipFillLegs };
2679
3030
  //# sourceMappingURL=intents-helpers.js.map
2680
3031
  //# sourceMappingURL=intents-helpers.js.map