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