@hyperbridge/sdk 2.4.2 → 2.6.0
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 +109 -14
- package/dist/browser/index.js +721 -131
- package/dist/browser/index.js.map +1 -1
- package/dist/node/{IntentGatewayV2-PzbhFn_7.d.cts → IntentGatewayV2-DuSwlT02.d.cts} +80 -5
- package/dist/node/{IntentGatewayV2-PzbhFn_7.d.ts → IntentGatewayV2-DuSwlT02.d.ts} +80 -5
- package/dist/node/index.cjs +720 -129
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.cts +32 -12
- package/dist/node/index.d.ts +32 -12
- package/dist/node/index.js +721 -131
- package/dist/node/index.js.map +1 -1
- package/dist/node/intents-helpers.cjs +518 -7
- package/dist/node/intents-helpers.cjs.map +1 -1
- package/dist/node/intents-helpers.d.cts +52 -10
- package/dist/node/intents-helpers.d.ts +52 -10
- package/dist/node/intents-helpers.js +516 -9
- package/dist/node/intents-helpers.js.map +1 -1
- package/package.json +1 -1
package/dist/node/index.cjs
CHANGED
|
@@ -2689,7 +2689,11 @@ var chainConfigs = {
|
|
|
2689
2689
|
DAI: "0x6b175474e89094c44da98b954eedeac495271d0f",
|
|
2690
2690
|
USDC: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
|
|
2691
2691
|
USDT: "0xdac17f958d2ee523a2206206994597c13d831ec7",
|
|
2692
|
-
cNGN: "0x17CDB2a01e7a34CbB3DD4b83260B05d0274C8dab"
|
|
2692
|
+
cNGN: "0x17CDB2a01e7a34CbB3DD4b83260B05d0274C8dab",
|
|
2693
|
+
ZARP: "0xb755506531786C8aC63B756BaB1ac387bACB0C04",
|
|
2694
|
+
EURC: "0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c",
|
|
2695
|
+
XSGD: "0x70e8dE73cE538DA2bEEd35d14187F6959a8ecA96",
|
|
2696
|
+
TRYB: "0x2C537E5624e4af88A7ae4060C022609376C8D0EB"
|
|
2693
2697
|
},
|
|
2694
2698
|
tokenDecimals: {
|
|
2695
2699
|
USDC: 6,
|
|
@@ -2720,6 +2724,7 @@ var chainConfigs = {
|
|
|
2720
2724
|
Permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
|
|
2721
2725
|
EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
|
|
2722
2726
|
CirclePaymaster: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec",
|
|
2727
|
+
SimplexPaymaster: "0xD4340d7466e040626383cb9cda9307ba8E081149",
|
|
2723
2728
|
Usdt0Oft: "0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee"
|
|
2724
2729
|
},
|
|
2725
2730
|
rpcEnvKey: "ETH_MAINNET",
|
|
@@ -2773,7 +2778,8 @@ var chainConfigs = {
|
|
|
2773
2778
|
UniswapV4StateView: "0xd13dd3d6e93f276fafc9db9e6bb47c1180aee0c4",
|
|
2774
2779
|
Calldispatcher: "0xc71251c8b3e7b02697a84363eef6dce8dfbdf333",
|
|
2775
2780
|
Permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
|
|
2776
|
-
EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108"
|
|
2781
|
+
EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
|
|
2782
|
+
SimplexPaymaster: "0xeD02f9f0df8F562B89cC5b25867Ad3C2d61252A9"
|
|
2777
2783
|
// "Usdt0Oft": Not available on BSC
|
|
2778
2784
|
},
|
|
2779
2785
|
rpcEnvKey: "BSC_MAINNET",
|
|
@@ -2831,6 +2837,7 @@ var chainConfigs = {
|
|
|
2831
2837
|
Permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
|
|
2832
2838
|
EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
|
|
2833
2839
|
CirclePaymaster: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec",
|
|
2840
|
+
SimplexPaymaster: "0x7281Bccb4f0BCE44F3B8542d1fC5e51c2F5fC08C",
|
|
2834
2841
|
Usdt0Oft: "0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92"
|
|
2835
2842
|
},
|
|
2836
2843
|
rpcEnvKey: "ARBITRUM_MAINNET",
|
|
@@ -2856,7 +2863,9 @@ var chainConfigs = {
|
|
|
2856
2863
|
USDC: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
|
|
2857
2864
|
USDT: "0xfde4c96c8593536e31f229ea8f37b2ada2699bb2",
|
|
2858
2865
|
EXT: "0x0e668E5127087e236578893a0e01E41837A28469",
|
|
2859
|
-
cNGN: "0x46C85152bFe9f96829aA94755D9f915F9B10EF5F"
|
|
2866
|
+
cNGN: "0x46C85152bFe9f96829aA94755D9f915F9B10EF5F",
|
|
2867
|
+
ZARP: "0xb755506531786C8aC63B756BaB1ac387bACB0C04",
|
|
2868
|
+
EURC: "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"
|
|
2860
2869
|
},
|
|
2861
2870
|
tokenDecimals: {
|
|
2862
2871
|
USDC: 6,
|
|
@@ -2885,6 +2894,7 @@ var chainConfigs = {
|
|
|
2885
2894
|
Permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
|
|
2886
2895
|
EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
|
|
2887
2896
|
CirclePaymaster: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec",
|
|
2897
|
+
SimplexPaymaster: "0x15b3B03C870c7ef252029c35A12d3b339F5c8d7f",
|
|
2888
2898
|
AerodromeRouter: "0xcF77a3Ba9A5CA399B7c97c74d54e5b1Beb874E43",
|
|
2889
2899
|
UniswapV4PositionManager: "0x7c5f5a4bbd8fd63184577525326123b519429bdc",
|
|
2890
2900
|
UniswapV4PoolManager: "0x498581ff718922c3f8e6a244956af099b2652b2b",
|
|
@@ -2915,7 +2925,9 @@ var chainConfigs = {
|
|
|
2915
2925
|
USDC: "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
|
|
2916
2926
|
USDT: "0xc2132d05d31c914a87c6611c10748aeb04b58e8f",
|
|
2917
2927
|
EXT: "0x7C8c11ADb8EF7cd3CFa718008Ea048445C6E7209",
|
|
2918
|
-
cNGN: "0x52828daa48C1a9A06F37500882b42daf0bE04C3B"
|
|
2928
|
+
cNGN: "0x52828daa48C1a9A06F37500882b42daf0bE04C3B",
|
|
2929
|
+
ZARP: "0xb755506531786C8aC63B756BaB1ac387bACB0C04",
|
|
2930
|
+
XSGD: "0xDC3326e71D45186F113a2F448984CA0e8D201995"
|
|
2919
2931
|
},
|
|
2920
2932
|
tokenDecimals: {
|
|
2921
2933
|
USDC: 6,
|
|
@@ -2947,6 +2959,7 @@ var chainConfigs = {
|
|
|
2947
2959
|
Permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
|
|
2948
2960
|
EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
|
|
2949
2961
|
CirclePaymaster: "0x0578cFB241215b77442a541325d6A4E6dFE700Ec",
|
|
2962
|
+
SimplexPaymaster: "0xe99acFe0f5fC4C8ea54A187D8D3b05f136150095",
|
|
2950
2963
|
Usdt0Oft: "0x6BA10300f0DC58B7a1e4c0e41f5daBb7D7829e13"
|
|
2951
2964
|
},
|
|
2952
2965
|
rpcEnvKey: "POLYGON_MAINNET",
|
|
@@ -5151,6 +5164,21 @@ var ChainConfigService = class {
|
|
|
5151
5164
|
getCNgnAsset(chain) {
|
|
5152
5165
|
return this.getConfig(chain)?.assets?.cNGN;
|
|
5153
5166
|
}
|
|
5167
|
+
/**
|
|
5168
|
+
* Address of `symbol` on `chain` from the per-chain asset table, matched
|
|
5169
|
+
* case-insensitively ("cNGN" ≡ "CNGN"). This table is the single source of
|
|
5170
|
+
* truth for token addresses — the simplex asset registry resolves through
|
|
5171
|
+
* it, so a new asset is added once in `chain.ts` and nowhere else.
|
|
5172
|
+
*/
|
|
5173
|
+
getAssetBySymbol(chain, symbol) {
|
|
5174
|
+
const assets = this.getConfig(chain)?.assets;
|
|
5175
|
+
if (!assets) return void 0;
|
|
5176
|
+
const target = symbol.trim().toUpperCase();
|
|
5177
|
+
for (const [key, address] of Object.entries(assets)) {
|
|
5178
|
+
if (key.toUpperCase() === target) return address;
|
|
5179
|
+
}
|
|
5180
|
+
return void 0;
|
|
5181
|
+
}
|
|
5154
5182
|
getCNgnDecimals(chain) {
|
|
5155
5183
|
return this.getConfig(chain)?.tokenDecimals?.cNGN;
|
|
5156
5184
|
}
|
|
@@ -5242,6 +5270,9 @@ var ChainConfigService = class {
|
|
|
5242
5270
|
getCirclePaymasterAddress(chain) {
|
|
5243
5271
|
return this.getConfig(chain)?.addresses.CirclePaymaster;
|
|
5244
5272
|
}
|
|
5273
|
+
getSimplexPaymasterAddress(chain) {
|
|
5274
|
+
return this.getConfig(chain)?.addresses.SimplexPaymaster;
|
|
5275
|
+
}
|
|
5245
5276
|
getHyperbridgeAddress() {
|
|
5246
5277
|
return hyperbridgeAddress;
|
|
5247
5278
|
}
|
|
@@ -7062,6 +7093,16 @@ var ExpectedError = class _ExpectedError extends Error {
|
|
|
7062
7093
|
return error instanceof _ExpectedError;
|
|
7063
7094
|
}
|
|
7064
7095
|
};
|
|
7096
|
+
var MISSING_CONSENSUS_UPDATE_TIME_MESSAGE = "Consensus update time not found";
|
|
7097
|
+
var MissingConsensusUpdateTimeError = class _MissingConsensusUpdateTimeError extends Error {
|
|
7098
|
+
constructor(message = MISSING_CONSENSUS_UPDATE_TIME_MESSAGE, options) {
|
|
7099
|
+
super(message, options);
|
|
7100
|
+
this.name = "Hyperbridge/SDK/MissingConsensusUpdateTimeError";
|
|
7101
|
+
}
|
|
7102
|
+
static isError(error) {
|
|
7103
|
+
return error instanceof _MissingConsensusUpdateTimeError || error instanceof Error && error.message.toLowerCase().includes(MISSING_CONSENSUS_UPDATE_TIME_MESSAGE.toLowerCase());
|
|
7104
|
+
}
|
|
7105
|
+
};
|
|
7065
7106
|
|
|
7066
7107
|
// src/configs/constants.ts
|
|
7067
7108
|
var ISMP_PREFIX = ":child_storage:default:ISMPv2";
|
|
@@ -7375,8 +7416,15 @@ var SubstrateChain = class _SubstrateChain {
|
|
|
7375
7416
|
id: stateMachineId,
|
|
7376
7417
|
height: Number(stateMachineHeight.height)
|
|
7377
7418
|
};
|
|
7378
|
-
|
|
7379
|
-
|
|
7419
|
+
try {
|
|
7420
|
+
const updateTime = await this.rpcClient.call("ismp_queryStateMachineUpdateTime", [payload]);
|
|
7421
|
+
return BigInt(updateTime);
|
|
7422
|
+
} catch (error) {
|
|
7423
|
+
if (MissingConsensusUpdateTimeError.isError(error)) {
|
|
7424
|
+
throw new MissingConsensusUpdateTimeError(MISSING_CONSENSUS_UPDATE_TIME_MESSAGE, { cause: error });
|
|
7425
|
+
}
|
|
7426
|
+
throw error;
|
|
7427
|
+
}
|
|
7380
7428
|
}
|
|
7381
7429
|
/**
|
|
7382
7430
|
* Get the challenge period for a given state machine id.
|
|
@@ -8122,26 +8170,76 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8122
8170
|
};
|
|
8123
8171
|
}
|
|
8124
8172
|
/**
|
|
8125
|
-
*
|
|
8126
|
-
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
const
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
|
|
8141
|
-
|
|
8173
|
+
* Reads the PhantomOrderRegistered events emitted in a single block.
|
|
8174
|
+
*/
|
|
8175
|
+
async getPhantomOrdersInBlock(blockNumber) {
|
|
8176
|
+
const blockHash = await this.api.rpc.chain.getBlockHash(blockNumber);
|
|
8177
|
+
const apiAt = await this.api.at(blockHash);
|
|
8178
|
+
const records = await apiAt.query.system.events();
|
|
8179
|
+
const orders = [];
|
|
8180
|
+
for (const { event } of records) {
|
|
8181
|
+
if (event.section !== "intentsCoprocessor" || event.method !== "PhantomOrderRegistered") continue;
|
|
8182
|
+
const [commitment, chain, createdAt, tokenA, tokenB, standardAmount] = event.data;
|
|
8183
|
+
orders.push({
|
|
8184
|
+
commitment: commitment.toHex(),
|
|
8185
|
+
chain: new TextDecoder().decode(util.hexToU8a(chain.toHex())),
|
|
8186
|
+
createdAt: createdAt.toNumber(),
|
|
8187
|
+
tokenA: tokenA.toHex(),
|
|
8188
|
+
tokenB: tokenB.toHex(),
|
|
8189
|
+
standardAmount: BigInt(standardAmount.toString())
|
|
8190
|
+
});
|
|
8191
|
+
}
|
|
8192
|
+
return orders;
|
|
8193
|
+
}
|
|
8194
|
+
/**
|
|
8195
|
+
* Polls for newly registered phantom orders, invoking the callback once per order.
|
|
8196
|
+
*
|
|
8197
|
+
* Each tick reads the current head and scans every block between the last one processed and that
|
|
8198
|
+
* head, so the block cursor — not the connection — determines what has been seen. This replaced a
|
|
8199
|
+
* system.events subscription, which was only as reliable as its socket: polkadot-js reconnects
|
|
8200
|
+
* the transport but does not reliably re-establish storage subscriptions, and anything emitted
|
|
8201
|
+
* while disconnected was lost silently. With a bid window measured in a handful of blocks, that
|
|
8202
|
+
* meant silently missed bids.
|
|
8203
|
+
*
|
|
8204
|
+
* Scanning a block range is gap-free rather than merely self-healing: an outage delays orders but
|
|
8205
|
+
* cannot drop them, because the cursor only advances past a block whose events were actually
|
|
8206
|
+
* read. Recovery replays the backlog.
|
|
8207
|
+
*
|
|
8208
|
+
* Returns a function that stops polling.
|
|
8209
|
+
*/
|
|
8210
|
+
pollPhantomOrders(callback, options = {}) {
|
|
8211
|
+
const { intervalMs = 6e3, maxBlocksPerPoll = 500, lookbackBlocks = 0, onError } = options;
|
|
8212
|
+
let cursor = null;
|
|
8213
|
+
let inFlight = false;
|
|
8214
|
+
let stopped = false;
|
|
8215
|
+
const tick = async () => {
|
|
8216
|
+
if (inFlight || stopped) return;
|
|
8217
|
+
inFlight = true;
|
|
8218
|
+
try {
|
|
8219
|
+
const head = (await this.api.rpc.chain.getHeader()).number.toNumber();
|
|
8220
|
+
if (cursor === null) {
|
|
8221
|
+
cursor = Math.max(head - 1 - lookbackBlocks, -1);
|
|
8222
|
+
}
|
|
8223
|
+
if (head <= cursor) return;
|
|
8224
|
+
const to = Math.min(head, cursor + maxBlocksPerPoll);
|
|
8225
|
+
for (let blockNumber = cursor + 1; blockNumber <= to; blockNumber++) {
|
|
8226
|
+
if (stopped) return;
|
|
8227
|
+
const orders = await this.getPhantomOrdersInBlock(blockNumber);
|
|
8228
|
+
for (const order of orders) callback(order);
|
|
8229
|
+
cursor = blockNumber;
|
|
8230
|
+
}
|
|
8231
|
+
} catch (err) {
|
|
8232
|
+
onError?.(err);
|
|
8233
|
+
} finally {
|
|
8234
|
+
inFlight = false;
|
|
8142
8235
|
}
|
|
8143
|
-
}
|
|
8144
|
-
|
|
8236
|
+
};
|
|
8237
|
+
void tick();
|
|
8238
|
+
const timer = setInterval(() => void tick(), intervalMs);
|
|
8239
|
+
return () => {
|
|
8240
|
+
stopped = true;
|
|
8241
|
+
clearInterval(timer);
|
|
8242
|
+
};
|
|
8145
8243
|
}
|
|
8146
8244
|
};
|
|
8147
8245
|
var TronChain = class _TronChain {
|
|
@@ -9274,7 +9372,12 @@ function sleep(ms) {
|
|
|
9274
9372
|
async function waitForChallengePeriod(chain, stateMachineHeight) {
|
|
9275
9373
|
const challengePeriod = await chain.challengePeriod(stateMachineHeight.id);
|
|
9276
9374
|
if (challengePeriod === BigInt(0)) return;
|
|
9277
|
-
const updateTime = await chain.stateMachineUpdateTime(stateMachineHeight)
|
|
9375
|
+
const updateTime = await retryPromise(() => chain.stateMachineUpdateTime(stateMachineHeight), {
|
|
9376
|
+
maxRetries: 3,
|
|
9377
|
+
backoffMs: 500,
|
|
9378
|
+
logMessage: "Fetching consensus update time while waiting for the challenge period",
|
|
9379
|
+
shouldRetry: (error) => !MissingConsensusUpdateTimeError.isError(error)
|
|
9380
|
+
});
|
|
9278
9381
|
let currentTimestamp = await chain.timestamp();
|
|
9279
9382
|
let timeElapsed = currentTimestamp - updateTime;
|
|
9280
9383
|
if (timeElapsed > challengePeriod) return;
|
|
@@ -9421,6 +9524,10 @@ function normalizeAddressForEvmBytes32(address) {
|
|
|
9421
9524
|
if (address.length === 66) return address;
|
|
9422
9525
|
return bytes20ToBytes32(address);
|
|
9423
9526
|
}
|
|
9527
|
+
function normalizeEvmAddress(address, field = "address") {
|
|
9528
|
+
if (!viem.isAddress(address)) throw new TypeError(`${field} must be a valid EVM address`);
|
|
9529
|
+
return address.toLowerCase();
|
|
9530
|
+
}
|
|
9424
9531
|
function normalizeAddressForStateMachine(address, stateMachineId) {
|
|
9425
9532
|
return isEvmChain(normalizeStateMachineId(stateMachineId)) ? normalizeAddressForEvmBytes32(address) : address;
|
|
9426
9533
|
}
|
|
@@ -9428,14 +9535,16 @@ var DEFAULT_LOGGER = consola.createConsola({
|
|
|
9428
9535
|
level: consola.LogLevels.silent
|
|
9429
9536
|
});
|
|
9430
9537
|
async function retryPromise(operation, retryConfig) {
|
|
9431
|
-
const { logger = DEFAULT_LOGGER, logMessage = "Retry operation failed" } = retryConfig;
|
|
9538
|
+
const { logger = DEFAULT_LOGGER, logMessage = "Retry operation failed", shouldRetry } = retryConfig;
|
|
9432
9539
|
let lastError;
|
|
9433
9540
|
for (let i = 0; i < retryConfig.maxRetries; i++) {
|
|
9434
9541
|
try {
|
|
9435
9542
|
return await operation();
|
|
9436
9543
|
} catch (error) {
|
|
9544
|
+
if (shouldRetry && !shouldRetry(error)) throw error;
|
|
9437
9545
|
logger.trace(`Retrying(${i}) > ${logMessage}`);
|
|
9438
9546
|
lastError = error;
|
|
9547
|
+
if (i === retryConfig.maxRetries - 1) break;
|
|
9439
9548
|
await new Promise((resolve) => setTimeout(resolve, retryConfig.backoffMs * 2 ** i));
|
|
9440
9549
|
}
|
|
9441
9550
|
}
|
|
@@ -11506,6 +11615,55 @@ query LatestPhantomOrderPriceSnapshot($tokenA: String!, $tokenB: String!) {
|
|
|
11506
11615
|
}
|
|
11507
11616
|
}
|
|
11508
11617
|
}`;
|
|
11618
|
+
var LATEST_PHANTOM_ORDER_LIQUIDITY_SNAPSHOT = `
|
|
11619
|
+
query LatestPhantomOrderLiquiditySnapshot($tokenA: String!, $tokenB: String!) {
|
|
11620
|
+
phantomOrderPriceSnapshots(
|
|
11621
|
+
filter: {
|
|
11622
|
+
and: [
|
|
11623
|
+
{ tokenA: { equalTo: $tokenA } }
|
|
11624
|
+
{ tokenB: { equalTo: $tokenB } }
|
|
11625
|
+
]
|
|
11626
|
+
}
|
|
11627
|
+
orderBy: SNAPSHOT_TIME_DESC
|
|
11628
|
+
first: 1
|
|
11629
|
+
) {
|
|
11630
|
+
nodes {
|
|
11631
|
+
commitment
|
|
11632
|
+
tokenA
|
|
11633
|
+
tokenB
|
|
11634
|
+
snapshotTime
|
|
11635
|
+
}
|
|
11636
|
+
}
|
|
11637
|
+
}`;
|
|
11638
|
+
var LIQUIDITY_PROVIDER_BALANCES = `
|
|
11639
|
+
query LiquidityProviderBalanceAggregates($commitment: String!, $tokenAddress: String!) {
|
|
11640
|
+
liquidityProviderBalances(
|
|
11641
|
+
filter: {
|
|
11642
|
+
and: [
|
|
11643
|
+
{ commitment: { equalTo: $commitment } }
|
|
11644
|
+
{ tokenAddress: { equalTo: $tokenAddress } }
|
|
11645
|
+
]
|
|
11646
|
+
}
|
|
11647
|
+
) {
|
|
11648
|
+
aggregates {
|
|
11649
|
+
sum {
|
|
11650
|
+
balance
|
|
11651
|
+
}
|
|
11652
|
+
distinctCount {
|
|
11653
|
+
providerId
|
|
11654
|
+
}
|
|
11655
|
+
}
|
|
11656
|
+
groupedAggregates(groupBy: [CHAIN, TOKEN_ADDRESS]) {
|
|
11657
|
+
keys
|
|
11658
|
+
sum {
|
|
11659
|
+
balance
|
|
11660
|
+
}
|
|
11661
|
+
distinctCount {
|
|
11662
|
+
providerId
|
|
11663
|
+
}
|
|
11664
|
+
}
|
|
11665
|
+
}
|
|
11666
|
+
}`;
|
|
11509
11667
|
var TOKEN_GATEWAY_ASSET_TELEPORTED_STATUS = `
|
|
11510
11668
|
query TokenGatewayAssetTeleportedStatus($commitment: String!) {
|
|
11511
11669
|
tokenGatewayAssetTeleportedV2s(
|
|
@@ -12090,6 +12248,7 @@ function createCancellationStorage(options = {}) {
|
|
|
12090
12248
|
const decoded = decodeIProof(value);
|
|
12091
12249
|
return decoded;
|
|
12092
12250
|
}
|
|
12251
|
+
if (key2.includes("postCommitment")) return value;
|
|
12093
12252
|
throw new Error(`Unknown storage key type: ${key2}`);
|
|
12094
12253
|
};
|
|
12095
12254
|
const setItem = async (key2, value) => {
|
|
@@ -12103,6 +12262,10 @@ function createCancellationStorage(options = {}) {
|
|
|
12103
12262
|
await baseStorage.setItem(key2, encoded);
|
|
12104
12263
|
return;
|
|
12105
12264
|
}
|
|
12265
|
+
if (key2.includes("postCommitment") && typeof value === "string") {
|
|
12266
|
+
await baseStorage.setItem(key2, value);
|
|
12267
|
+
return;
|
|
12268
|
+
}
|
|
12106
12269
|
throw new Error(`Unknown storage key type: ${key2}`);
|
|
12107
12270
|
};
|
|
12108
12271
|
const removeItem = async (key2) => {
|
|
@@ -12116,11 +12279,25 @@ function createCancellationStorage(options = {}) {
|
|
|
12116
12279
|
});
|
|
12117
12280
|
}
|
|
12118
12281
|
var STORAGE_KEYS = Object.freeze({
|
|
12119
|
-
destProof: (orderId) => `cancel-order:${orderId}:destProof`,
|
|
12120
|
-
getRequest: (orderId) => `cancel-order:${orderId}:getRequest`,
|
|
12121
|
-
sourceProof: (orderId) => `cancel-order:${orderId}:sourceProof`,
|
|
12122
|
-
postCommitment: (orderId) => `cancel-order:${orderId}:postCommitment`
|
|
12282
|
+
destProof: (orderId, source, destination) => `cancel-order:${cancellationScope(orderId, source, destination)}:destProof`,
|
|
12283
|
+
getRequest: (orderId, source, destination) => `cancel-order:${cancellationScope(orderId, source, destination)}:getRequest`,
|
|
12284
|
+
sourceProof: (orderId, source, destination) => `cancel-order:${cancellationScope(orderId, source, destination)}:sourceProof`,
|
|
12285
|
+
postCommitment: (orderId, source, destination) => `cancel-order:${cancellationScope(orderId, source, destination)}:postCommitment`
|
|
12123
12286
|
});
|
|
12287
|
+
var LEGACY_STORAGE_KEYS = Object.freeze({
|
|
12288
|
+
destProof: (orderId, source, destination) => `cancel-order:${legacyCancellationScope(orderId, source, destination)}:destProof`,
|
|
12289
|
+
getRequest: (orderId, source, destination) => `cancel-order:${legacyCancellationScope(orderId, source, destination)}:getRequest`,
|
|
12290
|
+
sourceProof: (orderId, source, destination) => `cancel-order:${legacyCancellationScope(orderId, source, destination)}:sourceProof`,
|
|
12291
|
+
postCommitment: (orderId, source, destination) => `cancel-order:${legacyCancellationScope(orderId, source, destination)}:postCommitment`
|
|
12292
|
+
});
|
|
12293
|
+
function cancellationScope(orderId, source, destination) {
|
|
12294
|
+
if (!source || !destination) return orderId;
|
|
12295
|
+
return `${orderId}:${encodeURIComponent(normalizeStateMachineId(source))}:${encodeURIComponent(normalizeStateMachineId(destination))}`;
|
|
12296
|
+
}
|
|
12297
|
+
function legacyCancellationScope(orderId, source, destination) {
|
|
12298
|
+
if (!source || !destination) return orderId;
|
|
12299
|
+
return `${orderId}:${encodeURIComponent(source)}:${encodeURIComponent(destination)}`;
|
|
12300
|
+
}
|
|
12124
12301
|
function createUsedUserOpsStorage(options = {}) {
|
|
12125
12302
|
options.env ?? detectEnvironment();
|
|
12126
12303
|
const driver = loadDriver({ options }) ?? inMemoryDriver__default.default();
|
|
@@ -15286,6 +15463,29 @@ async function convertFeeTokenToWei(ctx, feeTokenAmount, feeTokenIn, evmChainID)
|
|
|
15286
15463
|
}
|
|
15287
15464
|
|
|
15288
15465
|
// src/protocols/intents/OrderPlacer.ts
|
|
15466
|
+
function deriveCanonicalPlacedOrder(order, args) {
|
|
15467
|
+
const canonicalOrder = {
|
|
15468
|
+
...order,
|
|
15469
|
+
user: args.user,
|
|
15470
|
+
source: args.source,
|
|
15471
|
+
destination: args.destination,
|
|
15472
|
+
deadline: args.deadline,
|
|
15473
|
+
nonce: args.nonce,
|
|
15474
|
+
fees: args.fees,
|
|
15475
|
+
session: args.session,
|
|
15476
|
+
predispatch: {
|
|
15477
|
+
assets: args.predispatch.map((asset) => ({ ...asset })),
|
|
15478
|
+
call: order.predispatch.call
|
|
15479
|
+
},
|
|
15480
|
+
inputs: args.inputs.map((asset) => ({ ...asset })),
|
|
15481
|
+
output: {
|
|
15482
|
+
beneficiary: args.beneficiary,
|
|
15483
|
+
assets: args.outputs.map((asset) => ({ ...asset })),
|
|
15484
|
+
call: order.output.call
|
|
15485
|
+
}
|
|
15486
|
+
};
|
|
15487
|
+
return { ...canonicalOrder, id: orderCommitment(canonicalOrder) };
|
|
15488
|
+
}
|
|
15289
15489
|
var OrderPlacer = class {
|
|
15290
15490
|
/**
|
|
15291
15491
|
* @param ctx - Shared IntentsV2 context providing the source chain client,
|
|
@@ -15302,17 +15502,16 @@ var OrderPlacer = class {
|
|
|
15302
15502
|
* 1. Yields `{ to, data, sessionPrivateKey }` — the caller must sign a
|
|
15303
15503
|
* transaction sending `data` to `to` and pass the signed transaction back
|
|
15304
15504
|
* via `gen.next(signedTx)`.
|
|
15305
|
-
* 2. Returns `{ order, transactionHash }` — the finalized order
|
|
15306
|
-
*
|
|
15307
|
-
* transaction hash.
|
|
15505
|
+
* 2. Returns `{ order, transactionHash }` — the finalized order rebuilt from
|
|
15506
|
+
* the canonical `OrderPlaced` fields with its computed `id`, plus the
|
|
15507
|
+
* placement transaction hash.
|
|
15308
15508
|
*
|
|
15309
15509
|
* A fresh ephemeral session key is generated for every call. The key is
|
|
15310
15510
|
* stored immediately (without a commitment) so it can be retrieved by
|
|
15311
15511
|
* address, then updated with the commitment once the `OrderPlaced` event
|
|
15312
|
-
* confirms the
|
|
15512
|
+
* confirms the canonical order.
|
|
15313
15513
|
*
|
|
15314
|
-
* @param order - The order to place.
|
|
15315
|
-
* with the generated session key address before yielding.
|
|
15514
|
+
* @param order - The order to place. It is not mutated.
|
|
15316
15515
|
* @param graffiti - Optional bytes32 tag for orderflow attribution /
|
|
15317
15516
|
* revenue share. Defaults to {@link DEFAULT_GRAFFITI} (bytes32 zero).
|
|
15318
15517
|
* @yields `{ to, data, sessionPrivateKey }` — target contract address,
|
|
@@ -15327,7 +15526,7 @@ var OrderPlacer = class {
|
|
|
15327
15526
|
const account = accounts.privateKeyToAccount(privateKey);
|
|
15328
15527
|
const sessionKeyAddress = account.address;
|
|
15329
15528
|
const createdAt = Date.now();
|
|
15330
|
-
order
|
|
15529
|
+
const placementOrder = { ...order, session: sessionKeyAddress };
|
|
15331
15530
|
await this.ctx.sessionKeyStorage.setSessionKeyByAddress(sessionKeyAddress, {
|
|
15332
15531
|
privateKey,
|
|
15333
15532
|
address: sessionKeyAddress,
|
|
@@ -15336,7 +15535,7 @@ var OrderPlacer = class {
|
|
|
15336
15535
|
const data = viem.encodeFunctionData({
|
|
15337
15536
|
abi: ABI3,
|
|
15338
15537
|
functionName: "placeOrder",
|
|
15339
|
-
args: [transformOrderForContract(
|
|
15538
|
+
args: [transformOrderForContract(placementOrder), graffiti]
|
|
15340
15539
|
});
|
|
15341
15540
|
const intentGatewayAddress = this.ctx.source.configService.getIntentGatewayAddress(
|
|
15342
15541
|
normalizeStateMachineId(order.source)
|
|
@@ -15356,20 +15555,15 @@ var OrderPlacer = class {
|
|
|
15356
15555
|
if (!orderPlacedEvent) {
|
|
15357
15556
|
throw new Error("OrderPlaced event not found in transaction receipt");
|
|
15358
15557
|
}
|
|
15359
|
-
|
|
15360
|
-
order.inputs = orderPlacedEvent.args.inputs.map((input) => ({
|
|
15361
|
-
token: input.token,
|
|
15362
|
-
amount: input.amount
|
|
15363
|
-
}));
|
|
15364
|
-
order.id = orderCommitment(order);
|
|
15558
|
+
const finalizedOrder = deriveCanonicalPlacedOrder(placementOrder, orderPlacedEvent.args);
|
|
15365
15559
|
const sessionKeyData = {
|
|
15366
15560
|
privateKey,
|
|
15367
15561
|
address: sessionKeyAddress,
|
|
15368
|
-
commitment:
|
|
15562
|
+
commitment: finalizedOrder.id,
|
|
15369
15563
|
createdAt
|
|
15370
15564
|
};
|
|
15371
15565
|
await this.ctx.sessionKeyStorage.setSessionKeyByAddress(sessionKeyAddress, sessionKeyData);
|
|
15372
|
-
return { order, receipt };
|
|
15566
|
+
return { order: finalizedOrder, receipt };
|
|
15373
15567
|
}
|
|
15374
15568
|
};
|
|
15375
15569
|
|
|
@@ -15699,7 +15893,7 @@ var OrderExecutor = class {
|
|
|
15699
15893
|
}
|
|
15700
15894
|
}
|
|
15701
15895
|
};
|
|
15702
|
-
var OrderCanceller = class {
|
|
15896
|
+
var OrderCanceller = class _OrderCanceller {
|
|
15703
15897
|
/**
|
|
15704
15898
|
* @param ctx - Shared IntentsV2 context providing the source and destination
|
|
15705
15899
|
* chain clients, config service, and cancellation storage.
|
|
@@ -15708,6 +15902,9 @@ var OrderCanceller = class {
|
|
|
15708
15902
|
this.ctx = ctx;
|
|
15709
15903
|
}
|
|
15710
15904
|
ctx;
|
|
15905
|
+
static DEFAULT_MAX_RECOVERY_RESTARTS = 1;
|
|
15906
|
+
static PROOF_FRESHNESS_MAX_RETRIES = 3;
|
|
15907
|
+
static PROOF_FRESHNESS_BACKOFF_MS = 500;
|
|
15711
15908
|
logger = consola.createConsola({
|
|
15712
15909
|
level: consola.LogLevels.info,
|
|
15713
15910
|
formatOptions: { columns: 80, colors: true, compact: true, date: false }
|
|
@@ -15739,11 +15936,14 @@ var OrderCanceller = class {
|
|
|
15739
15936
|
* @returns The native token dispatch fee in wei.
|
|
15740
15937
|
*/
|
|
15741
15938
|
async quoteCancelFromSource(order) {
|
|
15742
|
-
if (order.source === order.destination) return { nativeValue: 0n, relayerFee: 0n };
|
|
15743
15939
|
const sourceStateMachine = normalizeStateMachineId(order.source);
|
|
15940
|
+
const destStateMachine = normalizeStateMachineId(order.destination);
|
|
15941
|
+
if (sourceStateMachine === destStateMachine) {
|
|
15942
|
+
return { nativeValue: 0n, relayerFee: 0n };
|
|
15943
|
+
}
|
|
15744
15944
|
const height = order.deadline + 1n;
|
|
15745
15945
|
const destIntentGateway = this.ctx.dest.configService.getIntentGatewayAddress(
|
|
15746
|
-
|
|
15946
|
+
destStateMachine
|
|
15747
15947
|
);
|
|
15748
15948
|
const slotHash = await this.ctx.dest.client.readContract({
|
|
15749
15949
|
abi: ABI3,
|
|
@@ -15755,8 +15955,8 @@ var OrderCanceller = class {
|
|
|
15755
15955
|
const context = encodeWithdrawalRequest(order, order.user);
|
|
15756
15956
|
const getRequest = {
|
|
15757
15957
|
source: sourceStateMachine,
|
|
15758
|
-
dest:
|
|
15759
|
-
from: this.ctx.source.configService.getIntentGatewayAddress(
|
|
15958
|
+
dest: destStateMachine,
|
|
15959
|
+
from: this.ctx.source.configService.getIntentGatewayAddress(destStateMachine),
|
|
15760
15960
|
nonce: await this.ctx.source.getHostNonce(),
|
|
15761
15961
|
height,
|
|
15762
15962
|
keys: [key],
|
|
@@ -15773,7 +15973,9 @@ var OrderCanceller = class {
|
|
|
15773
15973
|
* cancellation is complete.
|
|
15774
15974
|
*
|
|
15775
15975
|
* Delegates to `cancelOrderFromSource` or `cancelOrderFromDest` based on
|
|
15776
|
-
* the `from` parameter.
|
|
15976
|
+
* the `from` parameter. If Hyperbridge has pruned consensus data needed for
|
|
15977
|
+
* source-side GET recovery, clears its stale checkpoint and restarts the
|
|
15978
|
+
* source-side stream internally.
|
|
15777
15979
|
*
|
|
15778
15980
|
* @param order - The order to cancel.
|
|
15779
15981
|
* @param indexerClient - Indexer client used to stream ISMP request status
|
|
@@ -15783,12 +15985,32 @@ var OrderCanceller = class {
|
|
|
15783
15985
|
* cancellation lifecycle.
|
|
15784
15986
|
*/
|
|
15785
15987
|
async *cancelOrder(order, indexerClient, options = {}) {
|
|
15786
|
-
const
|
|
15988
|
+
const sourceStateMachine = normalizeStateMachineId(order.source);
|
|
15989
|
+
const destStateMachine = normalizeStateMachineId(order.destination);
|
|
15990
|
+
const isSameChain = sourceStateMachine === destStateMachine;
|
|
15787
15991
|
if (options.from === "destination" && !isSameChain) {
|
|
15788
15992
|
yield* this.cancelOrderFromDest(order, indexerClient);
|
|
15789
15993
|
return;
|
|
15790
15994
|
}
|
|
15791
|
-
|
|
15995
|
+
let recoveryRestarts = 0;
|
|
15996
|
+
while (true) {
|
|
15997
|
+
try {
|
|
15998
|
+
yield* this.cancelOrderFromSource(order, indexerClient);
|
|
15999
|
+
return;
|
|
16000
|
+
} catch (error) {
|
|
16001
|
+
if (!MissingConsensusUpdateTimeError.isError(error)) throw error;
|
|
16002
|
+
if (recoveryRestarts >= _OrderCanceller.DEFAULT_MAX_RECOVERY_RESTARTS) {
|
|
16003
|
+
throw new Error(
|
|
16004
|
+
`Cancellation recovery stopped after ${recoveryRestarts} restart(s): Hyperbridge no longer retains a required consensus update.`
|
|
16005
|
+
);
|
|
16006
|
+
}
|
|
16007
|
+
recoveryRestarts += 1;
|
|
16008
|
+
this.logger.warn(
|
|
16009
|
+
`Restarting cancellation recovery (${recoveryRestarts}/${_OrderCanceller.DEFAULT_MAX_RECOVERY_RESTARTS}) after a required consensus update was pruned`
|
|
16010
|
+
);
|
|
16011
|
+
await this.clearGetRecoveryCache(order);
|
|
16012
|
+
}
|
|
16013
|
+
}
|
|
15792
16014
|
}
|
|
15793
16015
|
/**
|
|
15794
16016
|
* Async generator that cancels an order by initiating the cancel from the
|
|
@@ -15814,11 +16036,10 @@ var OrderCanceller = class {
|
|
|
15814
16036
|
* @throws If the cancel transaction does not contain the expected on-chain event.
|
|
15815
16037
|
*/
|
|
15816
16038
|
async *cancelOrderFromSource(order, indexerClient) {
|
|
15817
|
-
const
|
|
15818
|
-
const
|
|
15819
|
-
const
|
|
15820
|
-
|
|
15821
|
-
);
|
|
16039
|
+
const sourceStateMachine = normalizeStateMachineId(order.source);
|
|
16040
|
+
const destStateMachine = normalizeStateMachineId(order.destination);
|
|
16041
|
+
const isSameChain = sourceStateMachine === destStateMachine;
|
|
16042
|
+
const intentGatewayAddress = this.ctx.source.configService.getIntentGatewayAddress(sourceStateMachine);
|
|
15822
16043
|
if (isSameChain) {
|
|
15823
16044
|
const data = viem.encodeFunctionData({
|
|
15824
16045
|
abi: ABI3,
|
|
@@ -15847,18 +16068,37 @@ var OrderCanceller = class {
|
|
|
15847
16068
|
};
|
|
15848
16069
|
return;
|
|
15849
16070
|
}
|
|
16071
|
+
const storageKeys = this.recoveryStorageKeys(order);
|
|
16072
|
+
const legacyStorageKeys = this.legacyRecoveryStorageKeys(order);
|
|
15850
16073
|
const hyperbridge = indexerClient.hyperbridge;
|
|
15851
|
-
const sourceStateMachine = normalizeStateMachineId(order.source);
|
|
15852
16074
|
const sourceConsensusStateId = this.ctx.source.configService.getConsensusStateId(sourceStateMachine);
|
|
15853
|
-
let destIProof = await this.
|
|
16075
|
+
let destIProof = await this.getRecoveryItem(
|
|
16076
|
+
storageKeys.destProof,
|
|
16077
|
+
legacyStorageKeys.map((keys) => keys.destProof)
|
|
16078
|
+
);
|
|
15854
16079
|
if (!destIProof) {
|
|
15855
16080
|
destIProof = yield* this.fetchDestinationProof(order, indexerClient);
|
|
15856
|
-
await this.ctx.cancellationStorage.setItem(
|
|
16081
|
+
await this.ctx.cancellationStorage.setItem(storageKeys.destProof, destIProof);
|
|
15857
16082
|
} else {
|
|
15858
|
-
|
|
16083
|
+
let refreshed = false;
|
|
16084
|
+
try {
|
|
16085
|
+
await this.assertProofFresh(hyperbridge, destIProof);
|
|
16086
|
+
} catch (error) {
|
|
16087
|
+
if (!MissingConsensusUpdateTimeError.isError(error)) throw error;
|
|
16088
|
+
await this.removeRecoveryItems(
|
|
16089
|
+
storageKeys.destProof,
|
|
16090
|
+
...legacyStorageKeys.map((keys) => keys.destProof)
|
|
16091
|
+
);
|
|
16092
|
+
destIProof = yield* this.fetchDestinationProof(order, indexerClient);
|
|
16093
|
+
await this.ctx.cancellationStorage.setItem(storageKeys.destProof, destIProof);
|
|
16094
|
+
refreshed = true;
|
|
16095
|
+
}
|
|
16096
|
+
if (!refreshed) yield { status: "DESTINATION_FINALIZED", proof: destIProof };
|
|
15859
16097
|
}
|
|
15860
|
-
|
|
15861
|
-
|
|
16098
|
+
await this.assertProofFresh(hyperbridge, destIProof);
|
|
16099
|
+
let getRequest = await this.getRecoveryItem(
|
|
16100
|
+
storageKeys.getRequest,
|
|
16101
|
+
legacyStorageKeys.map((keys) => keys.getRequest)
|
|
15862
16102
|
);
|
|
15863
16103
|
if (!getRequest) {
|
|
15864
16104
|
const quote = await this.quoteCancelFromSource(order);
|
|
@@ -15878,7 +16118,7 @@ var OrderCanceller = class {
|
|
|
15878
16118
|
const request = events.find((e) => e.eventName === "GetRequestEvent");
|
|
15879
16119
|
if (!request) throw new Error("GetRequest missing");
|
|
15880
16120
|
getRequest = request.args;
|
|
15881
|
-
await this.ctx.cancellationStorage.setItem(
|
|
16121
|
+
await this.ctx.cancellationStorage.setItem(storageKeys.getRequest, getRequest);
|
|
15882
16122
|
yield {
|
|
15883
16123
|
status: "CANCEL_STARTED",
|
|
15884
16124
|
receipt
|
|
@@ -15897,8 +16137,9 @@ var OrderCanceller = class {
|
|
|
15897
16137
|
metadata: statusUpdate.metadata
|
|
15898
16138
|
};
|
|
15899
16139
|
const sourceHeight = BigInt(statusUpdate.metadata.blockNumber);
|
|
15900
|
-
let sourceIProof = await this.
|
|
15901
|
-
|
|
16140
|
+
let sourceIProof = await this.getRecoveryItem(
|
|
16141
|
+
storageKeys.sourceProof,
|
|
16142
|
+
legacyStorageKeys.map((keys) => keys.sourceProof)
|
|
15902
16143
|
);
|
|
15903
16144
|
if (!sourceIProof) {
|
|
15904
16145
|
sourceIProof = await fetchSourceProof(
|
|
@@ -15908,8 +16149,9 @@ var OrderCanceller = class {
|
|
|
15908
16149
|
sourceConsensusStateId,
|
|
15909
16150
|
sourceHeight
|
|
15910
16151
|
);
|
|
15911
|
-
await this.ctx.cancellationStorage.setItem(
|
|
16152
|
+
await this.ctx.cancellationStorage.setItem(storageKeys.sourceProof, sourceIProof);
|
|
15912
16153
|
}
|
|
16154
|
+
await this.assertProofFresh(hyperbridge, sourceIProof);
|
|
15913
16155
|
await waitForChallengePeriod(hyperbridge, {
|
|
15914
16156
|
height: sourceIProof.height,
|
|
15915
16157
|
id: {
|
|
@@ -15917,6 +16159,10 @@ var OrderCanceller = class {
|
|
|
15917
16159
|
consensusStateId: sourceConsensusStateId
|
|
15918
16160
|
}
|
|
15919
16161
|
});
|
|
16162
|
+
await Promise.all([
|
|
16163
|
+
this.assertProofFresh(hyperbridge, sourceIProof),
|
|
16164
|
+
this.assertProofFresh(hyperbridge, destIProof)
|
|
16165
|
+
]);
|
|
15920
16166
|
const getRequestMessage = {
|
|
15921
16167
|
kind: "GetRequest",
|
|
15922
16168
|
requests: [getRequest],
|
|
@@ -15938,9 +16184,12 @@ var OrderCanceller = class {
|
|
|
15938
16184
|
status: "HYPERBRIDGE_FINALIZED",
|
|
15939
16185
|
metadata: statusUpdate.metadata
|
|
15940
16186
|
};
|
|
15941
|
-
await this.
|
|
15942
|
-
|
|
15943
|
-
|
|
16187
|
+
await this.removeRecoveryItems(
|
|
16188
|
+
storageKeys.destProof,
|
|
16189
|
+
storageKeys.getRequest,
|
|
16190
|
+
storageKeys.sourceProof,
|
|
16191
|
+
...legacyStorageKeys.flatMap((keys) => [keys.destProof, keys.getRequest, keys.sourceProof])
|
|
16192
|
+
);
|
|
15944
16193
|
return;
|
|
15945
16194
|
}
|
|
15946
16195
|
}
|
|
@@ -15957,9 +16206,11 @@ var OrderCanceller = class {
|
|
|
15957
16206
|
* @returns The native token dispatch fee in wei.
|
|
15958
16207
|
*/
|
|
15959
16208
|
async quoteCancelFromDest(order) {
|
|
15960
|
-
if (order.source === order.destination) return { nativeValue: 0n, relayerFee: 0n };
|
|
15961
|
-
const destStateMachine = normalizeStateMachineId(order.destination);
|
|
15962
16209
|
const sourceStateMachine = normalizeStateMachineId(order.source);
|
|
16210
|
+
const destStateMachine = normalizeStateMachineId(order.destination);
|
|
16211
|
+
if (sourceStateMachine === destStateMachine) {
|
|
16212
|
+
return { nativeValue: 0n, relayerFee: 0n };
|
|
16213
|
+
}
|
|
15963
16214
|
const destIntentGateway = this.ctx.dest.configService.getIntentGatewayAddress(destStateMachine);
|
|
15964
16215
|
const sourceIntentGateway = this.ctx.source.configService.getIntentGatewayAddress(sourceStateMachine);
|
|
15965
16216
|
const relayerFee = await this.estimateRelayerFee(sourceStateMachine, destStateMachine);
|
|
@@ -15997,11 +16248,13 @@ var OrderCanceller = class {
|
|
|
15997
16248
|
* @throws If the cancel transaction does not contain a `PostRequestEvent`.
|
|
15998
16249
|
*/
|
|
15999
16250
|
async *cancelOrderFromDest(order, indexerClient) {
|
|
16000
|
-
const
|
|
16251
|
+
const storageKeys = this.recoveryStorageKeys(order);
|
|
16001
16252
|
const destStateMachine = normalizeStateMachineId(order.destination);
|
|
16002
16253
|
const intentGatewayAddress = this.ctx.dest.configService.getIntentGatewayAddress(destStateMachine);
|
|
16003
|
-
|
|
16004
|
-
|
|
16254
|
+
const legacyStorageKeys = this.legacyRecoveryStorageKeys(order);
|
|
16255
|
+
let commitment = await this.getRecoveryItem(
|
|
16256
|
+
storageKeys.postCommitment,
|
|
16257
|
+
legacyStorageKeys.map((keys) => keys.postCommitment)
|
|
16005
16258
|
);
|
|
16006
16259
|
if (!commitment) {
|
|
16007
16260
|
const quote = await this.quoteCancelFromDest(order);
|
|
@@ -16026,7 +16279,7 @@ var OrderCanceller = class {
|
|
|
16026
16279
|
if (!postEvent) throw new Error("PostRequestEvent not found in cancel transaction receipt");
|
|
16027
16280
|
const postArgs = postEvent.args;
|
|
16028
16281
|
commitment = postRequestCommitment(postArgs).commitment;
|
|
16029
|
-
await this.ctx.cancellationStorage.setItem(
|
|
16282
|
+
await this.ctx.cancellationStorage.setItem(storageKeys.postCommitment, commitment);
|
|
16030
16283
|
}
|
|
16031
16284
|
const statusStream = indexerClient.postRequestStatusStream(commitment);
|
|
16032
16285
|
for await (const statusUpdate of statusStream) {
|
|
@@ -16060,7 +16313,10 @@ var OrderCanceller = class {
|
|
|
16060
16313
|
if (refundEvents.length === 0) {
|
|
16061
16314
|
throw new Error("EscrowRefunded event not found in source-chain delivery receipt");
|
|
16062
16315
|
}
|
|
16063
|
-
await this.
|
|
16316
|
+
await this.removeRecoveryItems(
|
|
16317
|
+
storageKeys.postCommitment,
|
|
16318
|
+
...legacyStorageKeys.map((keys) => keys.postCommitment)
|
|
16319
|
+
);
|
|
16064
16320
|
yield {
|
|
16065
16321
|
status: "CANCELLATION_COMPLETE",
|
|
16066
16322
|
blockNumber: statusUpdate.metadata.blockNumber,
|
|
@@ -16103,7 +16359,7 @@ var OrderCanceller = class {
|
|
|
16103
16359
|
const intentGatewayV2Address = this.ctx.dest.configService.getIntentGatewayAddress(
|
|
16104
16360
|
this.ctx.dest.config.stateMachineId
|
|
16105
16361
|
);
|
|
16106
|
-
const orderId = order
|
|
16362
|
+
const orderId = this.orderId(order);
|
|
16107
16363
|
const slotHash = await this.ctx.dest.client.readContract({
|
|
16108
16364
|
abi: ABI3,
|
|
16109
16365
|
address: intentGatewayV2Address,
|
|
@@ -16120,6 +16376,7 @@ var OrderCanceller = class {
|
|
|
16120
16376
|
yield { status: "DESTINATION_FINALIZED", proof };
|
|
16121
16377
|
return proof;
|
|
16122
16378
|
} catch (e) {
|
|
16379
|
+
if (MissingConsensusUpdateTimeError.isError(e)) throw e;
|
|
16123
16380
|
lastFailedHeight = latestHeight;
|
|
16124
16381
|
await sleep(1e4);
|
|
16125
16382
|
}
|
|
@@ -16150,6 +16407,7 @@ var OrderCanceller = class {
|
|
|
16150
16407
|
this.logger.info(`Unsigned GET ${commitment} submitted; waiting for Hyperbridge response receipt`);
|
|
16151
16408
|
await sleep(3e4);
|
|
16152
16409
|
} catch (error) {
|
|
16410
|
+
if (MissingConsensusUpdateTimeError.isError(error)) throw error;
|
|
16153
16411
|
this.logger.warn(
|
|
16154
16412
|
`Unsigned GET submit failed for ${commitment}; polling response receipt before failing: ${String(error)}`
|
|
16155
16413
|
);
|
|
@@ -16158,14 +16416,128 @@ var OrderCanceller = class {
|
|
|
16158
16416
|
await this.pollDeliveredReceipt(hyperbridge, commitment);
|
|
16159
16417
|
this.logger.info(`Confirmed Hyperbridge GET delivery for ${commitment}`);
|
|
16160
16418
|
} catch (error) {
|
|
16419
|
+
if (MissingConsensusUpdateTimeError.isError(error)) throw error;
|
|
16161
16420
|
const message2 = `Failed to deliver GET request to Hyperbridge; no response receipt found for ${commitment}: ${String(error)}`;
|
|
16162
16421
|
this.logger.error(message2);
|
|
16163
16422
|
throw new Error(message2);
|
|
16164
16423
|
}
|
|
16165
16424
|
}
|
|
16425
|
+
/** Verify that Hyperbridge still retains the consensus update for a proof. */
|
|
16426
|
+
async assertProofFresh(hyperbridge, proof) {
|
|
16427
|
+
await retryPromise(
|
|
16428
|
+
() => hyperbridge.stateMachineUpdateTime({
|
|
16429
|
+
height: proof.height,
|
|
16430
|
+
id: {
|
|
16431
|
+
stateId: parseStateMachineId(proof.stateMachine).stateId,
|
|
16432
|
+
consensusStateId: proof.consensusStateId
|
|
16433
|
+
}
|
|
16434
|
+
}),
|
|
16435
|
+
{
|
|
16436
|
+
maxRetries: _OrderCanceller.PROOF_FRESHNESS_MAX_RETRIES,
|
|
16437
|
+
backoffMs: _OrderCanceller.PROOF_FRESHNESS_BACKOFF_MS,
|
|
16438
|
+
logger: this.logger,
|
|
16439
|
+
logMessage: `Checking consensus update time for proof at height ${proof.height}`,
|
|
16440
|
+
shouldRetry: (error) => !MissingConsensusUpdateTimeError.isError(error)
|
|
16441
|
+
}
|
|
16442
|
+
);
|
|
16443
|
+
}
|
|
16444
|
+
/**
|
|
16445
|
+
* Drops the source-initiated GET recovery checkpoint after Hyperbridge prunes
|
|
16446
|
+
* a consensus update required by that recovery attempt.
|
|
16447
|
+
*/
|
|
16448
|
+
async clearGetRecoveryCache(order) {
|
|
16449
|
+
const keys = this.recoveryStorageKeys(order);
|
|
16450
|
+
const legacyStorageKeys = this.legacyRecoveryStorageKeys(order);
|
|
16451
|
+
await this.removeRecoveryItems(
|
|
16452
|
+
keys.destProof,
|
|
16453
|
+
keys.sourceProof,
|
|
16454
|
+
keys.getRequest,
|
|
16455
|
+
...legacyStorageKeys.flatMap((legacyKeys) => [
|
|
16456
|
+
legacyKeys.destProof,
|
|
16457
|
+
legacyKeys.sourceProof,
|
|
16458
|
+
legacyKeys.getRequest
|
|
16459
|
+
])
|
|
16460
|
+
);
|
|
16461
|
+
}
|
|
16462
|
+
/**
|
|
16463
|
+
* Returns the pair-scoped storage keys used to resume cancellation recovery
|
|
16464
|
+
* for an order.
|
|
16465
|
+
*
|
|
16466
|
+
* @param order - The order whose source/destination recovery state is stored.
|
|
16467
|
+
*/
|
|
16468
|
+
recoveryStorageKeys(order) {
|
|
16469
|
+
const orderId = this.orderId(order);
|
|
16470
|
+
return {
|
|
16471
|
+
destProof: STORAGE_KEYS.destProof(orderId, order.source, order.destination),
|
|
16472
|
+
getRequest: STORAGE_KEYS.getRequest(orderId, order.source, order.destination),
|
|
16473
|
+
sourceProof: STORAGE_KEYS.sourceProof(orderId, order.source, order.destination),
|
|
16474
|
+
postCommitment: STORAGE_KEYS.postCommitment(orderId, order.source, order.destination)
|
|
16475
|
+
};
|
|
16476
|
+
}
|
|
16477
|
+
/** Older SDK key shapes, checked once and migrated to the normalized pair-scoped key. */
|
|
16478
|
+
legacyRecoveryStorageKeys(order) {
|
|
16479
|
+
const orderId = this.orderId(order);
|
|
16480
|
+
return [
|
|
16481
|
+
{
|
|
16482
|
+
destProof: LEGACY_STORAGE_KEYS.destProof(orderId, order.source, order.destination),
|
|
16483
|
+
getRequest: LEGACY_STORAGE_KEYS.getRequest(orderId, order.source, order.destination),
|
|
16484
|
+
sourceProof: LEGACY_STORAGE_KEYS.sourceProof(orderId, order.source, order.destination),
|
|
16485
|
+
postCommitment: LEGACY_STORAGE_KEYS.postCommitment(orderId, order.source, order.destination)
|
|
16486
|
+
},
|
|
16487
|
+
{
|
|
16488
|
+
destProof: LEGACY_STORAGE_KEYS.destProof(orderId),
|
|
16489
|
+
getRequest: LEGACY_STORAGE_KEYS.getRequest(orderId),
|
|
16490
|
+
sourceProof: LEGACY_STORAGE_KEYS.sourceProof(orderId),
|
|
16491
|
+
postCommitment: LEGACY_STORAGE_KEYS.postCommitment(orderId)
|
|
16492
|
+
}
|
|
16493
|
+
];
|
|
16494
|
+
}
|
|
16495
|
+
async getRecoveryItem(currentKey, legacyKeys) {
|
|
16496
|
+
const current = await this.ctx.cancellationStorage.getItem(currentKey);
|
|
16497
|
+
if (current) return current;
|
|
16498
|
+
for (const legacyKey of new Set(legacyKeys)) {
|
|
16499
|
+
if (legacyKey === currentKey) continue;
|
|
16500
|
+
const legacy = await this.ctx.cancellationStorage.getItem(legacyKey);
|
|
16501
|
+
if (!legacy) continue;
|
|
16502
|
+
await this.ctx.cancellationStorage.setItem(currentKey, legacy);
|
|
16503
|
+
await this.ctx.cancellationStorage.removeItem(legacyKey);
|
|
16504
|
+
return legacy;
|
|
16505
|
+
}
|
|
16506
|
+
return null;
|
|
16507
|
+
}
|
|
16508
|
+
async removeRecoveryItems(...keys) {
|
|
16509
|
+
await Promise.all(
|
|
16510
|
+
[...new Set(keys)].map((key) => this.ctx.cancellationStorage.removeItem(key))
|
|
16511
|
+
);
|
|
16512
|
+
}
|
|
16513
|
+
/**
|
|
16514
|
+
* Returns the order identifier required to persist or clear recovery state.
|
|
16515
|
+
*
|
|
16516
|
+
* @param order - The order being cancelled.
|
|
16517
|
+
* @throws If the order has no identifier.
|
|
16518
|
+
*/
|
|
16519
|
+
orderId(order) {
|
|
16520
|
+
if (!order.id) throw new Error("An order id is required to recover cancellation");
|
|
16521
|
+
return order.id;
|
|
16522
|
+
}
|
|
16523
|
+
/**
|
|
16524
|
+
* Reads the Hyperbridge response receipt for a GET request commitment.
|
|
16525
|
+
*
|
|
16526
|
+
* @param hyperbridge - Hyperbridge chain client used to query the receipt.
|
|
16527
|
+
* @param commitment - Commitment of the GET request.
|
|
16528
|
+
* @returns The receipt commitment when delivered, otherwise `undefined`.
|
|
16529
|
+
*/
|
|
16166
16530
|
async queryDeliveredReceipt(hyperbridge, commitment) {
|
|
16167
16531
|
return hyperbridge.queryResponseReceipt(commitment);
|
|
16168
16532
|
}
|
|
16533
|
+
/**
|
|
16534
|
+
* Polls Hyperbridge until it records a response receipt for the GET request.
|
|
16535
|
+
*
|
|
16536
|
+
* @param hyperbridge - Hyperbridge chain client used to query the receipt.
|
|
16537
|
+
* @param commitment - Commitment of the GET request.
|
|
16538
|
+
* @returns The delivered response receipt commitment.
|
|
16539
|
+
* @throws If no receipt is observed within the configured retry limit.
|
|
16540
|
+
*/
|
|
16169
16541
|
async pollDeliveredReceipt(hyperbridge, commitment) {
|
|
16170
16542
|
this.logger.info(`Polling Hyperbridge GET response receipt for ${commitment}`);
|
|
16171
16543
|
return retryPromise(
|
|
@@ -16903,6 +17275,7 @@ var BidManager = class {
|
|
|
16903
17275
|
}
|
|
16904
17276
|
}
|
|
16905
17277
|
};
|
|
17278
|
+
var RELAYER_MESSAGE_GAS = 1000000n;
|
|
16906
17279
|
var GasEstimator = class {
|
|
16907
17280
|
/**
|
|
16908
17281
|
* @param ctx - Shared IntentsV2 context providing the source and destination
|
|
@@ -16923,7 +17296,12 @@ var GasEstimator = class {
|
|
|
16923
17296
|
*
|
|
16924
17297
|
* **Cross-chain orders:** also estimates the ISMP POST request fee required
|
|
16925
17298
|
* for the solver to trigger source-chain escrow redemption after filling, and
|
|
16926
|
-
* includes it in `fillOptions.relayerFee
|
|
17299
|
+
* includes it in `fillOptions.relayerFee`. The dispatch is always paid in the
|
|
17300
|
+
* fee token — `nativeDispatchFee` is fixed at 0. The native rail would draw
|
|
17301
|
+
* from the solver account's native balance, which nothing guarantees, and a
|
|
17302
|
+
* shortfall is invisible to estimation (the account balance is overridden
|
|
17303
|
+
* during simulation) — it would only surface as a reverted execution that
|
|
17304
|
+
* still bills the paymaster.
|
|
16927
17305
|
*
|
|
16928
17306
|
* **Bundler path:** constructs a mock `PackedUserOperation` signed by an
|
|
16929
17307
|
* ephemeral keypair, applies state overrides, and calls
|
|
@@ -16972,24 +17350,19 @@ var GasEstimator = class {
|
|
|
16972
17350
|
intentGatewayV2Address,
|
|
16973
17351
|
entryPointAddress
|
|
16974
17352
|
}),
|
|
16975
|
-
isSameChain ? Promise.resolve({ postRequestFee: 0n,
|
|
16976
|
-
sourceFeeToken,
|
|
16977
|
-
destFeeToken,
|
|
16978
|
-
souceStateMachineId,
|
|
16979
|
-
destStateMachineId,
|
|
16980
|
-
order
|
|
16981
|
-
)
|
|
17353
|
+
isSameChain ? Promise.resolve({ postRequestFee: 0n, relayerFeeInSourceFeeToken: 0n }) : this.estimateCrossChainFees(sourceFeeToken, destFeeToken, souceStateMachineId)
|
|
16982
17354
|
]);
|
|
16983
17355
|
const { viem: stateOverrides, bundler: bundlerStateOverrides } = stateOverridesResult;
|
|
16984
17356
|
const fillOptions = {
|
|
16985
17357
|
relayerFee: crossChainFees.postRequestFee,
|
|
16986
|
-
|
|
17358
|
+
// Always dispatch with the fee token (see the method docs).
|
|
17359
|
+
nativeDispatchFee: 0n,
|
|
16987
17360
|
outputs: order.output.assets.map((asset) => ({
|
|
16988
17361
|
...asset,
|
|
16989
17362
|
token: normalizeAddressForEvmBytes32(asset.token)
|
|
16990
17363
|
}))
|
|
16991
17364
|
};
|
|
16992
|
-
const totalNativeValue = totalEthValue
|
|
17365
|
+
const totalNativeValue = totalEthValue;
|
|
16993
17366
|
const priorityFeeBumpPercent = params.maxPriorityFeePerGasBumpPercent ?? 8;
|
|
16994
17367
|
const maxFeeBumpPercent = params.maxFeePerGasBumpPercent ?? 10;
|
|
16995
17368
|
let maxPriorityFeePerGas = gasPrice + gasPrice * BigInt(priorityFeeBumpPercent) / 100n;
|
|
@@ -17156,37 +17529,32 @@ var GasEstimator = class {
|
|
|
17156
17529
|
maxPriorityFeePerGas,
|
|
17157
17530
|
totalGasCostWei,
|
|
17158
17531
|
totalGasInFeeToken: totalGasInSourceFeeToken,
|
|
17532
|
+
relayerFeeInSourceFeeToken: crossChainFees.relayerFeeInSourceFeeToken,
|
|
17159
17533
|
fillOptions
|
|
17160
17534
|
};
|
|
17161
17535
|
}
|
|
17162
17536
|
/**
|
|
17163
|
-
* Estimates cross-chain ISMP POST request
|
|
17164
|
-
*
|
|
17165
|
-
|
|
17166
|
-
|
|
17167
|
-
|
|
17168
|
-
|
|
17169
|
-
|
|
17170
|
-
this.ctx
|
|
17171
|
-
|
|
17172
|
-
|
|
17537
|
+
* Estimates the cross-chain ISMP POST request fee (the relayer fee for the
|
|
17538
|
+
* RedeemEscrow message), in both the source and destination fee tokens.
|
|
17539
|
+
* The dispatch is always paid in the fee token — the native rail was
|
|
17540
|
+
* removed because it silently drew on a native balance nothing guarantees.
|
|
17541
|
+
*/
|
|
17542
|
+
async estimateCrossChainFees(sourceFeeToken, destFeeToken, sourceChainId) {
|
|
17543
|
+
const postRequestFeeInSourceFeeToken = await convertGasToFeeToken(
|
|
17544
|
+
this.ctx,
|
|
17545
|
+
RELAYER_MESSAGE_GAS,
|
|
17546
|
+
"source",
|
|
17547
|
+
sourceChainId
|
|
17548
|
+
);
|
|
17549
|
+
const postRequestFeeInDestFeeToken = adjustDecimals(
|
|
17173
17550
|
postRequestFeeInSourceFeeToken,
|
|
17174
17551
|
sourceFeeToken.decimals,
|
|
17175
17552
|
destFeeToken.decimals
|
|
17176
17553
|
);
|
|
17177
|
-
|
|
17178
|
-
|
|
17179
|
-
|
|
17180
|
-
body: constructRedeemEscrowRequestBody({ ...order, id: orderCommitment(order) }, MOCK_ADDRESS),
|
|
17181
|
-
timeoutTimestamp: 0n,
|
|
17182
|
-
nonce,
|
|
17183
|
-
from: this.ctx.source.configService.getIntentGatewayAddress(destChainId),
|
|
17184
|
-
to: this.ctx.source.configService.getIntentGatewayAddress(sourceChainId)
|
|
17554
|
+
return {
|
|
17555
|
+
postRequestFee: postRequestFeeInDestFeeToken,
|
|
17556
|
+
relayerFeeInSourceFeeToken: postRequestFeeInSourceFeeToken
|
|
17185
17557
|
};
|
|
17186
|
-
postRequestFeeInDestFeeToken = postRequestFeeInDestFeeToken * 1005n / 1000n;
|
|
17187
|
-
let protocolFeeInNativeToken = await this.ctx.dest.quoteNative(postRequest, postRequestFeeInDestFeeToken).catch(() => 0n);
|
|
17188
|
-
protocolFeeInNativeToken = protocolFeeInNativeToken * 1005n / 1000n;
|
|
17189
|
-
return { postRequestFee: postRequestFeeInDestFeeToken, protocolFee: protocolFeeInNativeToken };
|
|
17190
17558
|
}
|
|
17191
17559
|
/**
|
|
17192
17560
|
* Builds EVM state override objects for gas estimation of the `fillOrder`
|
|
@@ -17378,6 +17746,178 @@ var OrderStatusChecker = class {
|
|
|
17378
17746
|
return true;
|
|
17379
17747
|
}
|
|
17380
17748
|
};
|
|
17749
|
+
var COMMITMENT_PATTERN = /^0x[0-9a-f]{64}$/i;
|
|
17750
|
+
var LiquidityEngine = class {
|
|
17751
|
+
/**
|
|
17752
|
+
* @param queryClient - Nexus GraphQL client attached to the gateway.
|
|
17753
|
+
* @param chainConfigService - Resolves token decimals for formatted results.
|
|
17754
|
+
*/
|
|
17755
|
+
constructor(queryClient, chainConfigService) {
|
|
17756
|
+
this.queryClient = queryClient;
|
|
17757
|
+
this.chainConfigService = chainConfigService;
|
|
17758
|
+
}
|
|
17759
|
+
queryClient;
|
|
17760
|
+
chainConfigService;
|
|
17761
|
+
/**
|
|
17762
|
+
* Retrieves the newest directional Phantom snapshot for a pair and its
|
|
17763
|
+
* indexed output-token liquidity.
|
|
17764
|
+
*
|
|
17765
|
+
* Nexus filters balances to the canonical output token, then aggregates them
|
|
17766
|
+
* overall and by chain. The returned amounts are decimal strings: the total
|
|
17767
|
+
* uses the canonical Base output-token decimals, while each chain group uses
|
|
17768
|
+
* that chain's token decimals. They describe `snapshotTime`, not live
|
|
17769
|
+
* reservations or fill guarantees.
|
|
17770
|
+
*
|
|
17771
|
+
* @param params - Canonical Phantom-market input and output token addresses.
|
|
17772
|
+
* @returns The latest snapshot, or `undefined` if Nexus has no snapshot for
|
|
17773
|
+
* the directional pair.
|
|
17774
|
+
* @throws {InvalidAvailableLiquiditySnapshotError} If Nexus returns malformed
|
|
17775
|
+
* or internally inconsistent snapshot data.
|
|
17776
|
+
*/
|
|
17777
|
+
async getAvailableLiquiditySnapshot(params) {
|
|
17778
|
+
const tokenIn = normalizeEvmAddress(params.tokenIn, "tokenIn");
|
|
17779
|
+
const tokenOut = normalizeEvmAddress(params.tokenOut, "tokenOut");
|
|
17780
|
+
const response = await this.queryClient.request(
|
|
17781
|
+
LATEST_PHANTOM_ORDER_LIQUIDITY_SNAPSHOT,
|
|
17782
|
+
{ tokenA: tokenIn, tokenB: tokenOut }
|
|
17783
|
+
);
|
|
17784
|
+
const node = response?.phantomOrderPriceSnapshots?.nodes?.[0];
|
|
17785
|
+
if (!node) return;
|
|
17786
|
+
const commitment = node.commitment.toLowerCase();
|
|
17787
|
+
if (!COMMITMENT_PATTERN.test(commitment)) {
|
|
17788
|
+
throw new InvalidAvailableLiquiditySnapshotError(commitment || "<missing>", "commitment is not bytes32 hex");
|
|
17789
|
+
}
|
|
17790
|
+
if (node.tokenA.toLowerCase() !== tokenIn || node.tokenB.toLowerCase() !== tokenOut) {
|
|
17791
|
+
throw new InvalidAvailableLiquiditySnapshotError(commitment, "snapshot token pair does not match the query");
|
|
17792
|
+
}
|
|
17793
|
+
const snapshotTime = new Date(dateStringtoTimestamp(node.snapshotTime));
|
|
17794
|
+
if (Number.isNaN(snapshotTime.getTime())) {
|
|
17795
|
+
throw new InvalidAvailableLiquiditySnapshotError(commitment, "snapshotTime is invalid");
|
|
17796
|
+
}
|
|
17797
|
+
const { totalLiquidity, providerCount, liquidityByChain } = await this.querySnapshotLiquidityAggregates({
|
|
17798
|
+
commitment,
|
|
17799
|
+
tokenAddress: tokenOut
|
|
17800
|
+
});
|
|
17801
|
+
return {
|
|
17802
|
+
totalLiquidity: this.formatLiquidity(totalLiquidity, "EVM-8453" /* BASE_MAINNET */, tokenOut, commitment),
|
|
17803
|
+
providerCount,
|
|
17804
|
+
tokenAddress: tokenOut,
|
|
17805
|
+
snapshotTime,
|
|
17806
|
+
liquidityByChain: liquidityByChain.map((group) => ({
|
|
17807
|
+
...group,
|
|
17808
|
+
totalLiquidity: this.formatLiquidity(group.totalLiquidity, group.chain, group.tokenAddress, commitment)
|
|
17809
|
+
}))
|
|
17810
|
+
};
|
|
17811
|
+
}
|
|
17812
|
+
/**
|
|
17813
|
+
* Requests server-side sums and distinct provider counts for one immutable
|
|
17814
|
+
* snapshot/output-token pair, including chain-level aggregate groups.
|
|
17815
|
+
*
|
|
17816
|
+
* `commitment` uniquely identifies the selected snapshot
|
|
17817
|
+
*/
|
|
17818
|
+
async querySnapshotLiquidityAggregates(params) {
|
|
17819
|
+
const response = await this.queryClient.request(
|
|
17820
|
+
LIQUIDITY_PROVIDER_BALANCES,
|
|
17821
|
+
{ commitment: params.commitment, tokenAddress: params.tokenAddress }
|
|
17822
|
+
);
|
|
17823
|
+
const connection = response?.liquidityProviderBalances;
|
|
17824
|
+
const aggregates = connection?.aggregates;
|
|
17825
|
+
if (!connection || !aggregates) {
|
|
17826
|
+
throw new InvalidAvailableLiquiditySnapshotError(
|
|
17827
|
+
params.commitment,
|
|
17828
|
+
"liquidityProviderBalances aggregates are missing"
|
|
17829
|
+
);
|
|
17830
|
+
}
|
|
17831
|
+
const totalLiquidity = parseSnapshotBigInt(aggregates.sum.balance ?? "0", params.commitment, "total balance");
|
|
17832
|
+
const providerCount = parseProviderCount(aggregates.distinctCount.providerId, params.commitment, "total");
|
|
17833
|
+
const liquidityByChain = connection.groupedAggregates.map((group, index) => {
|
|
17834
|
+
const [chain, tokenAddress] = group.keys;
|
|
17835
|
+
if (!chain?.trim() || !tokenAddress) {
|
|
17836
|
+
throw new InvalidAvailableLiquiditySnapshotError(
|
|
17837
|
+
params.commitment,
|
|
17838
|
+
`liquidity group ${index} has invalid keys`
|
|
17839
|
+
);
|
|
17840
|
+
}
|
|
17841
|
+
const normalizedTokenAddress = normalizeIndexedLiquidityAddress(
|
|
17842
|
+
tokenAddress,
|
|
17843
|
+
params.commitment,
|
|
17844
|
+
`liquidity group ${index} tokenAddress`
|
|
17845
|
+
);
|
|
17846
|
+
if (normalizedTokenAddress !== params.tokenAddress) {
|
|
17847
|
+
throw new InvalidAvailableLiquiditySnapshotError(
|
|
17848
|
+
params.commitment,
|
|
17849
|
+
`liquidity group ${index} tokenAddress does not match the snapshot output token`
|
|
17850
|
+
);
|
|
17851
|
+
}
|
|
17852
|
+
return {
|
|
17853
|
+
chain: chain.trim(),
|
|
17854
|
+
tokenAddress: normalizedTokenAddress,
|
|
17855
|
+
totalLiquidity: parseSnapshotBigInt(
|
|
17856
|
+
group.sum.balance ?? "0",
|
|
17857
|
+
params.commitment,
|
|
17858
|
+
`liquidity group ${index} balance`
|
|
17859
|
+
),
|
|
17860
|
+
providerCount: parseProviderCount(
|
|
17861
|
+
group.distinctCount.providerId,
|
|
17862
|
+
params.commitment,
|
|
17863
|
+
`liquidity group ${index}`
|
|
17864
|
+
)
|
|
17865
|
+
};
|
|
17866
|
+
});
|
|
17867
|
+
const groupedTotal = liquidityByChain.reduce((sum, group) => sum + group.totalLiquidity, 0n);
|
|
17868
|
+
if (groupedTotal !== totalLiquidity) {
|
|
17869
|
+
throw new InvalidAvailableLiquiditySnapshotError(
|
|
17870
|
+
params.commitment,
|
|
17871
|
+
"grouped liquidity does not match the total liquidity"
|
|
17872
|
+
);
|
|
17873
|
+
}
|
|
17874
|
+
return { totalLiquidity, providerCount, liquidityByChain };
|
|
17875
|
+
}
|
|
17876
|
+
/** Formats a raw amount using the configured decimals for its chain/token. */
|
|
17877
|
+
formatLiquidity(amount, chain, tokenAddress, commitment) {
|
|
17878
|
+
const decimals = this.chainConfigService.getAssetMetadataByAddress(chain, tokenAddress)?.decimals;
|
|
17879
|
+
if (decimals === void 0) {
|
|
17880
|
+
throw new InvalidAvailableLiquiditySnapshotError(
|
|
17881
|
+
commitment,
|
|
17882
|
+
`token decimals are not configured for ${tokenAddress} on ${chain}`
|
|
17883
|
+
);
|
|
17884
|
+
}
|
|
17885
|
+
return viem.formatUnits(amount, decimals);
|
|
17886
|
+
}
|
|
17887
|
+
};
|
|
17888
|
+
var InvalidAvailableLiquiditySnapshotError = class extends Error {
|
|
17889
|
+
/** Creates an error that identifies the invalid snapshot and field/reason. */
|
|
17890
|
+
constructor(commitment, reason) {
|
|
17891
|
+
super(`Invalid available-liquidity snapshot ${commitment}: ${reason}`);
|
|
17892
|
+
this.name = "InvalidAvailableLiquiditySnapshotError";
|
|
17893
|
+
}
|
|
17894
|
+
};
|
|
17895
|
+
function parseSnapshotBigInt(value, commitment, field) {
|
|
17896
|
+
try {
|
|
17897
|
+
const amount = BigInt(value);
|
|
17898
|
+
if (amount < 0n) {
|
|
17899
|
+
throw new InvalidAvailableLiquiditySnapshotError(commitment, `${field} cannot be negative`);
|
|
17900
|
+
}
|
|
17901
|
+
return amount;
|
|
17902
|
+
} catch (error) {
|
|
17903
|
+
if (error instanceof InvalidAvailableLiquiditySnapshotError) throw error;
|
|
17904
|
+
throw new InvalidAvailableLiquiditySnapshotError(commitment, `${field} is not an integer`);
|
|
17905
|
+
}
|
|
17906
|
+
}
|
|
17907
|
+
function parseProviderCount(value, commitment, field) {
|
|
17908
|
+
const count = Number(value);
|
|
17909
|
+
if (!Number.isSafeInteger(count) || count < 0) {
|
|
17910
|
+
throw new InvalidAvailableLiquiditySnapshotError(commitment, `${field} provider count is invalid`);
|
|
17911
|
+
}
|
|
17912
|
+
return count;
|
|
17913
|
+
}
|
|
17914
|
+
function normalizeIndexedLiquidityAddress(address, commitment, field) {
|
|
17915
|
+
try {
|
|
17916
|
+
return normalizeEvmAddress(address, field);
|
|
17917
|
+
} catch {
|
|
17918
|
+
throw new InvalidAvailableLiquiditySnapshotError(commitment, `${field} is not a valid EVM address`);
|
|
17919
|
+
}
|
|
17920
|
+
}
|
|
17381
17921
|
|
|
17382
17922
|
// src/protocols/intents/quote/types.ts
|
|
17383
17923
|
var UnsupportedIntentQuoteStrategyError = class extends Error {
|
|
@@ -17664,12 +18204,14 @@ var PhantomSnapshotIntentQuoteStrategy = class {
|
|
|
17664
18204
|
constructor(chainConfigService, getQueryClient) {
|
|
17665
18205
|
this.chainConfigService = chainConfigService;
|
|
17666
18206
|
this.getQueryClient = getQueryClient;
|
|
18207
|
+
this.pairResolver = new PhantomSnapshotPairResolver(chainConfigService);
|
|
17667
18208
|
}
|
|
17668
18209
|
chainConfigService;
|
|
17669
18210
|
getQueryClient;
|
|
18211
|
+
pairResolver;
|
|
17670
18212
|
async quote(params, source, destination) {
|
|
17671
18213
|
validateQuoteParams(params);
|
|
17672
|
-
const pair = this.
|
|
18214
|
+
const pair = this.pairResolver.resolve(params, source.stateMachineId, destination.stateMachineId);
|
|
17673
18215
|
if (!pair) {
|
|
17674
18216
|
throw new UnsupportedIntentQuotePairError({
|
|
17675
18217
|
source: source.stateMachineId,
|
|
@@ -17742,7 +18284,13 @@ var PhantomSnapshotIntentQuoteStrategy = class {
|
|
|
17742
18284
|
throw new InvalidPhantomSnapshotError(snapshot.commitment, "snapshotTime is invalid");
|
|
17743
18285
|
}
|
|
17744
18286
|
}
|
|
17745
|
-
|
|
18287
|
+
};
|
|
18288
|
+
var PhantomSnapshotPairResolver = class {
|
|
18289
|
+
constructor(chainConfigService) {
|
|
18290
|
+
this.chainConfigService = chainConfigService;
|
|
18291
|
+
}
|
|
18292
|
+
chainConfigService;
|
|
18293
|
+
resolve(params, sourceStateMachineId, destinationStateMachineId) {
|
|
17746
18294
|
const tokenIn = this.resolveBaseSnapshotToken(sourceStateMachineId, params.tokenIn);
|
|
17747
18295
|
const tokenOut = this.resolveBaseSnapshotToken(destinationStateMachineId, params.tokenOut);
|
|
17748
18296
|
if (!tokenIn || !tokenOut || !isSupportedSnapshotPair(tokenIn.symbol, tokenOut.symbol)) return;
|
|
@@ -17766,7 +18314,6 @@ function isConfiguredAddress(address) {
|
|
|
17766
18314
|
}
|
|
17767
18315
|
|
|
17768
18316
|
// src/protocols/intents/IntentGateway.ts
|
|
17769
|
-
var CROSS_CHAIN_ORDER_FEE_GAS_BUMP = 600000n;
|
|
17770
18317
|
var IntentGateway = class _IntentGateway {
|
|
17771
18318
|
/** EVM chain on which orders are placed and escrowed. */
|
|
17772
18319
|
source;
|
|
@@ -17794,6 +18341,8 @@ var IntentGateway = class _IntentGateway {
|
|
|
17794
18341
|
gasEstimator;
|
|
17795
18342
|
/** Quote strategies for pricing orders before placement, keyed by strategy name. */
|
|
17796
18343
|
quoteStrategies;
|
|
18344
|
+
/** Resolves order tokens to canonical Phantom snapshot market pairs. */
|
|
18345
|
+
phantomSnapshotPairResolver;
|
|
17797
18346
|
/**
|
|
17798
18347
|
* Private constructor — use {@link IntentGateway.create} instead.
|
|
17799
18348
|
*
|
|
@@ -17837,6 +18386,7 @@ var IntentGateway = class _IntentGateway {
|
|
|
17837
18386
|
this.bidManager = bidManager;
|
|
17838
18387
|
this.gasEstimator = gasEstimator;
|
|
17839
18388
|
this._crypto = crypto;
|
|
18389
|
+
this.phantomSnapshotPairResolver = new PhantomSnapshotPairResolver(dest.configService);
|
|
17840
18390
|
this.quoteStrategies = {
|
|
17841
18391
|
phantom_snapshot: new PhantomSnapshotIntentQuoteStrategy(
|
|
17842
18392
|
dest.configService,
|
|
@@ -17915,15 +18465,47 @@ var IntentGateway = class _IntentGateway {
|
|
|
17915
18465
|
if (!handler) throw new UnsupportedIntentQuoteStrategyError(strategy);
|
|
17916
18466
|
return handler.quote({ ...params, strategy }, source, destination);
|
|
17917
18467
|
}
|
|
18468
|
+
/**
|
|
18469
|
+
* Returns the output-token liquidity measured in the latest directional
|
|
18470
|
+
* Phantom snapshot for this gateway's source and destination.
|
|
18471
|
+
*
|
|
18472
|
+
* Pair resolution uses the same canonical Base market as {@link quoteIntent}.
|
|
18473
|
+
* The snapshot itself determines the output token and chain to aggregate. The
|
|
18474
|
+
* amount is in the token's smallest unit and reflects the indexer's
|
|
18475
|
+
* `snapshotTime`; it is not a live reservation or fill guarantee.
|
|
18476
|
+
*
|
|
18477
|
+
* Requires a prior call to {@link withQueryClient}.
|
|
18478
|
+
*/
|
|
18479
|
+
async queryAvailableLiquidity(params) {
|
|
18480
|
+
const { queryClient } = this.requireIndexer();
|
|
18481
|
+
const sourceStateMachineId = this.source.config.stateMachineId;
|
|
18482
|
+
const destinationStateMachineId = this.dest.config.stateMachineId;
|
|
18483
|
+
const pair = this.phantomSnapshotPairResolver.resolve(params, sourceStateMachineId, destinationStateMachineId);
|
|
18484
|
+
if (!pair) {
|
|
18485
|
+
throw new UnsupportedIntentQuotePairError({
|
|
18486
|
+
source: sourceStateMachineId,
|
|
18487
|
+
destination: destinationStateMachineId,
|
|
18488
|
+
tokenIn: params.tokenIn,
|
|
18489
|
+
tokenOut: params.tokenOut,
|
|
18490
|
+
quoteSource: "Phantom snapshot pair"
|
|
18491
|
+
});
|
|
18492
|
+
}
|
|
18493
|
+
return new LiquidityEngine(queryClient, this.dest.configService).getAvailableLiquiditySnapshot({
|
|
18494
|
+
tokenIn: pair.tokenA,
|
|
18495
|
+
tokenOut: pair.tokenB
|
|
18496
|
+
});
|
|
18497
|
+
}
|
|
17918
18498
|
/**
|
|
17919
18499
|
* Bidirectional async generator that orchestrates the full order lifecycle:
|
|
17920
18500
|
* placement, fee estimation, bid collection, and execution.
|
|
17921
18501
|
*
|
|
17922
18502
|
* **Yield/receive protocol:**
|
|
17923
|
-
* 1. If `order.fees` is unset or zero, estimates gas
|
|
17924
|
-
*
|
|
17925
|
-
*
|
|
17926
|
-
* the
|
|
18503
|
+
* 1. If `order.fees` is unset or zero, estimates gas on an internal copy and
|
|
18504
|
+
* sets same-chain fees to twice the estimate. Cross-chain orders attach
|
|
18505
|
+
* (fill gas + a settlement-message uplift of `RELAYER_MESSAGE_GAS`, the
|
|
18506
|
+
* same gas budget the solver's relayer fee uses) with a 5% buffer over
|
|
18507
|
+
* the whole sum — strictly above the solver's unpadded requirement. The
|
|
18508
|
+
* wei cost used for the `value` field receives a 2% buffer.
|
|
17927
18509
|
* 2. Yields `AWAITING_PLACE_ORDER` with `{ to, data, value, sessionPrivateKey }`.
|
|
17928
18510
|
* The caller must sign the transaction and pass it back via `gen.next(signedTx)`.
|
|
17929
18511
|
* 3. Yields `ORDER_PLACED` with the finalised order and transaction hash once
|
|
@@ -17931,8 +18513,8 @@ var IntentGateway = class _IntentGateway {
|
|
|
17931
18513
|
* 4. Delegates to {@link OrderExecutor.executeOrder} and forwards all
|
|
17932
18514
|
* subsequent status updates until the order is filled, exhausted, or fails.
|
|
17933
18515
|
*
|
|
17934
|
-
* @param order - The order to place and execute. `order.fees`
|
|
17935
|
-
*
|
|
18516
|
+
* @param order - The order to place and execute. It is not mutated. `order.fees`
|
|
18517
|
+
* may be 0; fees are estimated automatically if so.
|
|
17936
18518
|
* @param graffiti - Optional bytes32 tag for orderflow attribution /
|
|
17937
18519
|
* revenue share. Defaults to {@link DEFAULT_GRAFFITI}.
|
|
17938
18520
|
* @param options - Optional tuning parameters:
|
|
@@ -17945,10 +18527,11 @@ var IntentGateway = class _IntentGateway {
|
|
|
17945
18527
|
* estimation returns zero.
|
|
17946
18528
|
*/
|
|
17947
18529
|
async *execute(order, graffiti = DEFAULT_GRAFFITI, options) {
|
|
18530
|
+
const executionOrder = { ...order };
|
|
17948
18531
|
let value;
|
|
17949
|
-
if (!
|
|
18532
|
+
if (!executionOrder.fees || executionOrder.fees === 0n) {
|
|
17950
18533
|
const estimate = await this.gasEstimator.estimateFillOrder({
|
|
17951
|
-
order,
|
|
18534
|
+
order: executionOrder,
|
|
17952
18535
|
maxPriorityFeePerGasBumpPercent: options?.maxPriorityFeePerGasBumpPercent,
|
|
17953
18536
|
maxFeePerGasBumpPercent: options?.maxFeePerGasBumpPercent
|
|
17954
18537
|
});
|
|
@@ -17959,13 +18542,13 @@ var IntentGateway = class _IntentGateway {
|
|
|
17959
18542
|
value = estimate.totalGasCostWei + estimate.totalGasCostWei * 2n / 100n;
|
|
17960
18543
|
const crossChainFeeBump = isSameChain ? 0n : await convertGasToFeeToken(
|
|
17961
18544
|
this.ctx,
|
|
17962
|
-
|
|
18545
|
+
RELAYER_MESSAGE_GAS,
|
|
17963
18546
|
"source",
|
|
17964
18547
|
this.source.config.stateMachineId
|
|
17965
18548
|
);
|
|
17966
|
-
|
|
18549
|
+
executionOrder.fees = isSameChain ? estimate.totalGasInFeeToken * 2n : (estimate.totalGasInFeeToken + crossChainFeeBump) * 105n / 100n;
|
|
17967
18550
|
}
|
|
17968
|
-
const placeOrderGen = this.orderPlacer.placeOrder(
|
|
18551
|
+
const placeOrderGen = this.orderPlacer.placeOrder(executionOrder, graffiti);
|
|
17969
18552
|
const placeOrderFirst = await placeOrderGen.next();
|
|
17970
18553
|
if (placeOrderFirst.done) {
|
|
17971
18554
|
throw new Error("placeOrder generator completed without yielding");
|
|
@@ -18076,13 +18659,20 @@ var IntentGateway = class _IntentGateway {
|
|
|
18076
18659
|
const gen = this.execute(order, graffiti, options);
|
|
18077
18660
|
try {
|
|
18078
18661
|
let input;
|
|
18662
|
+
let finalizedOrder;
|
|
18079
18663
|
while (true) {
|
|
18080
18664
|
const { value, done } = await gen.next(input);
|
|
18081
18665
|
input = void 0;
|
|
18082
18666
|
if (done) break;
|
|
18083
|
-
if (value.status === "
|
|
18667
|
+
if (value.status === "ORDER_PLACED") {
|
|
18668
|
+
finalizedOrder = value.order;
|
|
18084
18669
|
yield value;
|
|
18085
|
-
|
|
18670
|
+
} else if (value.status === "BIDS_RECEIVED") {
|
|
18671
|
+
if (!finalizedOrder) {
|
|
18672
|
+
throw new Error("Received bids before the order was finalized");
|
|
18673
|
+
}
|
|
18674
|
+
yield value;
|
|
18675
|
+
input = await this.autoSelect(finalizedOrder, value.bids);
|
|
18086
18676
|
} else if (value.status === "AWAITING_PLACE_ORDER") {
|
|
18087
18677
|
input = yield value;
|
|
18088
18678
|
} else {
|
|
@@ -22959,6 +23549,7 @@ exports.hyperbridgeAddress = hyperbridgeAddress;
|
|
|
22959
23549
|
exports.maxBigInt = maxBigInt;
|
|
22960
23550
|
exports.normalizeAddressForEvmBytes32 = normalizeAddressForEvmBytes32;
|
|
22961
23551
|
exports.normalizeAddressForStateMachine = normalizeAddressForStateMachine;
|
|
23552
|
+
exports.normalizeEvmAddress = normalizeEvmAddress;
|
|
22962
23553
|
exports.normalizeEvmChainId = normalizeEvmChainId;
|
|
22963
23554
|
exports.normalizeStateMachineId = normalizeStateMachineId;
|
|
22964
23555
|
exports.orderCommitment = orderCommitment;
|