@hyperbridge/sdk 2.8.2 → 2.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.d.ts +365 -51
- package/dist/browser/index.js +732 -291
- package/dist/browser/index.js.map +1 -1
- package/dist/node/index.cjs +739 -289
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.cts +69 -16
- package/dist/node/index.d.ts +69 -16
- package/dist/node/index.js +732 -291
- package/dist/node/index.js.map +1 -1
- package/dist/node/{intents-helpers-D_km9I2f.d.cts → intents-helpers-CkPAsLHB.d.cts} +332 -40
- package/dist/node/{intents-helpers-D_km9I2f.d.ts → intents-helpers-CkPAsLHB.d.ts} +332 -40
- package/dist/node/intents-helpers.cjs +394 -39
- package/dist/node/intents-helpers.cjs.map +1 -1
- package/dist/node/intents-helpers.d.cts +1 -1
- package/dist/node/intents-helpers.d.ts +1 -1
- package/dist/node/intents-helpers.js +391 -40
- package/dist/node/intents-helpers.js.map +1 -1
- package/package.json +155 -153
package/dist/node/index.cjs
CHANGED
|
@@ -2767,18 +2767,25 @@ var chainConfigs = {
|
|
|
2767
2767
|
DAI: "0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3",
|
|
2768
2768
|
USDC: "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d",
|
|
2769
2769
|
USDT: "0x55d398326f99059ff775485246999027b3197955",
|
|
2770
|
-
EXT: "0x7C8c11ADb8EF7cd3CFa718008Ea048445C6E7209"
|
|
2770
|
+
EXT: "0x7C8c11ADb8EF7cd3CFa718008Ea048445C6E7209",
|
|
2771
|
+
cNGN: "0xa8AEA66B361a8d53e8865c62D142167Af28Af058"
|
|
2771
2772
|
},
|
|
2772
2773
|
tokenDecimals: {
|
|
2773
2774
|
USDC: 18,
|
|
2774
2775
|
USDT: 18,
|
|
2776
|
+
// 6, not 18 — cNGN keeps the same decimals it has on every other chain, unlike the
|
|
2777
|
+
// Binance-pegged stables above. Every phantom standard_amount and pool rate divides
|
|
2778
|
+
// by this, so the divergence from its neighbours here is load-bearing, not a typo.
|
|
2779
|
+
cNGN: 6,
|
|
2775
2780
|
EXT: 18
|
|
2776
2781
|
},
|
|
2777
2782
|
tokenStorageSlots: {
|
|
2778
2783
|
USDT: { balanceSlot: 1, allowanceSlot: 2 },
|
|
2779
2784
|
USDC: { balanceSlot: 1, allowanceSlot: 2 },
|
|
2780
2785
|
WETH: { balanceSlot: 3, allowanceSlot: 4 },
|
|
2781
|
-
DAI: { balanceSlot: 0, allowanceSlot: 0 }
|
|
2786
|
+
DAI: { balanceSlot: 0, allowanceSlot: 0 },
|
|
2787
|
+
cNGN: { balanceSlot: 201, allowanceSlot: 202 }
|
|
2788
|
+
// custom upgradeable layout, as on Base
|
|
2782
2789
|
},
|
|
2783
2790
|
addresses: {
|
|
2784
2791
|
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
@@ -5243,11 +5250,21 @@ var ChainConfigService = class {
|
|
|
5243
5250
|
* it, so a new asset is added once in `chain.ts` and nowhere else.
|
|
5244
5251
|
*/
|
|
5245
5252
|
getAssetBySymbol(chain, symbol) {
|
|
5246
|
-
|
|
5253
|
+
return this.getAssetMetadataBySymbol(chain, symbol)?.address;
|
|
5254
|
+
}
|
|
5255
|
+
/** Resolves a configured token symbol case-insensitively on a specific chain. */
|
|
5256
|
+
getAssetMetadataBySymbol(chain, symbol) {
|
|
5257
|
+
const config = this.getConfig(chain);
|
|
5258
|
+
const assets = config?.assets;
|
|
5247
5259
|
if (!assets) return void 0;
|
|
5248
5260
|
const target = symbol.trim().toUpperCase();
|
|
5249
5261
|
for (const [key, address] of Object.entries(assets)) {
|
|
5250
|
-
if (key.toUpperCase()
|
|
5262
|
+
if (key.toUpperCase() !== target) continue;
|
|
5263
|
+
return {
|
|
5264
|
+
symbol: key,
|
|
5265
|
+
address,
|
|
5266
|
+
decimals: config.tokenDecimals?.[key]
|
|
5267
|
+
};
|
|
5251
5268
|
}
|
|
5252
5269
|
return void 0;
|
|
5253
5270
|
}
|
|
@@ -7810,6 +7827,28 @@ function encodeISMPMessage(message) {
|
|
|
7810
7827
|
}
|
|
7811
7828
|
var OFFCHAIN_BID_PREFIX = new TextEncoder().encode("intents::bid::");
|
|
7812
7829
|
var OFFCHAIN_PHANTOM_PREFIX = new TextEncoder().encode("intents::phantom::order::");
|
|
7830
|
+
var HYPERBRIDGE_TYPES_BUNDLE = {
|
|
7831
|
+
spec: {
|
|
7832
|
+
nexus: { hasher: utilCrypto.keccakAsU8a },
|
|
7833
|
+
gargantua: { hasher: utilCrypto.keccakAsU8a }
|
|
7834
|
+
}
|
|
7835
|
+
};
|
|
7836
|
+
var BASE_TIP = 1000000000n;
|
|
7837
|
+
var HTTP_CONNECT_TIMEOUT_MS = 2e4;
|
|
7838
|
+
var INCLUSION_TIMEOUT_MS = 2e4;
|
|
7839
|
+
var PHANTOM_POLL_INTERVAL_MS = 15e3;
|
|
7840
|
+
var GARGANTUA_PHANTOM_POLL_INTERVAL_MS = 6e3;
|
|
7841
|
+
function rejectAfter(ms, message) {
|
|
7842
|
+
return new Promise((_resolve, reject) => {
|
|
7843
|
+
const timer = setTimeout(() => reject(new Error(message)), ms);
|
|
7844
|
+
timer.unref?.();
|
|
7845
|
+
});
|
|
7846
|
+
}
|
|
7847
|
+
function deriveHttpUrl(wsUrl) {
|
|
7848
|
+
if (wsUrl.startsWith("wss://")) return `https://${wsUrl.slice("wss://".length)}`;
|
|
7849
|
+
if (wsUrl.startsWith("ws://")) return `http://${wsUrl.slice("ws://".length)}`;
|
|
7850
|
+
throw new Error(`Cannot derive an HTTP endpoint from a non-websocket url: ${wsUrl}`);
|
|
7851
|
+
}
|
|
7813
7852
|
var BidCodec = scaleTs.Struct({ filler: scaleTs.Bytes(32), user_op: scaleTs.Vector(scaleTs.u8) });
|
|
7814
7853
|
var PackedUserOperationCodec = scaleTs.Struct({
|
|
7815
7854
|
sender: scaleTs.Bytes(20),
|
|
@@ -7870,6 +7909,8 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
7870
7909
|
ownsConnection;
|
|
7871
7910
|
/** Cached result of whether the node exposes intents_* RPC methods */
|
|
7872
7911
|
hasIntentsRpc = null;
|
|
7912
|
+
/** The HTTP-backed api, connected on first use. Cleared after a failed attempt so it retries. */
|
|
7913
|
+
httpApi = null;
|
|
7873
7914
|
// Serialises every extrinsic submission on this instance's substrate account. All submit/retract
|
|
7874
7915
|
// methods funnel through signAndSendExtrinsic, each using the API's auto-nonce; fired in parallel
|
|
7875
7916
|
// (bids for orders on different chains, or several phantom orders in one interval) they would grab
|
|
@@ -7886,12 +7927,7 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
7886
7927
|
static async connect(wsUrl, substratePrivateKey) {
|
|
7887
7928
|
const api$1 = await api.ApiPromise.create({
|
|
7888
7929
|
provider: new api.WsProvider(wsUrl),
|
|
7889
|
-
typesBundle:
|
|
7890
|
-
spec: {
|
|
7891
|
-
nexus: { hasher: utilCrypto.keccakAsU8a },
|
|
7892
|
-
gargantua: { hasher: utilCrypto.keccakAsU8a }
|
|
7893
|
-
}
|
|
7894
|
-
}
|
|
7930
|
+
typesBundle: HYPERBRIDGE_TYPES_BUNDLE
|
|
7895
7931
|
});
|
|
7896
7932
|
return new _IntentsCoprocessor(api$1, substratePrivateKey, true);
|
|
7897
7933
|
}
|
|
@@ -7917,15 +7953,85 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
7917
7953
|
static fromApi(api, substratePrivateKey) {
|
|
7918
7954
|
return new _IntentsCoprocessor(api, substratePrivateKey, false);
|
|
7919
7955
|
}
|
|
7956
|
+
/**
|
|
7957
|
+
* The API every RPC query runs on: HTTP, connected to the same node as the websocket. Exposed so
|
|
7958
|
+
* callers query through this connection rather than opening one of their own.
|
|
7959
|
+
*
|
|
7960
|
+
* The split is by what each transport is for. Queries are one-shot request/response, which HTTP
|
|
7961
|
+
* serves without holding any state that can silently rot between calls. The websocket earns its
|
|
7962
|
+
* keep only where subscriptions do — watching a submitted extrinsic to inclusion.
|
|
7963
|
+
*/
|
|
7964
|
+
async queryApi() {
|
|
7965
|
+
return await this.http();
|
|
7966
|
+
}
|
|
7967
|
+
/**
|
|
7968
|
+
* The websocket API, exposed so callers share this one connection instead of opening a second
|
|
7969
|
+
* socket to the same node. Only needed for subscriptions; use {@link queryApi} to read.
|
|
7970
|
+
*/
|
|
7971
|
+
get apiConnection() {
|
|
7972
|
+
return this.api;
|
|
7973
|
+
}
|
|
7920
7974
|
/**
|
|
7921
7975
|
* Disconnects the underlying API connection if this instance owns it.
|
|
7922
|
-
* Only disconnects if created via `connect()`, not when using shared connections.
|
|
7976
|
+
* Only disconnects the websocket if created via `connect()`, not when using shared connections.
|
|
7977
|
+
* The HTTP api is always created here, so it is always ours to close.
|
|
7923
7978
|
*/
|
|
7924
7979
|
async disconnect() {
|
|
7980
|
+
const http4 = this.httpApi;
|
|
7981
|
+
this.httpApi = null;
|
|
7982
|
+
if (http4) {
|
|
7983
|
+
await http4.then((api) => api.disconnect()).catch(() => {
|
|
7984
|
+
});
|
|
7985
|
+
}
|
|
7925
7986
|
if (this.ownsConnection) {
|
|
7926
7987
|
await this.api.disconnect();
|
|
7927
7988
|
}
|
|
7928
7989
|
}
|
|
7990
|
+
/**
|
|
7991
|
+
* The HTTP api for this node, connected on first use. Every coprocessor has one — the endpoint
|
|
7992
|
+
* is derived from the websocket's own endpoint, so there is nothing to configure and nothing to
|
|
7993
|
+
* be absent.
|
|
7994
|
+
*
|
|
7995
|
+
* The connection attempt is bounded on both sides. `isReadyOrError` rejects on a failed
|
|
7996
|
+
* handshake, where plain `isReady` would simply never resolve, and the timeout covers an
|
|
7997
|
+
* endpoint that accepts the request and then goes quiet. An unbounded wait here would hang the
|
|
7998
|
+
* poll tick awaiting it, which is precisely the silent stall that polling exists to avoid. A
|
|
7999
|
+
* failed attempt is not cached, so the next call tries again.
|
|
8000
|
+
*/
|
|
8001
|
+
async http() {
|
|
8002
|
+
if (!this.httpApi) {
|
|
8003
|
+
const httpUrl = deriveHttpUrl(this.wsEndpoint());
|
|
8004
|
+
const api$1 = new api.ApiPromise({
|
|
8005
|
+
provider: new api.HttpProvider(httpUrl),
|
|
8006
|
+
typesBundle: HYPERBRIDGE_TYPES_BUNDLE,
|
|
8007
|
+
// A second connection to the node the ws api already reported on; its init warnings
|
|
8008
|
+
// would just be duplicates.
|
|
8009
|
+
noInitWarn: true
|
|
8010
|
+
});
|
|
8011
|
+
this.httpApi = Promise.race([
|
|
8012
|
+
api$1.isReadyOrError,
|
|
8013
|
+
rejectAfter(HTTP_CONNECT_TIMEOUT_MS, `HTTP RPC ${httpUrl} did not become ready`)
|
|
8014
|
+
]).catch(async (err) => {
|
|
8015
|
+
await api$1.disconnect().catch(() => {
|
|
8016
|
+
});
|
|
8017
|
+
this.httpApi = null;
|
|
8018
|
+
throw new Error(`HTTP RPC ${httpUrl} is unavailable: ${err instanceof Error ? err.message : err}`);
|
|
8019
|
+
});
|
|
8020
|
+
}
|
|
8021
|
+
return await this.httpApi;
|
|
8022
|
+
}
|
|
8023
|
+
/**
|
|
8024
|
+
* The endpoint the websocket provider is connected to. Read from the provider rather than
|
|
8025
|
+
* remembered from a constructor argument, so it is the one endpoint in use no matter which
|
|
8026
|
+
* factory built this instance.
|
|
8027
|
+
*/
|
|
8028
|
+
wsEndpoint() {
|
|
8029
|
+
const endpoint = this.api._rpcCore?.provider?.endpoint;
|
|
8030
|
+
if (!endpoint) {
|
|
8031
|
+
throw new Error("Cannot determine the Hyperbridge websocket endpoint to derive an HTTP endpoint from");
|
|
8032
|
+
}
|
|
8033
|
+
return endpoint;
|
|
8034
|
+
}
|
|
7929
8035
|
/**
|
|
7930
8036
|
* Creates a Substrate keypair from the configured private key.
|
|
7931
8037
|
* Supports hex seed (with or without 0x), mnemonic phrases, and URI derivation paths (//Alice).
|
|
@@ -7948,50 +8054,119 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
7948
8054
|
/**
|
|
7949
8055
|
* Signs and sends an extrinsic. Submissions are serialised through {@link submissionQueue} so
|
|
7950
8056
|
* concurrent calls never collide on the substrate account nonce — each extrinsic reaches a block
|
|
7951
|
-
* (or is confirmed still pooled and returned as `pending`) before the next is signed
|
|
7952
|
-
*
|
|
8057
|
+
* (or is confirmed still pooled and returned as `pending`) before the next is signed. The
|
|
8058
|
+
* auto-nonce is the account's on-chain nonce, so a still-pooled extrinsic does not advance it: a
|
|
8059
|
+
* submission signed behind a pending one bounces off it (1013/1014) and is reported as pending
|
|
8060
|
+
* too, rather than landing as a second copy.
|
|
8061
|
+
*
|
|
8062
|
+
* The extrinsic is built rather than passed in because the api it is built on decides where it
|
|
8063
|
+
* is signed and sent: a websocket that is down when the queue reaches this submission diverts it
|
|
8064
|
+
* to {@link sendViaHttp}, which needs the call bound to the HTTP api instead.
|
|
7953
8065
|
*/
|
|
7954
|
-
async signAndSendExtrinsic(
|
|
7955
|
-
const result = await this.submissionQueue.add(
|
|
7956
|
-
(
|
|
7957
|
-
|
|
8066
|
+
async signAndSendExtrinsic(build, maxRetries = 3, timeoutMs = INCLUSION_TIMEOUT_MS) {
|
|
8067
|
+
const result = await this.submissionQueue.add(async () => {
|
|
8068
|
+
if (!this.api.isConnected) {
|
|
8069
|
+
try {
|
|
8070
|
+
return await this.sendViaHttp(await this.http(), build);
|
|
8071
|
+
} catch (err) {
|
|
8072
|
+
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
8073
|
+
}
|
|
8074
|
+
}
|
|
8075
|
+
return await this.sendExtrinsicWithRetries(build(this.api), maxRetries, timeoutMs);
|
|
8076
|
+
});
|
|
7958
8077
|
return result ?? { success: false, error: "Submission queue returned no result" };
|
|
7959
8078
|
}
|
|
8079
|
+
/**
|
|
8080
|
+
* Last-resort submission for when the websocket is down at signing time. A bid is only worth
|
|
8081
|
+
* anything inside its window, so waiting for a reconnect usually means not bidding at all.
|
|
8082
|
+
*
|
|
8083
|
+
* HTTP has no subscriptions, so this is `author_submitExtrinsic`: the node accepts the extrinsic
|
|
8084
|
+
* into its pool and returns its hash, and nothing further is observable from here. That is
|
|
8085
|
+
* exactly the `pending` contract — in flight, outcome unknown, do not re-sign — so the result
|
|
8086
|
+
* says so rather than claiming a success it cannot see.
|
|
8087
|
+
*
|
|
8088
|
+
* Only reached when the socket was already down before signing. A submission that got as far as
|
|
8089
|
+
* the pool over the websocket is never retried here: that is the duplicate-nonce race the
|
|
8090
|
+
* `pending` result exists to prevent.
|
|
8091
|
+
*/
|
|
8092
|
+
async sendViaHttp(api, build) {
|
|
8093
|
+
try {
|
|
8094
|
+
const hash = await build(api).signAndSend(this.getKeyPair(), { tip: BASE_TIP });
|
|
8095
|
+
return { success: false, pending: true, extrinsicHash: hash.toHex() };
|
|
8096
|
+
} catch (err) {
|
|
8097
|
+
return this.classifySubmissionError(err instanceof Error ? err : new Error(String(err)));
|
|
8098
|
+
}
|
|
8099
|
+
}
|
|
7960
8100
|
/**
|
|
7961
8101
|
* Signs and sends an extrinsic, handling status updates and errors.
|
|
7962
8102
|
* Implements retry logic with progressive tip increases for stuck transactions.
|
|
7963
8103
|
*
|
|
7964
|
-
*
|
|
7965
|
-
*
|
|
7966
|
-
*
|
|
7967
|
-
*
|
|
7968
|
-
*
|
|
7969
|
-
*
|
|
8104
|
+
* Two kinds of failure are retried, and the difference is the nonce.
|
|
8105
|
+
*
|
|
8106
|
+
* An attempt that verifiably went nowhere (rejected before the pool, dropped, invalid) leaves
|
|
8107
|
+
* the account nonce free, so the next attempt simply re-signs with the auto-nonce.
|
|
8108
|
+
*
|
|
8109
|
+
* An attempt that reached the pool and was still there when the watch timed out (`stalled`) is
|
|
8110
|
+
* retried as a *replacement*: the same nonce it was signed with, and double the tip. Substrate's
|
|
8111
|
+
* pool evicts a pooled extrinsic in favour of a higher-priority one at the same (account, nonce),
|
|
8112
|
+
* so exactly one of the two can ever execute. This matters for a bid, which is worth nothing once
|
|
8113
|
+
* its window closes — waiting out a stalled extrinsic usually means not bidding at all.
|
|
8114
|
+
*
|
|
8115
|
+
* Re-signing without pinning the nonce is what must never happen here. The auto-nonce is read
|
|
8116
|
+
* from on-chain state, so it is only the stalled extrinsic's nonce for as long as that extrinsic
|
|
8117
|
+
* stays out of a block — and a stall is precisely the case where it may land at any moment. Once
|
|
8118
|
+
* it does, an unpinned retry takes the *next* nonce and both execute: a duplicate `placeBid` that
|
|
8119
|
+
* fails on-chain, and a second `retractBid` that pulls the bid just placed. When the signed nonce
|
|
8120
|
+
* cannot be read, the stalled result is returned rather than guessed at.
|
|
8121
|
+
*
|
|
8122
|
+
* A rejection that bounced off a copy already pooled (1013/1014) is likewise left alone: that
|
|
8123
|
+
* copy is in flight and its outcome is unknown here, so the `pending` result goes back to the
|
|
8124
|
+
* caller to confirm later.
|
|
7970
8125
|
*/
|
|
7971
8126
|
async sendExtrinsicWithRetries(extrinsic, maxRetries, timeoutMs) {
|
|
7972
8127
|
const keyPair = this.getKeyPair();
|
|
7973
|
-
const baseTip = 1000000000n;
|
|
7974
8128
|
let attempt = 0;
|
|
8129
|
+
let nonce;
|
|
8130
|
+
let stalled;
|
|
7975
8131
|
while (attempt < maxRetries) {
|
|
7976
|
-
const currentTip =
|
|
8132
|
+
const currentTip = BASE_TIP * BigInt(2 ** attempt);
|
|
7977
8133
|
attempt++;
|
|
7978
8134
|
try {
|
|
7979
|
-
const result = await this.sendWithTimeout(extrinsic, keyPair, currentTip, timeoutMs);
|
|
7980
|
-
if (result.success || result.
|
|
8135
|
+
const result = await this.sendWithTimeout(extrinsic, keyPair, currentTip, timeoutMs, nonce);
|
|
8136
|
+
if (result.success || result.error?.includes("Dispatch error")) {
|
|
7981
8137
|
return result;
|
|
7982
8138
|
}
|
|
8139
|
+
if (result.stalled) {
|
|
8140
|
+
stalled = result;
|
|
8141
|
+
nonce ??= this.signedNonce(extrinsic);
|
|
8142
|
+
if (nonce === void 0) return result;
|
|
8143
|
+
continue;
|
|
8144
|
+
}
|
|
8145
|
+
if (result.pending) return stalled ?? result;
|
|
7983
8146
|
} catch (err) {
|
|
7984
|
-
return {
|
|
8147
|
+
return stalled ?? {
|
|
7985
8148
|
success: false,
|
|
7986
8149
|
error: err instanceof Error ? err.message : "Unknown error"
|
|
7987
8150
|
};
|
|
7988
8151
|
}
|
|
7989
8152
|
}
|
|
7990
|
-
return {
|
|
8153
|
+
return stalled ?? {
|
|
7991
8154
|
success: false,
|
|
7992
8155
|
error: `Transaction failed after ${maxRetries} attempts`
|
|
7993
8156
|
};
|
|
7994
8157
|
}
|
|
8158
|
+
/**
|
|
8159
|
+
* The nonce an extrinsic was signed with, or undefined if it carries no readable one — which is
|
|
8160
|
+
* the case before it has ever been signed, and for a stub api in tests.
|
|
8161
|
+
*/
|
|
8162
|
+
signedNonce(extrinsic) {
|
|
8163
|
+
try {
|
|
8164
|
+
const nonce = extrinsic.nonce?.toNumber?.();
|
|
8165
|
+
return typeof nonce === "number" && Number.isFinite(nonce) ? nonce : void 0;
|
|
8166
|
+
} catch {
|
|
8167
|
+
return void 0;
|
|
8168
|
+
}
|
|
8169
|
+
}
|
|
7995
8170
|
/**
|
|
7996
8171
|
* Classifies a submission rejection. Codes 1013 ("already imported") and 1014 ("priority is
|
|
7997
8172
|
* too low") both mean a copy of this account+nonce is already in the pool — almost always our
|
|
@@ -8009,10 +8184,15 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8009
8184
|
*
|
|
8010
8185
|
* A timeout is only a failure when the extrinsic never made it into the transaction pool.
|
|
8011
8186
|
* Once a pool-entry status (Future/Ready/Broadcast/Retracted) has been seen, the extrinsic is
|
|
8012
|
-
* in flight and may well execute after the watch is abandoned — the result is then `pending
|
|
8013
|
-
* telling the caller to
|
|
8187
|
+
* in flight and may well execute after the watch is abandoned — the result is then `pending`
|
|
8188
|
+
* and `stalled`, telling the caller to replace it under the same nonce or confirm it later,
|
|
8189
|
+
* never to re-sign the same call under a fresh one.
|
|
8190
|
+
*
|
|
8191
|
+
* `nonce` pins the submission to a specific account nonce, which is what makes a retry a pool
|
|
8192
|
+
* replacement rather than a second extrinsic queued behind the first. Left undefined on the
|
|
8193
|
+
* first attempt, where the api's auto-nonce is correct.
|
|
8014
8194
|
*/
|
|
8015
|
-
async sendWithTimeout(extrinsic, keyPair, tip, timeoutMs) {
|
|
8195
|
+
async sendWithTimeout(extrinsic, keyPair, tip, timeoutMs, nonce) {
|
|
8016
8196
|
return new Promise((resolve) => {
|
|
8017
8197
|
let resolved = false;
|
|
8018
8198
|
let unsubscribe = null;
|
|
@@ -8026,12 +8206,13 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8026
8206
|
resolve({
|
|
8027
8207
|
success: false,
|
|
8028
8208
|
pending: enteredPool || void 0,
|
|
8209
|
+
stalled: enteredPool || void 0,
|
|
8029
8210
|
extrinsicHash: enteredPool ? extrinsic.hash.toHex() : void 0,
|
|
8030
8211
|
error: `Transaction timed out after ${timeoutMs}ms${enteredPool ? " while in the transaction pool" : ""}`
|
|
8031
8212
|
});
|
|
8032
8213
|
}
|
|
8033
8214
|
}, timeoutMs);
|
|
8034
|
-
extrinsic.signAndSend(keyPair, { tip }, (result) => {
|
|
8215
|
+
extrinsic.signAndSend(keyPair, nonce === void 0 ? { tip } : { tip, nonce }, (result) => {
|
|
8035
8216
|
if (resolved) return;
|
|
8036
8217
|
if (result.status.isFuture || result.status.isReady || result.status.isBroadcast || result.status.isRetracted) {
|
|
8037
8218
|
enteredPool = true;
|
|
@@ -8039,16 +8220,9 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8039
8220
|
if (result.dispatchError && (result.status.isInBlock || result.status.isFinalized)) {
|
|
8040
8221
|
resolved = true;
|
|
8041
8222
|
clearTimeout(timeoutId);
|
|
8042
|
-
let errorMsg;
|
|
8043
|
-
if (result.dispatchError.isModule) {
|
|
8044
|
-
const decoded = this.api.registry.findMetaError(result.dispatchError.asModule);
|
|
8045
|
-
errorMsg = `Dispatch error: ${decoded.section}::${decoded.name}`;
|
|
8046
|
-
} else {
|
|
8047
|
-
errorMsg = `Dispatch error: ${result.dispatchError.toString()}`;
|
|
8048
|
-
}
|
|
8049
8223
|
resolve({
|
|
8050
8224
|
success: false,
|
|
8051
|
-
error:
|
|
8225
|
+
error: `Dispatch error: ${this.describeDispatchError(result.dispatchError)}`
|
|
8052
8226
|
});
|
|
8053
8227
|
} else if (result.status.isDropped || result.status.isInvalid || result.status.isUsurped || result.status.isFinalityTimeout) {
|
|
8054
8228
|
resolved = true;
|
|
@@ -8066,21 +8240,19 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8066
8240
|
if (interrupted) {
|
|
8067
8241
|
const [indexCodec, dispatchError] = interrupted.event.data;
|
|
8068
8242
|
if (Number(indexCodec.toString()) === 0) {
|
|
8069
|
-
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
} else {
|
|
8074
|
-
errorMsg = `Dispatch error: batch interrupted (${dispatchError?.toString()})`;
|
|
8075
|
-
}
|
|
8076
|
-
resolve({ success: false, error: errorMsg });
|
|
8243
|
+
resolve({
|
|
8244
|
+
success: false,
|
|
8245
|
+
error: `Dispatch error: ${this.describeDispatchError(dispatchError)}`
|
|
8246
|
+
});
|
|
8077
8247
|
return;
|
|
8078
8248
|
}
|
|
8079
8249
|
}
|
|
8080
8250
|
resolve({
|
|
8081
8251
|
success: true,
|
|
8082
8252
|
blockHash: (result.status.isInBlock ? result.status.asInBlock : result.status.asFinalized).toHex(),
|
|
8083
|
-
extrinsicHash: extrinsic.hash.toHex()
|
|
8253
|
+
extrinsicHash: extrinsic.hash.toHex(),
|
|
8254
|
+
// Carried so a batch caller can attribute each item's outcome.
|
|
8255
|
+
events: result.events
|
|
8084
8256
|
});
|
|
8085
8257
|
}
|
|
8086
8258
|
}).then((unsub) => {
|
|
@@ -8107,8 +8279,7 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8107
8279
|
*/
|
|
8108
8280
|
async submitBid(commitment, userOp) {
|
|
8109
8281
|
try {
|
|
8110
|
-
|
|
8111
|
-
return await this.signAndSendExtrinsic(extrinsic);
|
|
8282
|
+
return await this.signAndSendExtrinsic((api) => api.tx.intentsCoprocessor.placeBid(commitment, userOp));
|
|
8112
8283
|
} catch (error) {
|
|
8113
8284
|
return {
|
|
8114
8285
|
success: false,
|
|
@@ -8126,8 +8297,7 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8126
8297
|
*/
|
|
8127
8298
|
async retractBid(commitment) {
|
|
8128
8299
|
try {
|
|
8129
|
-
|
|
8130
|
-
return await this.signAndSendExtrinsic(extrinsic);
|
|
8300
|
+
return await this.signAndSendExtrinsic((api) => api.tx.intentsCoprocessor.retractBid(commitment));
|
|
8131
8301
|
} catch (error) {
|
|
8132
8302
|
return {
|
|
8133
8303
|
success: false,
|
|
@@ -8157,11 +8327,12 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8157
8327
|
*/
|
|
8158
8328
|
async submitBidWithRetraction(retractCommitment, bidCommitment, userOp) {
|
|
8159
8329
|
try {
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
|
|
8164
|
-
|
|
8330
|
+
return await this.signAndSendExtrinsic(
|
|
8331
|
+
(api) => api.tx.utility.batch([
|
|
8332
|
+
api.tx.intentsCoprocessor.placeBid(bidCommitment, userOp),
|
|
8333
|
+
api.tx.intentsCoprocessor.retractBid(retractCommitment)
|
|
8334
|
+
])
|
|
8335
|
+
);
|
|
8165
8336
|
} catch (error) {
|
|
8166
8337
|
return {
|
|
8167
8338
|
success: false,
|
|
@@ -8169,6 +8340,98 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8169
8340
|
};
|
|
8170
8341
|
}
|
|
8171
8342
|
}
|
|
8343
|
+
/**
|
|
8344
|
+
* Places every phantom bid of one interval in a single extrinsic, retracting each chain's
|
|
8345
|
+
* previous bid alongside it.
|
|
8346
|
+
*
|
|
8347
|
+
* The pallet registers one phantom order per configured chain in the same block, so this is the
|
|
8348
|
+
* whole interval's set. Submitting them one at a time costs a block per chain: submissions are
|
|
8349
|
+
* serialised on the account nonce and each waits for inclusion, so the last chain's bid is many
|
|
8350
|
+
* blocks behind the first, against a bid window measured in tens of blocks. Batched, every bid
|
|
8351
|
+
* lands in the same block.
|
|
8352
|
+
*
|
|
8353
|
+
* Uses `utility.force_batch`, not `utility.batch`. `batch` stops at the first failing call, so
|
|
8354
|
+
* one rejected bid — a closed window, a duplicate, an insufficient deposit — would silently
|
|
8355
|
+
* drop every bid after it. `force_batch` runs them all and reports each outcome. It needs no
|
|
8356
|
+
* special origin: any signed account may call it, exactly like `batch`.
|
|
8357
|
+
*
|
|
8358
|
+
* @param bids - The bids to place; an empty list is a no-op
|
|
8359
|
+
* @returns Per-bid outcomes, in the order given
|
|
8360
|
+
*/
|
|
8361
|
+
async submitPhantomBids(bids) {
|
|
8362
|
+
if (bids.length === 0) return { bids: [] };
|
|
8363
|
+
const placeIndexByBid = [];
|
|
8364
|
+
let callCount = 0;
|
|
8365
|
+
for (const bid of bids) {
|
|
8366
|
+
placeIndexByBid.push(callCount);
|
|
8367
|
+
callCount += bid.retractCommitment ? 2 : 1;
|
|
8368
|
+
}
|
|
8369
|
+
const outcome = await this.signAndSendExtrinsic(
|
|
8370
|
+
(api) => api.tx.utility.forceBatch(
|
|
8371
|
+
bids.flatMap((bid) => {
|
|
8372
|
+
const calls = [api.tx.intentsCoprocessor.placeBid(bid.commitment, bid.userOp)];
|
|
8373
|
+
if (bid.retractCommitment) {
|
|
8374
|
+
calls.push(api.tx.intentsCoprocessor.retractBid(bid.retractCommitment));
|
|
8375
|
+
}
|
|
8376
|
+
return calls;
|
|
8377
|
+
})
|
|
8378
|
+
)
|
|
8379
|
+
);
|
|
8380
|
+
if (!outcome.success) {
|
|
8381
|
+
return {
|
|
8382
|
+
bids: bids.map((bid) => ({ commitment: bid.commitment, success: false, error: outcome.error })),
|
|
8383
|
+
pending: outcome.pending,
|
|
8384
|
+
extrinsicHash: outcome.extrinsicHash,
|
|
8385
|
+
error: outcome.error
|
|
8386
|
+
};
|
|
8387
|
+
}
|
|
8388
|
+
const items = this.readForceBatchItems(outcome.events ?? [], callCount);
|
|
8389
|
+
return {
|
|
8390
|
+
bids: bids.map((bid, index) => {
|
|
8391
|
+
const error = items.errors[placeIndexByBid[index]];
|
|
8392
|
+
return { commitment: bid.commitment, success: !error, error };
|
|
8393
|
+
}),
|
|
8394
|
+
blockHash: outcome.blockHash,
|
|
8395
|
+
extrinsicHash: outcome.extrinsicHash,
|
|
8396
|
+
error: items.error
|
|
8397
|
+
};
|
|
8398
|
+
}
|
|
8399
|
+
/**
|
|
8400
|
+
* Reads one outcome per call out of a force_batch's events.
|
|
8401
|
+
*
|
|
8402
|
+
* `ItemFailed` carries no index — pallet-utility emits exactly one `ItemCompleted` or
|
|
8403
|
+
* `ItemFailed` per call, in call order, so the k-th item event belongs to call k.
|
|
8404
|
+
*
|
|
8405
|
+
* A count that does not match the calls submitted means the events are not the ones assumed
|
|
8406
|
+
* here, and every attribution after the discrepancy would be off by one. The bids are then
|
|
8407
|
+
* reported as placed: a bid wrongly recorded as landed is retracted next interval and the
|
|
8408
|
+
* retraction harmlessly fails with `BidNotFound`, whereas one wrongly recorded as failed is
|
|
8409
|
+
* never retracted at all and leaves its deposit reserved.
|
|
8410
|
+
*/
|
|
8411
|
+
readForceBatchItems(events, callCount) {
|
|
8412
|
+
const errors = [];
|
|
8413
|
+
for (const { event } of events) {
|
|
8414
|
+
if (event.section !== "utility") continue;
|
|
8415
|
+
if (event.method === "ItemCompleted") errors.push(void 0);
|
|
8416
|
+
else if (event.method === "ItemFailed") errors.push(this.describeDispatchError(event.data[0]));
|
|
8417
|
+
}
|
|
8418
|
+
if (errors.length !== callCount) {
|
|
8419
|
+
return {
|
|
8420
|
+
errors: new Array(callCount).fill(void 0),
|
|
8421
|
+
error: `force_batch reported ${errors.length} item events for ${callCount} calls; outcomes not attributed`
|
|
8422
|
+
};
|
|
8423
|
+
}
|
|
8424
|
+
return { errors };
|
|
8425
|
+
}
|
|
8426
|
+
/** Renders a DispatchError as `pallet::Error`, falling back to its raw form. */
|
|
8427
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8428
|
+
describeDispatchError(dispatchError) {
|
|
8429
|
+
if (dispatchError?.isModule) {
|
|
8430
|
+
const decoded = this.api.registry.findMetaError(dispatchError.asModule);
|
|
8431
|
+
return `${decoded.section}::${decoded.name}`;
|
|
8432
|
+
}
|
|
8433
|
+
return dispatchError?.toString() ?? "unknown dispatch error";
|
|
8434
|
+
}
|
|
8172
8435
|
/**
|
|
8173
8436
|
* Fetches all bid storage entries for a given order commitment.
|
|
8174
8437
|
* Returns the on-chain data only (filler addresses and deposits).
|
|
@@ -8177,7 +8440,8 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8177
8440
|
* @returns Array of BidStorageEntry objects
|
|
8178
8441
|
*/
|
|
8179
8442
|
async getBidStorageEntries(commitment) {
|
|
8180
|
-
const
|
|
8443
|
+
const api = await this.http();
|
|
8444
|
+
const entries = await api.query.intentsCoprocessor.bids.entries(commitment);
|
|
8181
8445
|
return entries.map(([storageKey, depositValue]) => ({
|
|
8182
8446
|
commitment,
|
|
8183
8447
|
filler: storageKey.args[1].toString(),
|
|
@@ -8207,9 +8471,8 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8207
8471
|
* Single round-trip but does not include deposit amounts.
|
|
8208
8472
|
*/
|
|
8209
8473
|
async getBidsViaRpc(commitment) {
|
|
8210
|
-
const
|
|
8211
|
-
|
|
8212
|
-
]);
|
|
8474
|
+
const api$1 = await this.http();
|
|
8475
|
+
const result = await api$1._rpcCore.provider.send("intents_getBidsForOrder", [commitment]);
|
|
8213
8476
|
return result.map((entry) => {
|
|
8214
8477
|
const userOp = decodeUserOpScale(entry.user_op);
|
|
8215
8478
|
const filler = new api.Keyring({ type: "sr25519" }).encodeAddress(util.hexToU8a(entry.filler));
|
|
@@ -8221,7 +8484,8 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8221
8484
|
* Slower but works on all nodes and includes deposit amounts.
|
|
8222
8485
|
*/
|
|
8223
8486
|
async getBidsViaStorage(commitment) {
|
|
8224
|
-
const
|
|
8487
|
+
const api = await this.http();
|
|
8488
|
+
const entries = await api.query.intentsCoprocessor.bids.entries(commitment);
|
|
8225
8489
|
if (entries.length === 0) return [];
|
|
8226
8490
|
const bidPromises = entries.map(async ([storageKey, depositValue]) => {
|
|
8227
8491
|
try {
|
|
@@ -8229,7 +8493,7 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8229
8493
|
const deposit = BigInt(depositValue.toString());
|
|
8230
8494
|
const offchainKey = this.buildOffchainBidKey(commitment, filler);
|
|
8231
8495
|
const offchainKeyHex = util.u8aToHex(offchainKey);
|
|
8232
|
-
const offchainResult = await
|
|
8496
|
+
const offchainResult = await api.rpc.offchain.localStorageGet("PERSISTENT", offchainKeyHex);
|
|
8233
8497
|
if (!offchainResult || offchainResult.isNone) return null;
|
|
8234
8498
|
const bidData = offchainResult.unwrap().toHex();
|
|
8235
8499
|
const decoded = this.decodeBid(bidData);
|
|
@@ -8263,7 +8527,8 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8263
8527
|
*/
|
|
8264
8528
|
async fetchPhantomOrder(commitment) {
|
|
8265
8529
|
const key = util.u8aConcat(OFFCHAIN_PHANTOM_PREFIX, util.hexToU8a(commitment));
|
|
8266
|
-
const
|
|
8530
|
+
const api = await this.http();
|
|
8531
|
+
const result = await api.rpc.offchain.localStorageGet("PERSISTENT", util.u8aToHex(key));
|
|
8267
8532
|
if (!result || result.isNone) return null;
|
|
8268
8533
|
const rawHex = result.unwrap().toHex();
|
|
8269
8534
|
if (rawHex === "0x" || rawHex === "0x00") return null;
|
|
@@ -8307,8 +8572,9 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8307
8572
|
* Reads the PhantomOrderRegistered events emitted in a single block.
|
|
8308
8573
|
*/
|
|
8309
8574
|
async getPhantomOrdersInBlock(blockNumber) {
|
|
8310
|
-
const
|
|
8311
|
-
const
|
|
8575
|
+
const api = await this.http();
|
|
8576
|
+
const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
|
|
8577
|
+
const apiAt = await api.at(blockHash);
|
|
8312
8578
|
const records = await apiAt.query.system.events();
|
|
8313
8579
|
const orders = [];
|
|
8314
8580
|
for (const { event } of records) {
|
|
@@ -8329,7 +8595,13 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8329
8595
|
return orders;
|
|
8330
8596
|
}
|
|
8331
8597
|
/**
|
|
8332
|
-
* Polls for newly registered phantom orders, invoking the callback once per
|
|
8598
|
+
* Polls for newly registered phantom orders, invoking the callback once per block that carries
|
|
8599
|
+
* any, with all of that block's orders.
|
|
8600
|
+
*
|
|
8601
|
+
* Per block rather than per order because that is how the pallet writes them: one order per
|
|
8602
|
+
* configured chain, all registered in the same `on_initialize`. Delivering them together lets a
|
|
8603
|
+
* caller bid on the whole interval in one extrinsic (see {@link submitPhantomBids}) instead of
|
|
8604
|
+
* one per chain.
|
|
8333
8605
|
*
|
|
8334
8606
|
* Each tick reads the current head and scans every block between the last one processed and that
|
|
8335
8607
|
* head, so the block cursor — not the connection — determines what has been seen. This replaced a
|
|
@@ -8342,10 +8614,16 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8342
8614
|
* cannot drop them, because the cursor only advances past a block whose events were actually
|
|
8343
8615
|
* read. Recovery replays the backlog.
|
|
8344
8616
|
*
|
|
8617
|
+
* Every read here goes over HTTP, never the websocket. Polling is a sequence of independent
|
|
8618
|
+
* one-shot requests with no state to lose between them, which is exactly what a stateless
|
|
8619
|
+
* transport does well: a request either answers or fails loudly on this tick, instead of a
|
|
8620
|
+
* socket that looks alive while delivering nothing. It also means a websocket outage does not
|
|
8621
|
+
* pause phantom bidding at all — the two transports fail independently.
|
|
8622
|
+
*
|
|
8345
8623
|
* Returns a function that stops polling.
|
|
8346
8624
|
*/
|
|
8347
8625
|
pollPhantomOrders(callback, options = {}) {
|
|
8348
|
-
const { intervalMs
|
|
8626
|
+
const { intervalMs, maxBlocksPerPoll = 500, lookbackBlocks = 0, onError } = options;
|
|
8349
8627
|
let cursor = null;
|
|
8350
8628
|
let inFlight = false;
|
|
8351
8629
|
let stopped = false;
|
|
@@ -8353,7 +8631,7 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8353
8631
|
if (inFlight || stopped) return;
|
|
8354
8632
|
inFlight = true;
|
|
8355
8633
|
try {
|
|
8356
|
-
const head = (await this.
|
|
8634
|
+
const head = (await (await this.http()).rpc.chain.getHeader()).number.toNumber();
|
|
8357
8635
|
if (cursor === null) {
|
|
8358
8636
|
cursor = Math.max(head - 1 - lookbackBlocks, -1);
|
|
8359
8637
|
}
|
|
@@ -8362,7 +8640,7 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8362
8640
|
for (let blockNumber = cursor + 1; blockNumber <= to; blockNumber++) {
|
|
8363
8641
|
if (stopped) return;
|
|
8364
8642
|
const orders = await this.getPhantomOrdersInBlock(blockNumber);
|
|
8365
|
-
|
|
8643
|
+
if (orders.length > 0) callback(orders);
|
|
8366
8644
|
cursor = blockNumber;
|
|
8367
8645
|
}
|
|
8368
8646
|
} catch (err) {
|
|
@@ -8372,12 +8650,31 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8372
8650
|
}
|
|
8373
8651
|
};
|
|
8374
8652
|
void tick();
|
|
8375
|
-
|
|
8653
|
+
let timer = null;
|
|
8654
|
+
const startTimer = (ms) => {
|
|
8655
|
+
if (stopped) return;
|
|
8656
|
+
timer = setInterval(() => void tick(), ms);
|
|
8657
|
+
};
|
|
8658
|
+
if (intervalMs !== void 0) startTimer(intervalMs);
|
|
8659
|
+
else void this.phantomPollIntervalMs().then(startTimer);
|
|
8376
8660
|
return () => {
|
|
8377
8661
|
stopped = true;
|
|
8378
|
-
clearInterval(timer);
|
|
8662
|
+
if (timer) clearInterval(timer);
|
|
8379
8663
|
};
|
|
8380
8664
|
}
|
|
8665
|
+
/**
|
|
8666
|
+
* The poll cadence for the runtime this instance is connected to: Gargantua polls every block,
|
|
8667
|
+
* everything else every 15s. Falls back to the slower cadence if the runtime cannot be read,
|
|
8668
|
+
* since an unreachable node is the poll's problem to report, not the cadence lookup's.
|
|
8669
|
+
*/
|
|
8670
|
+
async phantomPollIntervalMs() {
|
|
8671
|
+
try {
|
|
8672
|
+
const specName = (await this.http()).runtimeVersion.specName.toString();
|
|
8673
|
+
return specName === "gargantua" ? GARGANTUA_PHANTOM_POLL_INTERVAL_MS : PHANTOM_POLL_INTERVAL_MS;
|
|
8674
|
+
} catch {
|
|
8675
|
+
return PHANTOM_POLL_INTERVAL_MS;
|
|
8676
|
+
}
|
|
8677
|
+
}
|
|
8381
8678
|
};
|
|
8382
8679
|
var TronChain = class _TronChain {
|
|
8383
8680
|
constructor(params, evm) {
|
|
@@ -11752,52 +12049,85 @@ query LatestPhantomOrderPriceSnapshot($tokenA: String!, $tokenB: String!) {
|
|
|
11752
12049
|
}
|
|
11753
12050
|
}
|
|
11754
12051
|
}`;
|
|
11755
|
-
var
|
|
11756
|
-
query
|
|
11757
|
-
|
|
12052
|
+
var AVAILABLE_LIQUIDITY = `
|
|
12053
|
+
query AvailableLiquidity(
|
|
12054
|
+
$poolId: String!
|
|
12055
|
+
$sourceChain: String!
|
|
12056
|
+
$destinationChain: String!
|
|
12057
|
+
$direction: String!
|
|
12058
|
+
) {
|
|
12059
|
+
poolChainLiquidities(
|
|
11758
12060
|
filter: {
|
|
11759
12061
|
and: [
|
|
11760
|
-
{
|
|
11761
|
-
{
|
|
12062
|
+
{ poolId: { equalToInsensitive: $poolId } }
|
|
12063
|
+
{ chain: { equalTo: $destinationChain } }
|
|
12064
|
+
{ direction: { equalTo: $direction } }
|
|
11762
12065
|
]
|
|
11763
12066
|
}
|
|
11764
|
-
orderBy: SNAPSHOT_TIME_DESC
|
|
11765
12067
|
first: 1
|
|
11766
12068
|
) {
|
|
11767
12069
|
nodes {
|
|
11768
|
-
|
|
11769
|
-
|
|
11770
|
-
|
|
11771
|
-
|
|
12070
|
+
depth
|
|
12071
|
+
bidCount
|
|
12072
|
+
unrestrictedDepth
|
|
12073
|
+
unrestrictedBidCount
|
|
12074
|
+
lastUpdatedAt
|
|
12075
|
+
}
|
|
12076
|
+
}
|
|
12077
|
+
poolRoutes(
|
|
12078
|
+
filter: {
|
|
12079
|
+
and: [
|
|
12080
|
+
{ poolId: { equalToInsensitive: $poolId } }
|
|
12081
|
+
{ sourceChain: { equalTo: $sourceChain } }
|
|
12082
|
+
{ chain: { equalTo: $destinationChain } }
|
|
12083
|
+
{ direction: { equalTo: $direction } }
|
|
12084
|
+
]
|
|
12085
|
+
}
|
|
12086
|
+
first: 1
|
|
12087
|
+
) {
|
|
12088
|
+
nodes {
|
|
12089
|
+
depth
|
|
12090
|
+
bidCount
|
|
12091
|
+
lastUpdatedAt
|
|
11772
12092
|
}
|
|
11773
12093
|
}
|
|
11774
12094
|
}`;
|
|
11775
|
-
var
|
|
11776
|
-
query
|
|
11777
|
-
|
|
12095
|
+
var BUY_AND_SELL_RATES = `
|
|
12096
|
+
query BuyAndSellRates(
|
|
12097
|
+
$poolId: String!
|
|
12098
|
+
$directChain: String!
|
|
12099
|
+
$directDirection: String!
|
|
12100
|
+
$reverseChain: String!
|
|
12101
|
+
$reverseDirection: String!
|
|
12102
|
+
) {
|
|
12103
|
+
direct: poolChainLiquidities(
|
|
11778
12104
|
filter: {
|
|
11779
12105
|
and: [
|
|
11780
|
-
{
|
|
11781
|
-
{
|
|
12106
|
+
{ poolId: { equalToInsensitive: $poolId } }
|
|
12107
|
+
{ chain: { equalTo: $directChain } }
|
|
12108
|
+
{ direction: { equalTo: $directDirection } }
|
|
11782
12109
|
]
|
|
11783
12110
|
}
|
|
12111
|
+
first: 1
|
|
11784
12112
|
) {
|
|
11785
|
-
|
|
11786
|
-
|
|
11787
|
-
|
|
11788
|
-
}
|
|
11789
|
-
distinctCount {
|
|
11790
|
-
providerId
|
|
11791
|
-
}
|
|
12113
|
+
nodes {
|
|
12114
|
+
rate
|
|
12115
|
+
lastUpdatedAt
|
|
11792
12116
|
}
|
|
11793
|
-
|
|
11794
|
-
|
|
11795
|
-
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
11799
|
-
|
|
11800
|
-
|
|
12117
|
+
}
|
|
12118
|
+
reverse: poolChainLiquidities(
|
|
12119
|
+
filter: {
|
|
12120
|
+
and: [
|
|
12121
|
+
{ poolId: { equalToInsensitive: $poolId } }
|
|
12122
|
+
{ chain: { equalTo: $reverseChain } }
|
|
12123
|
+
{ direction: { equalTo: $reverseDirection } }
|
|
12124
|
+
]
|
|
12125
|
+
}
|
|
12126
|
+
first: 1
|
|
12127
|
+
) {
|
|
12128
|
+
nodes {
|
|
12129
|
+
rate
|
|
12130
|
+
lastUpdatedAt
|
|
11801
12131
|
}
|
|
11802
12132
|
}
|
|
11803
12133
|
}`;
|
|
@@ -15179,6 +15509,12 @@ var CryptoUtils = class _CryptoUtils {
|
|
|
15179
15509
|
* signing infrastructure (hardware wallets, MPC/TEE policy engines) instead
|
|
15180
15510
|
* of an opaque 32-byte digest.
|
|
15181
15511
|
*
|
|
15512
|
+
* The payload must be a standard self-describing `eth_signTypedData_v4`
|
|
15513
|
+
* payload — `EIP712Domain` listed in `types`, `chainId` as a JSON number —
|
|
15514
|
+
* because some signing backends (e.g. MPC Vault) hash it server-side from
|
|
15515
|
+
* the JSON rather than locally via viem. viem ignores both details when
|
|
15516
|
+
* hashing, so the digest is unchanged for local signers.
|
|
15517
|
+
*
|
|
15182
15518
|
* @param userOp - The packed UserOperation to sign (signature field ignored).
|
|
15183
15519
|
* @param entryPoint - Address of the EntryPoint v0.8 contract.
|
|
15184
15520
|
* @param chainId - Chain ID of the network on which the operation will execute.
|
|
@@ -15189,10 +15525,22 @@ var CryptoUtils = class _CryptoUtils {
|
|
|
15189
15525
|
domain: {
|
|
15190
15526
|
name: "ERC4337",
|
|
15191
15527
|
version: "1",
|
|
15192
|
-
chainId
|
|
15528
|
+
// Runtime number so JSON.stringify emits a canonical v4 numeric chainId for
|
|
15529
|
+
// server-side hashers; viem's uint256 type mapping wants bigint but its
|
|
15530
|
+
// runtime accepts numbers, hence the cast.
|
|
15531
|
+
chainId: Number(chainId),
|
|
15193
15532
|
verifyingContract: entryPoint
|
|
15194
15533
|
},
|
|
15534
|
+
// `as const`: viem derives the domain's TYPE from `types.EIP712Domain`, so the
|
|
15535
|
+
// entries must stay string literals — widened `string` fields make viem's
|
|
15536
|
+
// typed-data generics reject the payload at every call site.
|
|
15195
15537
|
types: {
|
|
15538
|
+
EIP712Domain: [
|
|
15539
|
+
{ name: "name", type: "string" },
|
|
15540
|
+
{ name: "version", type: "string" },
|
|
15541
|
+
{ name: "chainId", type: "uint256" },
|
|
15542
|
+
{ name: "verifyingContract", type: "address" }
|
|
15543
|
+
],
|
|
15196
15544
|
PackedUserOperation: [
|
|
15197
15545
|
{ name: "sender", type: "address" },
|
|
15198
15546
|
{ name: "nonce", type: "uint256" },
|
|
@@ -17923,178 +18271,191 @@ var OrderStatusChecker = class {
|
|
|
17923
18271
|
return true;
|
|
17924
18272
|
}
|
|
17925
18273
|
};
|
|
17926
|
-
|
|
18274
|
+
|
|
18275
|
+
// src/protocols/intents/liquidity-pool.ts
|
|
18276
|
+
function sortPoolSymbols(symbolA, symbolB) {
|
|
18277
|
+
return symbolA.toLowerCase() <= symbolB.toLowerCase() ? [symbolA, symbolB] : [symbolB, symbolA];
|
|
18278
|
+
}
|
|
18279
|
+
function poolSlug(symbolA, symbolB) {
|
|
18280
|
+
return sortPoolSymbols(symbolA, symbolB).join("-");
|
|
18281
|
+
}
|
|
18282
|
+
function resolveLiquidityPool(symbolA, symbolB) {
|
|
18283
|
+
const [token0Symbol, token1Symbol] = sortPoolSymbols(symbolA, symbolB);
|
|
18284
|
+
return {
|
|
18285
|
+
poolId: `${token0Symbol}-${token1Symbol}`,
|
|
18286
|
+
token0Symbol,
|
|
18287
|
+
token1Symbol
|
|
18288
|
+
};
|
|
18289
|
+
}
|
|
18290
|
+
|
|
18291
|
+
// src/protocols/intents/LiquidityEngine.ts
|
|
18292
|
+
var INDEXER_FIXED_POINT_DECIMALS = 18;
|
|
18293
|
+
var POOL_RATE_SCALE = 10n ** 18n;
|
|
18294
|
+
var SELL = "SELL";
|
|
18295
|
+
var BUY = "BUY";
|
|
18296
|
+
var USD_STABLE_SYMBOLS = /* @__PURE__ */ new Set(["USDC", "USDT"]);
|
|
17927
18297
|
var LiquidityEngine = class {
|
|
17928
|
-
|
|
17929
|
-
* @param queryClient - Nexus GraphQL client attached to the gateway.
|
|
17930
|
-
* @param chainConfigService - Resolves token decimals for formatted results.
|
|
17931
|
-
*/
|
|
17932
|
-
constructor(queryClient, chainConfigService) {
|
|
18298
|
+
constructor(queryClient) {
|
|
17933
18299
|
this.queryClient = queryClient;
|
|
17934
|
-
this.chainConfigService = chainConfigService;
|
|
17935
18300
|
}
|
|
17936
18301
|
queryClient;
|
|
17937
|
-
chainConfigService;
|
|
17938
18302
|
/**
|
|
17939
|
-
*
|
|
17940
|
-
* indexed output-token liquidity.
|
|
18303
|
+
* Returns liquidity reachable from one source chain on one destination.
|
|
17941
18304
|
*
|
|
17942
|
-
*
|
|
17943
|
-
*
|
|
17944
|
-
*
|
|
17945
|
-
* that chain's token decimals. They describe `snapshotTime`, not live
|
|
17946
|
-
* reservations or fill guarantees.
|
|
18305
|
+
* The caller resolves chain-specific token addresses through chain
|
|
18306
|
+
* configuration; this layer only maps those configured symbols onto the
|
|
18307
|
+
* indexer's canonical pool and route fields.
|
|
17947
18308
|
*
|
|
17948
|
-
*
|
|
17949
|
-
*
|
|
17950
|
-
*
|
|
17951
|
-
* @
|
|
17952
|
-
*
|
|
17953
|
-
*/
|
|
17954
|
-
async
|
|
17955
|
-
const
|
|
17956
|
-
const
|
|
17957
|
-
const
|
|
17958
|
-
|
|
17959
|
-
|
|
17960
|
-
|
|
17961
|
-
|
|
17962
|
-
|
|
17963
|
-
const
|
|
17964
|
-
if (!
|
|
17965
|
-
throw new
|
|
17966
|
-
}
|
|
17967
|
-
if (node.tokenA.toLowerCase() !== tokenIn || node.tokenB.toLowerCase() !== tokenOut) {
|
|
17968
|
-
throw new InvalidAvailableLiquiditySnapshotError(commitment, "snapshot token pair does not match the query");
|
|
17969
|
-
}
|
|
17970
|
-
const snapshotTime = new Date(dateStringtoTimestamp(node.snapshotTime));
|
|
17971
|
-
if (Number.isNaN(snapshotTime.getTime())) {
|
|
17972
|
-
throw new InvalidAvailableLiquiditySnapshotError(commitment, "snapshotTime is invalid");
|
|
18309
|
+
* Destination, unrestricted, and explicit-route capacity are returned as
|
|
18310
|
+
* separate values so callers can apply their own source-chain policy.
|
|
18311
|
+
*
|
|
18312
|
+
* @returns `undefined` only when the indexer has not published a destination
|
|
18313
|
+
* pool sample yet.
|
|
18314
|
+
*/
|
|
18315
|
+
async getAvailableLiquidity(params) {
|
|
18316
|
+
const pool = resolveLiquidityPool(params.source.symbol, params.destination.symbol);
|
|
18317
|
+
const direction = params.source.symbol.toLowerCase() === pool.token0Symbol.toLowerCase() ? SELL : BUY;
|
|
18318
|
+
const variables = {
|
|
18319
|
+
poolId: pool.poolId,
|
|
18320
|
+
sourceChain: params.source.chain,
|
|
18321
|
+
destinationChain: params.destination.chain,
|
|
18322
|
+
direction
|
|
18323
|
+
};
|
|
18324
|
+
const response = await this.queryClient.request(AVAILABLE_LIQUIDITY, variables);
|
|
18325
|
+
if (!response?.poolChainLiquidities?.nodes || !response?.poolRoutes?.nodes) {
|
|
18326
|
+
throw new InvalidLiquidityIndexerResponseError("liquidity connections are missing");
|
|
17973
18327
|
}
|
|
17974
|
-
const
|
|
17975
|
-
|
|
17976
|
-
|
|
17977
|
-
});
|
|
18328
|
+
const chainLiquidity = response.poolChainLiquidities.nodes[0];
|
|
18329
|
+
if (!chainLiquidity) return void 0;
|
|
18330
|
+
const route = response.poolRoutes.nodes[0];
|
|
17978
18331
|
return {
|
|
17979
|
-
|
|
17980
|
-
|
|
17981
|
-
tokenAddress:
|
|
17982
|
-
|
|
17983
|
-
|
|
17984
|
-
|
|
17985
|
-
|
|
17986
|
-
|
|
18332
|
+
sourceChain: params.source.chain,
|
|
18333
|
+
destinationChain: params.destination.chain,
|
|
18334
|
+
tokenAddress: normalizeEvmAddress(params.destination.address, "destination token"),
|
|
18335
|
+
updatedAt: readIndexerDate(chainLiquidity.lastUpdatedAt, "destination lastUpdatedAt"),
|
|
18336
|
+
destination: readLiquiditySlice(chainLiquidity.depth, chainLiquidity.bidCount, "destination"),
|
|
18337
|
+
unrestricted: readLiquiditySlice(
|
|
18338
|
+
chainLiquidity.unrestrictedDepth,
|
|
18339
|
+
chainLiquidity.unrestrictedBidCount,
|
|
18340
|
+
"unrestricted"
|
|
18341
|
+
),
|
|
18342
|
+
explicitRoute: route ? {
|
|
18343
|
+
...readLiquiditySlice(route.depth, route.bidCount, "explicit route"),
|
|
18344
|
+
updatedAt: readIndexerDate(route.lastUpdatedAt, "route lastUpdatedAt")
|
|
18345
|
+
} : null
|
|
17987
18346
|
};
|
|
17988
18347
|
}
|
|
17989
18348
|
/**
|
|
17990
|
-
*
|
|
17991
|
-
*
|
|
18349
|
+
* Returns chain-specific buy and sell rates in less-valued quote-token units
|
|
18350
|
+
* per one base token.
|
|
17992
18351
|
*
|
|
17993
|
-
*
|
|
17994
|
-
|
|
17995
|
-
|
|
17996
|
-
|
|
17997
|
-
|
|
17998
|
-
|
|
17999
|
-
);
|
|
18000
|
-
const
|
|
18001
|
-
const
|
|
18002
|
-
|
|
18003
|
-
|
|
18004
|
-
|
|
18005
|
-
|
|
18006
|
-
|
|
18007
|
-
}
|
|
18008
|
-
const totalLiquidity = parseSnapshotBigInt(aggregates.sum.balance ?? "0", params.commitment, "total balance");
|
|
18009
|
-
const providerCount = parseProviderCount(aggregates.distinctCount.providerId, params.commitment, "total");
|
|
18010
|
-
const liquidityByChain = connection.groupedAggregates.map((group, index) => {
|
|
18011
|
-
const [chain, tokenAddress] = group.keys;
|
|
18012
|
-
if (!chain?.trim() || !tokenAddress) {
|
|
18013
|
-
throw new InvalidAvailableLiquiditySnapshotError(
|
|
18014
|
-
params.commitment,
|
|
18015
|
-
`liquidity group ${index} has invalid keys`
|
|
18016
|
-
);
|
|
18017
|
-
}
|
|
18018
|
-
const normalizedTokenAddress = normalizeIndexedLiquidityAddress(
|
|
18019
|
-
tokenAddress,
|
|
18020
|
-
params.commitment,
|
|
18021
|
-
`liquidity group ${index} tokenAddress`
|
|
18022
|
-
);
|
|
18023
|
-
if (normalizedTokenAddress !== params.tokenAddress) {
|
|
18024
|
-
throw new InvalidAvailableLiquiditySnapshotError(
|
|
18025
|
-
params.commitment,
|
|
18026
|
-
`liquidity group ${index} tokenAddress does not match the snapshot output token`
|
|
18027
|
-
);
|
|
18028
|
-
}
|
|
18029
|
-
return {
|
|
18030
|
-
chain: chain.trim(),
|
|
18031
|
-
tokenAddress: normalizedTokenAddress,
|
|
18032
|
-
totalLiquidity: parseSnapshotBigInt(
|
|
18033
|
-
group.sum.balance ?? "0",
|
|
18034
|
-
params.commitment,
|
|
18035
|
-
`liquidity group ${index} balance`
|
|
18036
|
-
),
|
|
18037
|
-
providerCount: parseProviderCount(
|
|
18038
|
-
group.distinctCount.providerId,
|
|
18039
|
-
params.commitment,
|
|
18040
|
-
`liquidity group ${index}`
|
|
18041
|
-
)
|
|
18042
|
-
};
|
|
18352
|
+
* The requested direction is read on the destination chain; its reverse is
|
|
18353
|
+
* read on the source chain. This mirrors where each direction's output token
|
|
18354
|
+
* must be delivered for a cross-chain trade.
|
|
18355
|
+
*/
|
|
18356
|
+
async getBuyAndSellRates(params) {
|
|
18357
|
+
const pool = resolveLiquidityPool(params.tokenInSymbol, params.tokenOutSymbol);
|
|
18358
|
+
const directDirection = params.tokenInSymbol.toLowerCase() === pool.token0Symbol.toLowerCase() ? SELL : BUY;
|
|
18359
|
+
const reverseDirection = directDirection === SELL ? BUY : SELL;
|
|
18360
|
+
const response = await this.queryClient.request(BUY_AND_SELL_RATES, {
|
|
18361
|
+
poolId: pool.poolId,
|
|
18362
|
+
directChain: params.destinationChain,
|
|
18363
|
+
directDirection,
|
|
18364
|
+
reverseChain: params.sourceChain,
|
|
18365
|
+
reverseDirection
|
|
18043
18366
|
});
|
|
18044
|
-
|
|
18045
|
-
|
|
18046
|
-
|
|
18047
|
-
|
|
18048
|
-
|
|
18049
|
-
|
|
18050
|
-
|
|
18051
|
-
|
|
18367
|
+
if (!response?.direct?.nodes || !response?.reverse?.nodes) {
|
|
18368
|
+
throw new InvalidLiquidityIndexerResponseError("rate connections are missing");
|
|
18369
|
+
}
|
|
18370
|
+
const direct = readIndexedRate(response.direct.nodes[0], "direct rate");
|
|
18371
|
+
const reverse = readIndexedRate(response.reverse.nodes[0], "reverse rate");
|
|
18372
|
+
if (!direct && !reverse) return void 0;
|
|
18373
|
+
const quoteTokenSymbol = resolveQuoteTokenSymbol(
|
|
18374
|
+
params.tokenInSymbol,
|
|
18375
|
+
params.tokenOutSymbol,
|
|
18376
|
+
direct?.scaledRate,
|
|
18377
|
+
reverse?.scaledRate
|
|
18378
|
+
);
|
|
18379
|
+
const quoteIsTokenOut = quoteTokenSymbol === params.tokenOutSymbol;
|
|
18380
|
+
const buy = quoteIsTokenOut ? direct : reverse;
|
|
18381
|
+
const sell = quoteIsTokenOut ? reverse : direct;
|
|
18382
|
+
return {
|
|
18383
|
+
baseTokenSymbol: quoteIsTokenOut ? params.tokenInSymbol : params.tokenOutSymbol,
|
|
18384
|
+
quoteTokenSymbol,
|
|
18385
|
+
sourceChain: params.sourceChain,
|
|
18386
|
+
destinationChain: params.destinationChain,
|
|
18387
|
+
buyRate: buy ? viem.formatUnits(buy.scaledRate, INDEXER_FIXED_POINT_DECIMALS) : null,
|
|
18388
|
+
sellRate: sell ? viem.formatUnits(reciprocalRate(sell.scaledRate, "sell rate"), INDEXER_FIXED_POINT_DECIMALS) : null,
|
|
18389
|
+
buyRateUpdatedAt: buy?.updatedAt ?? null,
|
|
18390
|
+
sellRateUpdatedAt: sell?.updatedAt ?? null
|
|
18391
|
+
};
|
|
18052
18392
|
}
|
|
18053
|
-
|
|
18054
|
-
|
|
18055
|
-
|
|
18056
|
-
|
|
18057
|
-
|
|
18058
|
-
commitment,
|
|
18059
|
-
`token decimals are not configured for ${tokenAddress} on ${chain}`
|
|
18060
|
-
);
|
|
18061
|
-
}
|
|
18062
|
-
return viem.formatUnits(amount, decimals);
|
|
18393
|
+
};
|
|
18394
|
+
var InvalidLiquidityIndexerResponseError = class extends Error {
|
|
18395
|
+
constructor(reason) {
|
|
18396
|
+
super(`Invalid liquidity indexer response: ${reason}`);
|
|
18397
|
+
this.name = "InvalidLiquidityIndexerResponseError";
|
|
18063
18398
|
}
|
|
18064
18399
|
};
|
|
18065
|
-
var
|
|
18066
|
-
|
|
18067
|
-
|
|
18068
|
-
|
|
18069
|
-
this.name = "InvalidAvailableLiquiditySnapshotError";
|
|
18400
|
+
var UnsupportedLiquidityAssetError = class extends Error {
|
|
18401
|
+
constructor(chain, asset) {
|
|
18402
|
+
super(`No configured liquidity asset found for ${asset} on ${chain}`);
|
|
18403
|
+
this.name = "UnsupportedLiquidityAssetError";
|
|
18070
18404
|
}
|
|
18071
18405
|
};
|
|
18072
|
-
|
|
18406
|
+
var UnsupportedLiquidityChainError = class extends Error {
|
|
18407
|
+
constructor(chainId) {
|
|
18408
|
+
super(`No configured liquidity chain found for chain ID ${chainId}`);
|
|
18409
|
+
this.name = "UnsupportedLiquidityChainError";
|
|
18410
|
+
}
|
|
18411
|
+
};
|
|
18412
|
+
function readLiquiditySlice(depth, providerCount, label) {
|
|
18413
|
+
if (!Number.isSafeInteger(providerCount) || providerCount < 0) {
|
|
18414
|
+
throw new InvalidLiquidityIndexerResponseError(`${label} provider count is invalid`);
|
|
18415
|
+
}
|
|
18416
|
+
return { totalLiquidity: formatIndexerAmount(depth, `${label} depth`), providerCount };
|
|
18417
|
+
}
|
|
18418
|
+
function formatIndexerAmount(value, label) {
|
|
18073
18419
|
try {
|
|
18074
18420
|
const amount = BigInt(value);
|
|
18075
|
-
if (amount < 0n)
|
|
18076
|
-
|
|
18077
|
-
|
|
18078
|
-
|
|
18079
|
-
} catch (error) {
|
|
18080
|
-
if (error instanceof InvalidAvailableLiquiditySnapshotError) throw error;
|
|
18081
|
-
throw new InvalidAvailableLiquiditySnapshotError(commitment, `${field} is not an integer`);
|
|
18421
|
+
if (amount < 0n) throw new Error();
|
|
18422
|
+
return viem.formatUnits(amount, INDEXER_FIXED_POINT_DECIMALS);
|
|
18423
|
+
} catch {
|
|
18424
|
+
throw new InvalidLiquidityIndexerResponseError(`${label} is not a non-negative integer`);
|
|
18082
18425
|
}
|
|
18083
18426
|
}
|
|
18084
|
-
function
|
|
18085
|
-
const
|
|
18086
|
-
if (
|
|
18087
|
-
|
|
18088
|
-
}
|
|
18089
|
-
return count;
|
|
18427
|
+
function readIndexerDate(value, label) {
|
|
18428
|
+
const date = new Date(dateStringtoTimestamp(value));
|
|
18429
|
+
if (Number.isNaN(date.getTime())) throw new InvalidLiquidityIndexerResponseError(`${label} is invalid`);
|
|
18430
|
+
return date;
|
|
18090
18431
|
}
|
|
18091
|
-
function
|
|
18432
|
+
function readIndexedRate(node, label) {
|
|
18433
|
+
if (!node) return void 0;
|
|
18092
18434
|
try {
|
|
18093
|
-
|
|
18094
|
-
|
|
18095
|
-
|
|
18435
|
+
const scaledRate = BigInt(node.rate);
|
|
18436
|
+
if (scaledRate <= 0n) throw new Error();
|
|
18437
|
+
return {
|
|
18438
|
+
scaledRate,
|
|
18439
|
+
updatedAt: readIndexerDate(node.lastUpdatedAt, `${label} lastUpdatedAt`)
|
|
18440
|
+
};
|
|
18441
|
+
} catch (error) {
|
|
18442
|
+
if (error instanceof InvalidLiquidityIndexerResponseError) throw error;
|
|
18443
|
+
throw new InvalidLiquidityIndexerResponseError(`${label} is not a positive integer`);
|
|
18096
18444
|
}
|
|
18097
18445
|
}
|
|
18446
|
+
function resolveQuoteTokenSymbol(tokenInSymbol, tokenOutSymbol, directRate, reverseRate) {
|
|
18447
|
+
const inputIsUsdStable = USD_STABLE_SYMBOLS.has(tokenInSymbol);
|
|
18448
|
+
const outputIsUsdStable = USD_STABLE_SYMBOLS.has(tokenOutSymbol);
|
|
18449
|
+
if (inputIsUsdStable !== outputIsUsdStable) return inputIsUsdStable ? tokenOutSymbol : tokenInSymbol;
|
|
18450
|
+
if (directRate !== void 0) return directRate >= POOL_RATE_SCALE ? tokenOutSymbol : tokenInSymbol;
|
|
18451
|
+
if (reverseRate !== void 0) return reverseRate >= POOL_RATE_SCALE ? tokenInSymbol : tokenOutSymbol;
|
|
18452
|
+
throw new InvalidLiquidityIndexerResponseError("cannot orient an empty rate pair");
|
|
18453
|
+
}
|
|
18454
|
+
function reciprocalRate(rate, label) {
|
|
18455
|
+
const reciprocal = POOL_RATE_SCALE * POOL_RATE_SCALE / rate;
|
|
18456
|
+
if (reciprocal <= 0n) throw new InvalidLiquidityIndexerResponseError(`${label} reciprocal underflowed`);
|
|
18457
|
+
return reciprocal;
|
|
18458
|
+
}
|
|
18098
18459
|
|
|
18099
18460
|
// src/protocols/intents/quote/types.ts
|
|
18100
18461
|
var UnsupportedIntentQuoteStrategyError = class extends Error {
|
|
@@ -18519,8 +18880,6 @@ var IntentGateway = class _IntentGateway {
|
|
|
18519
18880
|
gasEstimator;
|
|
18520
18881
|
/** Quote strategies for pricing orders before placement, keyed by strategy name. */
|
|
18521
18882
|
quoteStrategies;
|
|
18522
|
-
/** Resolves order tokens to canonical Phantom snapshot market pairs. */
|
|
18523
|
-
phantomSnapshotPairResolver;
|
|
18524
18883
|
/**
|
|
18525
18884
|
* Private constructor — use {@link IntentGateway.create} instead.
|
|
18526
18885
|
*
|
|
@@ -18564,7 +18923,6 @@ var IntentGateway = class _IntentGateway {
|
|
|
18564
18923
|
this.bidManager = bidManager;
|
|
18565
18924
|
this.gasEstimator = gasEstimator;
|
|
18566
18925
|
this._crypto = crypto;
|
|
18567
|
-
this.phantomSnapshotPairResolver = new PhantomSnapshotPairResolver(dest.configService);
|
|
18568
18926
|
this.quoteStrategies = {
|
|
18569
18927
|
phantom_snapshot: new PhantomSnapshotIntentQuoteStrategy(
|
|
18570
18928
|
dest.configService,
|
|
@@ -18644,33 +19002,62 @@ var IntentGateway = class _IntentGateway {
|
|
|
18644
19002
|
return handler.quote({ ...params, strategy }, source, destination);
|
|
18645
19003
|
}
|
|
18646
19004
|
/**
|
|
18647
|
-
* Returns
|
|
18648
|
-
* Phantom snapshot for this gateway's source and destination.
|
|
19005
|
+
* Returns indexed destination liquidity and its source-routing slices.
|
|
18649
19006
|
*
|
|
18650
|
-
*
|
|
18651
|
-
*
|
|
18652
|
-
*
|
|
18653
|
-
*
|
|
19007
|
+
* Destination, unrestricted, and explicit-route capacity come exclusively
|
|
19008
|
+
* from the indexer's pair-centric liquidity entities. The SDK does not decide
|
|
19009
|
+
* whether unrestricted bidders cover the source chain. Amounts reflect the
|
|
19010
|
+
* latest rolling sample; they are not reservations or fill guarantees.
|
|
18654
19011
|
*
|
|
18655
19012
|
* Requires a prior call to {@link withQueryClient}.
|
|
18656
19013
|
*/
|
|
18657
19014
|
async queryAvailableLiquidity(params) {
|
|
18658
19015
|
const { queryClient } = this.requireIndexer();
|
|
18659
|
-
const sourceStateMachineId = this.source.config.stateMachineId;
|
|
18660
|
-
const destinationStateMachineId = this.dest.config.stateMachineId;
|
|
18661
|
-
|
|
18662
|
-
if (!
|
|
18663
|
-
|
|
18664
|
-
|
|
18665
|
-
|
|
18666
|
-
|
|
18667
|
-
|
|
18668
|
-
|
|
18669
|
-
|
|
18670
|
-
|
|
18671
|
-
|
|
18672
|
-
|
|
18673
|
-
|
|
19016
|
+
const sourceStateMachineId = getConfigByStateMachineId(this.source.config.stateMachineId)?.stateMachineId;
|
|
19017
|
+
const destinationStateMachineId = getConfigByStateMachineId(this.dest.config.stateMachineId)?.stateMachineId;
|
|
19018
|
+
if (!sourceStateMachineId) throw new UnsupportedLiquidityChainError(this.source.config.stateMachineId);
|
|
19019
|
+
if (!destinationStateMachineId) throw new UnsupportedLiquidityChainError(this.dest.config.stateMachineId);
|
|
19020
|
+
const sourceToken = this.source.configService.getAssetMetadataByAddress(sourceStateMachineId, params.tokenIn);
|
|
19021
|
+
const destinationToken = this.dest.configService.getAssetMetadataByAddress(
|
|
19022
|
+
destinationStateMachineId,
|
|
19023
|
+
params.tokenOut
|
|
19024
|
+
);
|
|
19025
|
+
if (!sourceToken) throw new UnsupportedLiquidityAssetError(sourceStateMachineId, params.tokenIn);
|
|
19026
|
+
if (!destinationToken) throw new UnsupportedLiquidityAssetError(destinationStateMachineId, params.tokenOut);
|
|
19027
|
+
return new LiquidityEngine(queryClient).getAvailableLiquidity({
|
|
19028
|
+
source: {
|
|
19029
|
+
chain: sourceStateMachineId,
|
|
19030
|
+
...sourceToken
|
|
19031
|
+
},
|
|
19032
|
+
destination: {
|
|
19033
|
+
chain: destinationStateMachineId,
|
|
19034
|
+
...destinationToken
|
|
19035
|
+
}
|
|
19036
|
+
});
|
|
19037
|
+
}
|
|
19038
|
+
/**
|
|
19039
|
+
* Returns chain-specific buy and sell rates in less-valued quote-token units
|
|
19040
|
+
* without requiring token addresses. Symbols are matched case-insensitively;
|
|
19041
|
+
* chain IDs are numeric IDs for chains configured in the SDK.
|
|
19042
|
+
*/
|
|
19043
|
+
async queryBuyAndSellRates(params) {
|
|
19044
|
+
const { queryClient } = this.requireIndexer();
|
|
19045
|
+
const sourceChain = chainConfigs[params.sourceChainId]?.stateMachineId;
|
|
19046
|
+
const destinationChain = chainConfigs[params.destinationChainId]?.stateMachineId;
|
|
19047
|
+
if (!sourceChain) throw new UnsupportedLiquidityChainError(params.sourceChainId);
|
|
19048
|
+
if (!destinationChain) throw new UnsupportedLiquidityChainError(params.destinationChainId);
|
|
19049
|
+
const sourceToken = this.source.configService.getAssetMetadataBySymbol(sourceChain, params.tokenInSymbol);
|
|
19050
|
+
const destinationToken = this.dest.configService.getAssetMetadataBySymbol(
|
|
19051
|
+
destinationChain,
|
|
19052
|
+
params.tokenOutSymbol
|
|
19053
|
+
);
|
|
19054
|
+
if (!sourceToken) throw new UnsupportedLiquidityAssetError(sourceChain, params.tokenInSymbol);
|
|
19055
|
+
if (!destinationToken) throw new UnsupportedLiquidityAssetError(destinationChain, params.tokenOutSymbol);
|
|
19056
|
+
return new LiquidityEngine(queryClient).getBuyAndSellRates({
|
|
19057
|
+
sourceChain,
|
|
19058
|
+
destinationChain,
|
|
19059
|
+
tokenInSymbol: sourceToken.symbol,
|
|
19060
|
+
tokenOutSymbol: destinationToken.symbol
|
|
18674
19061
|
});
|
|
18675
19062
|
}
|
|
18676
19063
|
/**
|
|
@@ -19210,14 +19597,34 @@ var IntentGateway = class _IntentGateway {
|
|
|
19210
19597
|
}
|
|
19211
19598
|
}
|
|
19212
19599
|
};
|
|
19600
|
+
|
|
19601
|
+
// src/protocols/intents/phantom-aggregation.ts
|
|
19213
19602
|
var FILL_ORDER_ABI = IntentGatewayV2_default.ABI;
|
|
19214
|
-
var
|
|
19215
|
-
var
|
|
19216
|
-
|
|
19217
|
-
|
|
19218
|
-
|
|
19603
|
+
var DECLARATION_V1 = 1;
|
|
19604
|
+
var DECLARATION_V2 = 2;
|
|
19605
|
+
var MAX_DECLARED_ENTRIES = 255;
|
|
19606
|
+
var MAX_TOKEN_ID_BYTES = 32;
|
|
19607
|
+
function tokenIdToBytes(tokenId) {
|
|
19608
|
+
if (tokenId < 0n) throw new Error(`Uniswap V4 tokenId cannot be negative: ${tokenId}`);
|
|
19609
|
+
const bytes = [];
|
|
19610
|
+
let rest = tokenId;
|
|
19611
|
+
while (rest > 0n) {
|
|
19612
|
+
bytes.unshift(Number(rest & 0xffn));
|
|
19613
|
+
rest >>= 8n;
|
|
19614
|
+
}
|
|
19615
|
+
return bytes.length > 0 ? bytes : [0];
|
|
19616
|
+
}
|
|
19617
|
+
function encodePhantomBidDeclaration(declaration) {
|
|
19618
|
+
const chains2 = declaration.acceptedSourceChains ?? [];
|
|
19619
|
+
const positions = declaration.uniswapV4Positions ?? [];
|
|
19620
|
+
if (chains2.length > MAX_DECLARED_ENTRIES) {
|
|
19621
|
+
throw new Error(`Cannot declare more than ${MAX_DECLARED_ENTRIES} source chains`);
|
|
19219
19622
|
}
|
|
19220
|
-
|
|
19623
|
+
if (positions.length > MAX_DECLARED_ENTRIES) {
|
|
19624
|
+
throw new Error(`Cannot declare more than ${MAX_DECLARED_ENTRIES} Uniswap V4 positions`);
|
|
19625
|
+
}
|
|
19626
|
+
const version = positions.length > 0 ? DECLARATION_V2 : DECLARATION_V1;
|
|
19627
|
+
const bytes = [version, chains2.length];
|
|
19221
19628
|
for (const chain of chains2) {
|
|
19222
19629
|
const encoded = util.stringToU8a(chain);
|
|
19223
19630
|
if (encoded.length === 0 || encoded.length > 255) {
|
|
@@ -19225,25 +19632,59 @@ function encodeAcceptedSourceChains(chains2) {
|
|
|
19225
19632
|
}
|
|
19226
19633
|
bytes.push(encoded.length, ...encoded);
|
|
19227
19634
|
}
|
|
19635
|
+
if (version === DECLARATION_V2) {
|
|
19636
|
+
bytes.push(positions.length);
|
|
19637
|
+
for (const tokenId of positions) {
|
|
19638
|
+
const encoded = tokenIdToBytes(tokenId);
|
|
19639
|
+
if (encoded.length > MAX_TOKEN_ID_BYTES) {
|
|
19640
|
+
throw new Error(`Uniswap V4 tokenId exceeds uint256: ${tokenId}`);
|
|
19641
|
+
}
|
|
19642
|
+
bytes.push(encoded.length, ...encoded);
|
|
19643
|
+
}
|
|
19644
|
+
}
|
|
19228
19645
|
return util.u8aToHex(new Uint8Array(bytes));
|
|
19229
19646
|
}
|
|
19230
|
-
function
|
|
19231
|
-
|
|
19647
|
+
function decodePhantomBidDeclaration(paymasterAndData) {
|
|
19648
|
+
const absent = { acceptedSources: null, uniswapV4Positions: [] };
|
|
19649
|
+
if (!paymasterAndData || !util.isHex(paymasterAndData)) return absent;
|
|
19232
19650
|
const bytes = util.hexToU8a(paymasterAndData);
|
|
19233
|
-
if (bytes.length < 2
|
|
19234
|
-
const
|
|
19651
|
+
if (bytes.length < 2) return absent;
|
|
19652
|
+
const version = bytes[0];
|
|
19653
|
+
if (version !== DECLARATION_V1 && version !== DECLARATION_V2) return absent;
|
|
19235
19654
|
const chains2 = [];
|
|
19236
19655
|
let offset = 2;
|
|
19237
|
-
for (let entry = 0; entry <
|
|
19238
|
-
if (offset >= bytes.length) return
|
|
19656
|
+
for (let entry = 0; entry < bytes[1]; entry++) {
|
|
19657
|
+
if (offset >= bytes.length) return absent;
|
|
19239
19658
|
const length = bytes[offset];
|
|
19240
19659
|
offset += 1;
|
|
19241
|
-
if (length === 0 || offset + length > bytes.length) return
|
|
19660
|
+
if (length === 0 || offset + length > bytes.length) return absent;
|
|
19242
19661
|
chains2.push(util.u8aToString(bytes.subarray(offset, offset + length)));
|
|
19243
19662
|
offset += length;
|
|
19244
19663
|
}
|
|
19245
|
-
|
|
19246
|
-
|
|
19664
|
+
const positions = [];
|
|
19665
|
+
if (version === DECLARATION_V2) {
|
|
19666
|
+
if (offset >= bytes.length) return absent;
|
|
19667
|
+
const count = bytes[offset];
|
|
19668
|
+
offset += 1;
|
|
19669
|
+
for (let entry = 0; entry < count; entry++) {
|
|
19670
|
+
if (offset >= bytes.length) return absent;
|
|
19671
|
+
const length = bytes[offset];
|
|
19672
|
+
offset += 1;
|
|
19673
|
+
if (length === 0 || length > MAX_TOKEN_ID_BYTES || offset + length > bytes.length) return absent;
|
|
19674
|
+
let tokenId = 0n;
|
|
19675
|
+
for (const byte of bytes.subarray(offset, offset + length)) tokenId = tokenId << 8n | BigInt(byte);
|
|
19676
|
+
positions.push(tokenId);
|
|
19677
|
+
offset += length;
|
|
19678
|
+
}
|
|
19679
|
+
}
|
|
19680
|
+
if (offset !== bytes.length) return absent;
|
|
19681
|
+
return { acceptedSources: chains2, uniswapV4Positions: positions };
|
|
19682
|
+
}
|
|
19683
|
+
function encodeAcceptedSourceChains(chains2) {
|
|
19684
|
+
return encodePhantomBidDeclaration({ acceptedSourceChains: chains2 });
|
|
19685
|
+
}
|
|
19686
|
+
function decodeAcceptedSourceChains(paymasterAndData) {
|
|
19687
|
+
return decodePhantomBidDeclaration(paymasterAndData).acceptedSources;
|
|
19247
19688
|
}
|
|
19248
19689
|
FILL_ORDER_ABI.find(
|
|
19249
19690
|
(item) => item?.type === "function" && item?.name === "fillOrder"
|
|
@@ -23735,10 +24176,12 @@ exports.EvmLanguage = EvmLanguage;
|
|
|
23735
24176
|
exports.HyperClientStatus = HyperClientStatus;
|
|
23736
24177
|
exports.HyperFungibleToken = HyperFungibleToken;
|
|
23737
24178
|
exports.HyperFungibleTokenABI = HyperFungibleTokenABI;
|
|
24179
|
+
exports.INCLUSION_TIMEOUT_MS = INCLUSION_TIMEOUT_MS;
|
|
23738
24180
|
exports.IntentGateway = IntentGateway;
|
|
23739
24181
|
exports.IntentGatewayABI = ABI3;
|
|
23740
24182
|
exports.IntentOrderStatus = IntentOrderStatus;
|
|
23741
24183
|
exports.IntentsCoprocessor = IntentsCoprocessor;
|
|
24184
|
+
exports.InvalidLiquidityIndexerResponseError = InvalidLiquidityIndexerResponseError;
|
|
23742
24185
|
exports.InvalidPhantomSnapshotError = InvalidPhantomSnapshotError;
|
|
23743
24186
|
exports.IsmpClient = IsmpClient;
|
|
23744
24187
|
exports.MOCK_ADDRESS = MOCK_ADDRESS;
|
|
@@ -23768,6 +24211,8 @@ exports.TronChain = TronChain;
|
|
|
23768
24211
|
exports.USE_ETHERSCAN_CHAINS = USE_ETHERSCAN_CHAINS;
|
|
23769
24212
|
exports.UnsupportedIntentQuotePairError = UnsupportedIntentQuotePairError;
|
|
23770
24213
|
exports.UnsupportedIntentQuoteStrategyError = UnsupportedIntentQuoteStrategyError;
|
|
24214
|
+
exports.UnsupportedLiquidityAssetError = UnsupportedLiquidityAssetError;
|
|
24215
|
+
exports.UnsupportedLiquidityChainError = UnsupportedLiquidityChainError;
|
|
23771
24216
|
exports.WrappedHyperFungibleTokenABI = WrappedHyperFungibleTokenABI;
|
|
23772
24217
|
exports.__test = __test;
|
|
23773
24218
|
exports.adjustDecimals = adjustDecimals;
|
|
@@ -23789,10 +24234,13 @@ exports.createEvmChain = createEvmChain;
|
|
|
23789
24234
|
exports.createQueryClient = createQueryClient;
|
|
23790
24235
|
exports.decodeAcceptedSourceChains = decodeAcceptedSourceChains;
|
|
23791
24236
|
exports.decodeERC7821ExecuteBatch = decodeERC7821ExecuteBatch;
|
|
24237
|
+
exports.decodePhantomBidDeclaration = decodePhantomBidDeclaration;
|
|
23792
24238
|
exports.decodeUserOpScale = decodeUserOpScale;
|
|
24239
|
+
exports.deriveHttpUrl = deriveHttpUrl;
|
|
23793
24240
|
exports.encodeAcceptedSourceChains = encodeAcceptedSourceChains;
|
|
23794
24241
|
exports.encodeERC7821ExecuteBatch = encodeERC7821ExecuteBatch;
|
|
23795
24242
|
exports.encodeISMPMessage = encodeISMPMessage;
|
|
24243
|
+
exports.encodePhantomBidDeclaration = encodePhantomBidDeclaration;
|
|
23796
24244
|
exports.encodeStateMachineId = encodeStateMachineId;
|
|
23797
24245
|
exports.encodeUserOpScale = encodeUserOpScale;
|
|
23798
24246
|
exports.encodeWithdrawalRequest = encodeWithdrawalRequest;
|
|
@@ -23828,6 +24276,7 @@ exports.pharosAtlantic = pharosAtlantic;
|
|
|
23828
24276
|
exports.pharosMainnet = pharosMainnet;
|
|
23829
24277
|
exports.polkadotAssetHubPaseo = polkadotAssetHubPaseo;
|
|
23830
24278
|
exports.polkadotHubMainnet = polkadotHubMainnet;
|
|
24279
|
+
exports.poolSlug = poolSlug;
|
|
23831
24280
|
exports.postRequestCommitment = postRequestCommitment;
|
|
23832
24281
|
exports.queryAssetTeleported = queryAssetTeleported;
|
|
23833
24282
|
exports.queryGetRequest = queryGetRequest;
|
|
@@ -23836,6 +24285,7 @@ exports.quoteUniswap = quoteUniswap;
|
|
|
23836
24285
|
exports.requestCommitmentKey = requestCommitmentKey;
|
|
23837
24286
|
exports.responseCommitmentKey = responseCommitmentKey;
|
|
23838
24287
|
exports.retryPromise = retryPromise;
|
|
24288
|
+
exports.sortPoolSymbols = sortPoolSymbols;
|
|
23839
24289
|
exports.teleport = teleport;
|
|
23840
24290
|
exports.teleportDot = teleportDot;
|
|
23841
24291
|
exports.transformOrderForContract = transformOrderForContract;
|