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