@shogun-sdk/swap 0.0.2-test.4 → 0.0.2-test.41
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/core.cjs +1050 -165
- package/dist/core.d.cts +4 -136
- package/dist/core.d.ts +4 -136
- package/dist/core.js +1050 -138
- package/dist/index-BGHbFEV3.d.cts +401 -0
- package/dist/index-DB1gT72l.d.ts +401 -0
- package/dist/index.cjs +1038 -535
- package/dist/index.d.cts +4 -9
- package/dist/index.d.ts +4 -9
- package/dist/index.js +1038 -505
- package/dist/react.cjs +1588 -487
- package/dist/react.d.cts +285 -88
- package/dist/react.d.ts +285 -88
- package/dist/react.js +1580 -469
- package/dist/{wallet-MmUIz8GE.d.cts → wallet-B9bKceyN.d.cts} +3 -3
- package/dist/{wallet-MmUIz8GE.d.ts → wallet-B9bKceyN.d.ts} +3 -3
- package/dist/wallet-adapter.cjs +25659 -27
- package/dist/wallet-adapter.d.cts +1 -2
- package/dist/wallet-adapter.d.ts +1 -2
- package/dist/wallet-adapter.js +25679 -16
- package/package.json +44 -14
- package/dist/execute-FaLLPp1i.d.cts +0 -147
- package/dist/execute-HX1fQ7wG.d.ts +0 -147
package/dist/react.cjs
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
9
|
var __export = (target, all) => {
|
|
7
10
|
for (var name in all)
|
|
8
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -15,107 +18,49 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
18
|
}
|
|
16
19
|
return to;
|
|
17
20
|
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
18
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
19
31
|
|
|
20
32
|
// src/react/index.ts
|
|
21
33
|
var react_exports = {};
|
|
22
34
|
__export(react_exports, {
|
|
23
|
-
|
|
24
|
-
|
|
35
|
+
ChainId: () => ChainId,
|
|
36
|
+
OrderExecutionType: () => OrderExecutionType,
|
|
37
|
+
SupportedChains: () => SupportedChains,
|
|
38
|
+
SwapProvider: () => SwapProvider,
|
|
39
|
+
buildQuoteParams: () => buildQuoteParams,
|
|
40
|
+
isEvmChain: () => isEvmChain,
|
|
25
41
|
useBalances: () => useBalances,
|
|
26
|
-
|
|
42
|
+
useCancelOrder: () => useCancelOrder,
|
|
43
|
+
useExecuteTransaction: () => useExecuteTransaction,
|
|
44
|
+
useOrders: () => useOrders,
|
|
27
45
|
useQuote: () => useQuote,
|
|
28
|
-
|
|
46
|
+
useSwap: () => useSwap,
|
|
47
|
+
useTokenList: () => useTokenList,
|
|
48
|
+
useTokensData: () => useTokensData
|
|
29
49
|
});
|
|
30
50
|
module.exports = __toCommonJS(react_exports);
|
|
31
51
|
|
|
32
|
-
// src/react/
|
|
52
|
+
// src/react/SwapProvider.tsx
|
|
33
53
|
var import_react = require("react");
|
|
34
54
|
|
|
35
|
-
// src/core/
|
|
36
|
-
var
|
|
37
|
-
|
|
38
|
-
return (0, import_intents_sdk.getTokenList)(params);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// src/react/useTokenList.ts
|
|
42
|
-
var tokenCache = /* @__PURE__ */ new Map();
|
|
43
|
-
function useTokenList(params) {
|
|
44
|
-
const [data, setData] = (0, import_react.useState)(null);
|
|
45
|
-
const [loading, setLoading] = (0, import_react.useState)(false);
|
|
46
|
-
const [error, setError] = (0, import_react.useState)(null);
|
|
47
|
-
const controllerRef = (0, import_react.useRef)(null);
|
|
48
|
-
const debounceRef = (0, import_react.useRef)(null);
|
|
49
|
-
const debounceMs = params.debounceMs ?? 250;
|
|
50
|
-
const cacheKey = (0, import_react.useMemo)(() => {
|
|
51
|
-
return JSON.stringify({
|
|
52
|
-
q: params.q?.trim().toLowerCase() ?? "",
|
|
53
|
-
networkId: params.networkId ?? "all",
|
|
54
|
-
page: params.page ?? 1,
|
|
55
|
-
limit: params.limit ?? 50
|
|
56
|
-
});
|
|
57
|
-
}, [params.q, params.networkId, params.page, params.limit]);
|
|
58
|
-
async function fetchTokens(signal) {
|
|
59
|
-
if (tokenCache.has(cacheKey)) {
|
|
60
|
-
setData(tokenCache.get(cacheKey));
|
|
61
|
-
setLoading(false);
|
|
62
|
-
setError(null);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
try {
|
|
66
|
-
setLoading(true);
|
|
67
|
-
const result = await getTokenList({ ...params, signal });
|
|
68
|
-
tokenCache.set(cacheKey, result);
|
|
69
|
-
setData(result);
|
|
70
|
-
setError(null);
|
|
71
|
-
} catch (err) {
|
|
72
|
-
if (err.name !== "AbortError") {
|
|
73
|
-
const e = err instanceof Error ? err : new Error("Unknown error while fetching tokens");
|
|
74
|
-
setError(e);
|
|
75
|
-
}
|
|
76
|
-
} finally {
|
|
77
|
-
setLoading(false);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
(0, import_react.useEffect)(() => {
|
|
81
|
-
if (!params.q && !params.networkId) return;
|
|
82
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
83
|
-
if (controllerRef.current) controllerRef.current.abort();
|
|
84
|
-
const controller = new AbortController();
|
|
85
|
-
controllerRef.current = controller;
|
|
86
|
-
debounceRef.current = setTimeout(() => {
|
|
87
|
-
fetchTokens(controller.signal);
|
|
88
|
-
}, debounceMs);
|
|
89
|
-
return () => {
|
|
90
|
-
controller.abort();
|
|
91
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
92
|
-
};
|
|
93
|
-
}, [cacheKey, debounceMs]);
|
|
94
|
-
return (0, import_react.useMemo)(
|
|
95
|
-
() => ({
|
|
96
|
-
/** Current fetched data (cached when possible) */
|
|
97
|
-
data,
|
|
98
|
-
/** Whether a request is in progress */
|
|
99
|
-
loading,
|
|
100
|
-
/** Error object if a request failed */
|
|
101
|
-
error,
|
|
102
|
-
/** Manually refetch the token list */
|
|
103
|
-
refetch: () => fetchTokens(),
|
|
104
|
-
/** Clear all cached token results (shared across hook instances) */
|
|
105
|
-
clearCache: () => tokenCache.clear()
|
|
106
|
-
}),
|
|
107
|
-
[data, loading, error]
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// src/react/useExecuteOrder.ts
|
|
112
|
-
var import_react2 = require("react");
|
|
55
|
+
// src/core/getQuote.ts
|
|
56
|
+
var import_intents_sdk3 = require("@shogun-sdk/intents-sdk");
|
|
57
|
+
var import_viem2 = require("viem");
|
|
113
58
|
|
|
114
|
-
// src/core/
|
|
115
|
-
var
|
|
116
|
-
var import_viem3 = require("viem");
|
|
59
|
+
// src/core/execute/normalizeNative.ts
|
|
60
|
+
var import_intents_sdk2 = require("@shogun-sdk/intents-sdk");
|
|
117
61
|
|
|
118
62
|
// src/utils/address.ts
|
|
63
|
+
var import_viem = require("viem");
|
|
119
64
|
var NATIVE_TOKEN = {
|
|
120
65
|
ETH: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
|
|
121
66
|
SOL: "So11111111111111111111111111111111111111111",
|
|
@@ -126,13 +71,32 @@ var isNativeAddress = (tokenAddress) => {
|
|
|
126
71
|
const normalizedTokenAddress = tokenAddress.toLowerCase();
|
|
127
72
|
return !!tokenAddress && NATIVE_ADDRESSES.includes(normalizedTokenAddress);
|
|
128
73
|
};
|
|
74
|
+
function normalizeEvmTokenAddress(address) {
|
|
75
|
+
const lower = address.toLowerCase();
|
|
76
|
+
return lower === NATIVE_TOKEN.ETH.toLowerCase() ? import_viem.zeroAddress : address;
|
|
77
|
+
}
|
|
129
78
|
|
|
130
79
|
// src/utils/chain.ts
|
|
131
|
-
var
|
|
132
|
-
var SOLANA_CHAIN_ID =
|
|
133
|
-
var
|
|
80
|
+
var import_intents_sdk = require("@shogun-sdk/intents-sdk");
|
|
81
|
+
var SOLANA_CHAIN_ID = import_intents_sdk.ChainID.Solana;
|
|
82
|
+
var CURRENT_SUPPORTED = [
|
|
83
|
+
import_intents_sdk.ChainID.Solana,
|
|
84
|
+
import_intents_sdk.ChainID.BSC,
|
|
85
|
+
import_intents_sdk.ChainID.Base,
|
|
86
|
+
import_intents_sdk.ChainID.MONAD
|
|
87
|
+
];
|
|
88
|
+
var ChainId = Object.entries(import_intents_sdk.ChainID).reduce(
|
|
89
|
+
(acc, [key, value]) => {
|
|
90
|
+
if (typeof value === "number" && CURRENT_SUPPORTED.includes(value)) {
|
|
91
|
+
acc[key] = value;
|
|
92
|
+
}
|
|
93
|
+
return acc;
|
|
94
|
+
},
|
|
95
|
+
{}
|
|
96
|
+
);
|
|
97
|
+
var SupportedChainsInternal = [
|
|
134
98
|
{
|
|
135
|
-
id:
|
|
99
|
+
id: import_intents_sdk.ChainID.Arbitrum,
|
|
136
100
|
name: "Arbitrum",
|
|
137
101
|
isEVM: true,
|
|
138
102
|
wrapped: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
@@ -141,7 +105,7 @@ var SupportedChains = [
|
|
|
141
105
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
142
106
|
},
|
|
143
107
|
{
|
|
144
|
-
id:
|
|
108
|
+
id: import_intents_sdk.ChainID.Optimism,
|
|
145
109
|
name: "Optimism",
|
|
146
110
|
isEVM: true,
|
|
147
111
|
wrapped: "0x4200000000000000000000000000000000000006",
|
|
@@ -150,7 +114,7 @@ var SupportedChains = [
|
|
|
150
114
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
151
115
|
},
|
|
152
116
|
{
|
|
153
|
-
id:
|
|
117
|
+
id: import_intents_sdk.ChainID.Base,
|
|
154
118
|
name: "Base",
|
|
155
119
|
isEVM: true,
|
|
156
120
|
wrapped: "0x4200000000000000000000000000000000000006",
|
|
@@ -159,7 +123,7 @@ var SupportedChains = [
|
|
|
159
123
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
160
124
|
},
|
|
161
125
|
{
|
|
162
|
-
id:
|
|
126
|
+
id: import_intents_sdk.ChainID.Hyperliquid,
|
|
163
127
|
name: "Hyperliquid",
|
|
164
128
|
isEVM: true,
|
|
165
129
|
wrapped: "0x5555555555555555555555555555555555555555",
|
|
@@ -168,7 +132,7 @@ var SupportedChains = [
|
|
|
168
132
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
169
133
|
},
|
|
170
134
|
{
|
|
171
|
-
id:
|
|
135
|
+
id: import_intents_sdk.ChainID.BSC,
|
|
172
136
|
name: "BSC",
|
|
173
137
|
isEVM: true,
|
|
174
138
|
wrapped: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
|
|
@@ -184,8 +148,21 @@ var SupportedChains = [
|
|
|
184
148
|
symbol: "SOL",
|
|
185
149
|
decimals: 9,
|
|
186
150
|
tokenAddress: NATIVE_TOKEN.SOL
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
id: import_intents_sdk.ChainID.MONAD,
|
|
154
|
+
name: "Monad",
|
|
155
|
+
isEVM: true,
|
|
156
|
+
wrapped: "0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A",
|
|
157
|
+
symbol: "MON",
|
|
158
|
+
decimals: 18,
|
|
159
|
+
tokenAddress: NATIVE_TOKEN.ETH
|
|
187
160
|
}
|
|
188
161
|
];
|
|
162
|
+
var SupportedChains = SupportedChainsInternal.filter(
|
|
163
|
+
(c) => CURRENT_SUPPORTED.includes(c.id)
|
|
164
|
+
);
|
|
165
|
+
var isEvmChain = import_intents_sdk.isEvmChain;
|
|
189
166
|
|
|
190
167
|
// src/utils/viem.ts
|
|
191
168
|
function isViemWalletClient(wallet) {
|
|
@@ -213,10 +190,227 @@ function serializeBigIntsToStrings(obj) {
|
|
|
213
190
|
return obj;
|
|
214
191
|
}
|
|
215
192
|
|
|
193
|
+
// src/core/execute/normalizeNative.ts
|
|
194
|
+
function normalizeNative(chainId, address) {
|
|
195
|
+
if ((0, import_intents_sdk2.isEvmChain)(chainId) && isNativeAddress(address)) {
|
|
196
|
+
const chain = SupportedChains.find((c) => c.id === chainId);
|
|
197
|
+
if (!chain?.wrapped)
|
|
198
|
+
throw new Error(`Wrapped token not found for chainId ${chainId}`);
|
|
199
|
+
return chain.wrapped;
|
|
200
|
+
}
|
|
201
|
+
return address;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/core/getQuote.ts
|
|
205
|
+
async function getQuote(params) {
|
|
206
|
+
const amount = BigInt(params.amount);
|
|
207
|
+
if (!params.tokenIn?.address || !params.tokenOut?.address) {
|
|
208
|
+
throw new Error("Both tokenIn and tokenOut must include an address.");
|
|
209
|
+
}
|
|
210
|
+
if (!params.sourceChainId || !params.destChainId) {
|
|
211
|
+
throw new Error("Both sourceChainId and destChainId are required.");
|
|
212
|
+
}
|
|
213
|
+
if (amount <= 0n) {
|
|
214
|
+
throw new Error("Amount must be greater than 0.");
|
|
215
|
+
}
|
|
216
|
+
const slippagePercent = Math.min(Math.max(params.slippage ?? 0.5, 0), 50);
|
|
217
|
+
const slippageBps = BigInt(Math.round(slippagePercent * 100));
|
|
218
|
+
const applyUserSlippage = (value) => value * (10000n - slippageBps) / 10000n;
|
|
219
|
+
let warning;
|
|
220
|
+
if (slippagePercent > 10) {
|
|
221
|
+
warning = `\u26A0\uFE0F High slippage tolerance (${slippagePercent.toFixed(2)}%) \u2014 price may vary significantly.`;
|
|
222
|
+
}
|
|
223
|
+
const normalizedTokenIn = normalizeNative(params.sourceChainId, params.tokenIn.address);
|
|
224
|
+
const isSingleChain = params.sourceChainId === params.destChainId;
|
|
225
|
+
let data;
|
|
226
|
+
let netFloor;
|
|
227
|
+
let minStablecoinsAmount;
|
|
228
|
+
if (isSingleChain) {
|
|
229
|
+
const { quote, netAmountOut } = await import_intents_sdk3.QuoteProvider.getSingleChainNetQuote({
|
|
230
|
+
chainId: params.sourceChainId,
|
|
231
|
+
amount,
|
|
232
|
+
tokenIn: normalizedTokenIn,
|
|
233
|
+
tokenOut: params.tokenOut.address
|
|
234
|
+
});
|
|
235
|
+
netFloor = netAmountOut;
|
|
236
|
+
minStablecoinsAmount = 0n;
|
|
237
|
+
data = {
|
|
238
|
+
amountInUsd: quote.amountInUsd,
|
|
239
|
+
estimatedAmountInAsMinStablecoinAmount: 0n,
|
|
240
|
+
estimatedAmountOut: quote.amountOut,
|
|
241
|
+
estimatedAmountOutUsd: quote.amountOutUsd,
|
|
242
|
+
// filled in below once the user slippage buffer is known
|
|
243
|
+
estimatedAmountOutReduced: netAmountOut,
|
|
244
|
+
estimatedAmountOutUsdReduced: quote.amountOutUsd,
|
|
245
|
+
tokenIn: { address: params.tokenIn.address, chainId: params.sourceChainId },
|
|
246
|
+
tokenOut: { address: params.tokenOut.address, chainId: params.destChainId }
|
|
247
|
+
};
|
|
248
|
+
} else {
|
|
249
|
+
const crossChainQuote = await import_intents_sdk3.QuoteProvider.getCrossChainNetQuote({
|
|
250
|
+
sourceChainId: params.sourceChainId,
|
|
251
|
+
destChainId: params.destChainId,
|
|
252
|
+
tokenIn: normalizedTokenIn,
|
|
253
|
+
tokenOut: params.tokenOut.address,
|
|
254
|
+
amount
|
|
255
|
+
});
|
|
256
|
+
data = crossChainQuote;
|
|
257
|
+
netFloor = BigInt(crossChainQuote.estimatedAmountOutReduced);
|
|
258
|
+
minStablecoinsAmount = applyUserSlippage(
|
|
259
|
+
BigInt(crossChainQuote.estimatedAmountInAsMinStablecoinAmount)
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
const amountOut = applyUserSlippage(netFloor);
|
|
263
|
+
const grossAmountOut = BigInt(data.estimatedAmountOut);
|
|
264
|
+
const pricePerTokenOutInUsd = grossAmountOut > 0n ? data.estimatedAmountOutUsd / Number(grossAmountOut) : 0;
|
|
265
|
+
const amountOutUsd = Number(amountOut) * pricePerTokenOutInUsd;
|
|
266
|
+
const pricePerInputToken = grossAmountOut * 10n ** BigInt(params.tokenIn.decimals ?? 18) / amount;
|
|
267
|
+
return {
|
|
268
|
+
amountOut,
|
|
269
|
+
amountOutUsd,
|
|
270
|
+
amountInUsd: data.amountInUsd,
|
|
271
|
+
minStablecoinsAmount,
|
|
272
|
+
tokenIn: {
|
|
273
|
+
address: params.tokenIn.address,
|
|
274
|
+
decimals: params.tokenIn.decimals ?? 18,
|
|
275
|
+
chainId: params.sourceChainId
|
|
276
|
+
},
|
|
277
|
+
tokenOut: {
|
|
278
|
+
address: params.tokenOut.address,
|
|
279
|
+
decimals: params.tokenOut.decimals ?? 18,
|
|
280
|
+
chainId: params.destChainId
|
|
281
|
+
},
|
|
282
|
+
amountIn: amount,
|
|
283
|
+
pricePerInputToken,
|
|
284
|
+
slippage: slippagePercent,
|
|
285
|
+
internal: {
|
|
286
|
+
...data,
|
|
287
|
+
estimatedAmountOutReduced: amountOut,
|
|
288
|
+
estimatedAmountOutUsdReduced: amountOutUsd
|
|
289
|
+
},
|
|
290
|
+
warning
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
function buildQuoteParams({
|
|
294
|
+
tokenIn,
|
|
295
|
+
tokenOut,
|
|
296
|
+
sourceChainId,
|
|
297
|
+
destChainId,
|
|
298
|
+
amount,
|
|
299
|
+
slippage
|
|
300
|
+
}) {
|
|
301
|
+
return {
|
|
302
|
+
tokenIn,
|
|
303
|
+
tokenOut,
|
|
304
|
+
sourceChainId,
|
|
305
|
+
destChainId,
|
|
306
|
+
amount: (0, import_viem2.parseUnits)(amount.toString(), tokenIn.decimals ?? 18).toString(),
|
|
307
|
+
slippage
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// src/core/getBalances.ts
|
|
312
|
+
var import_intents_sdk4 = require("@shogun-sdk/intents-sdk");
|
|
313
|
+
async function getBalances(params, options) {
|
|
314
|
+
const { addresses, cursorEvm, cursorSvm } = params;
|
|
315
|
+
const { signal } = options ?? {};
|
|
316
|
+
if (!addresses?.evm && !addresses?.svm) {
|
|
317
|
+
throw new Error("At least one address (EVM or SVM) must be provided.");
|
|
318
|
+
}
|
|
319
|
+
const payload = JSON.stringify({
|
|
320
|
+
addresses,
|
|
321
|
+
cursorEvm,
|
|
322
|
+
cursorSvm
|
|
323
|
+
});
|
|
324
|
+
const start = performance.now();
|
|
325
|
+
const response = await fetch(`${import_intents_sdk4.TOKEN_SEARCH_API_BASE_URL}/tokens/balances`, {
|
|
326
|
+
method: "POST",
|
|
327
|
+
headers: {
|
|
328
|
+
accept: "application/json",
|
|
329
|
+
"Content-Type": "application/json"
|
|
330
|
+
},
|
|
331
|
+
body: payload,
|
|
332
|
+
signal
|
|
333
|
+
}).catch((err) => {
|
|
334
|
+
if (err.name === "AbortError") {
|
|
335
|
+
throw new Error("Balance request was cancelled.");
|
|
336
|
+
}
|
|
337
|
+
throw err;
|
|
338
|
+
});
|
|
339
|
+
if (!response.ok) {
|
|
340
|
+
const text = await response.text().catch(() => "");
|
|
341
|
+
throw new Error(`Failed to fetch balances: ${response.status} ${text}`);
|
|
342
|
+
}
|
|
343
|
+
const data = await response.json().catch(() => {
|
|
344
|
+
throw new Error("Invalid JSON response from balances API.");
|
|
345
|
+
});
|
|
346
|
+
const duration = (performance.now() - start).toFixed(1);
|
|
347
|
+
if (process.env.NODE_ENV !== "production") {
|
|
348
|
+
console.debug(`[Shogun SDK] Fetched balances in ${duration}ms`);
|
|
349
|
+
}
|
|
350
|
+
const evmItems = data.evm?.items ?? [];
|
|
351
|
+
const svmItems = data.svm?.items ?? [];
|
|
352
|
+
const combined = [...evmItems, ...svmItems];
|
|
353
|
+
const filtered = combined.filter(
|
|
354
|
+
(b) => CURRENT_SUPPORTED.includes(b.chainId)
|
|
355
|
+
);
|
|
356
|
+
return {
|
|
357
|
+
results: filtered,
|
|
358
|
+
nextCursorEvm: data.evm?.cursor ?? null,
|
|
359
|
+
nextCursorSvm: data.svm?.cursor ?? null
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// src/core/token-list.ts
|
|
364
|
+
var import_intents_sdk5 = require("@shogun-sdk/intents-sdk");
|
|
365
|
+
async function getTokenList(params) {
|
|
366
|
+
const url = new URL(`${import_intents_sdk5.TOKEN_SEARCH_API_BASE_URL}/tokens/search`);
|
|
367
|
+
if (params.q) url.searchParams.append("q", params.q);
|
|
368
|
+
if (params.networkId) url.searchParams.append("networkId", String(params.networkId));
|
|
369
|
+
if (params.page) url.searchParams.append("page", String(params.page));
|
|
370
|
+
if (params.limit) url.searchParams.append("limit", String(params.limit));
|
|
371
|
+
const res = await fetch(url.toString(), {
|
|
372
|
+
signal: params.signal
|
|
373
|
+
});
|
|
374
|
+
if (!res.ok) {
|
|
375
|
+
throw new Error(`Failed to fetch tokens: ${res.status} ${res.statusText}`);
|
|
376
|
+
}
|
|
377
|
+
const data = await res.json();
|
|
378
|
+
const filteredResults = data.results.filter(
|
|
379
|
+
(token) => CURRENT_SUPPORTED.includes(token.chainId)
|
|
380
|
+
);
|
|
381
|
+
return {
|
|
382
|
+
...data,
|
|
383
|
+
results: filteredResults,
|
|
384
|
+
count: filteredResults.length
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// src/core/token.ts
|
|
389
|
+
var import_intents_sdk6 = require("@shogun-sdk/intents-sdk");
|
|
390
|
+
async function getTokensData(addresses) {
|
|
391
|
+
if (!addresses?.length) return [];
|
|
392
|
+
const response = await fetch(`${import_intents_sdk6.TOKEN_SEARCH_API_BASE_URL}/tokens/tokens`, {
|
|
393
|
+
method: "POST",
|
|
394
|
+
headers: {
|
|
395
|
+
"Content-Type": "application/json",
|
|
396
|
+
accept: "*/*"
|
|
397
|
+
},
|
|
398
|
+
body: JSON.stringify({ addresses })
|
|
399
|
+
});
|
|
400
|
+
if (!response.ok) {
|
|
401
|
+
throw new Error(`Failed to fetch token data: ${response.statusText}`);
|
|
402
|
+
}
|
|
403
|
+
const data = await response.json();
|
|
404
|
+
const filtered = data.filter((t) => CURRENT_SUPPORTED.includes(Number(t.chainId)));
|
|
405
|
+
return filtered;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/core/execute/execute.ts
|
|
409
|
+
var import_intents_sdk12 = require("@shogun-sdk/intents-sdk");
|
|
410
|
+
var import_viem7 = require("viem");
|
|
411
|
+
|
|
216
412
|
// src/wallet-adapter/evm-wallet-adapter/adapter.ts
|
|
217
|
-
var
|
|
218
|
-
var import_utils = require("ethers/lib/utils.js");
|
|
219
|
-
var import_viem = require("viem");
|
|
413
|
+
var import_viem3 = require("viem");
|
|
220
414
|
function isEVMTransaction(tx) {
|
|
221
415
|
return typeof tx.from === "string";
|
|
222
416
|
}
|
|
@@ -234,15 +428,26 @@ var adaptViemWallet = (wallet) => {
|
|
|
234
428
|
if (!isEVMTransaction(transaction)) {
|
|
235
429
|
throw new Error("Expected EVMTransaction but got SolanaTransaction");
|
|
236
430
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
431
|
+
if (wallet.transport.type === "http") {
|
|
432
|
+
const request = await wallet.prepareTransactionRequest({
|
|
433
|
+
to: transaction.to,
|
|
434
|
+
data: transaction.data,
|
|
435
|
+
value: transaction.value,
|
|
436
|
+
chain: wallet.chain
|
|
437
|
+
});
|
|
438
|
+
const serializedTransaction = await wallet.signTransaction(request);
|
|
439
|
+
const tx = await wallet.sendRawTransaction({ serializedTransaction });
|
|
440
|
+
return tx;
|
|
441
|
+
} else {
|
|
442
|
+
const hash = await wallet.sendTransaction({
|
|
443
|
+
to: transaction.to,
|
|
444
|
+
data: transaction.data,
|
|
445
|
+
value: transaction.value,
|
|
446
|
+
chain: wallet.chain,
|
|
447
|
+
account: wallet.account
|
|
448
|
+
});
|
|
449
|
+
return hash;
|
|
450
|
+
}
|
|
246
451
|
};
|
|
247
452
|
const switchChain = async (chainId) => {
|
|
248
453
|
try {
|
|
@@ -265,67 +470,96 @@ var adaptViemWallet = (wallet) => {
|
|
|
265
470
|
if (!addr) throw new Error("No address found");
|
|
266
471
|
return addr;
|
|
267
472
|
};
|
|
473
|
+
const readContract = async ({
|
|
474
|
+
address: address2,
|
|
475
|
+
abi,
|
|
476
|
+
functionName,
|
|
477
|
+
args = []
|
|
478
|
+
}) => {
|
|
479
|
+
const publicClient = wallet.extend(import_viem3.publicActions);
|
|
480
|
+
return await publicClient.readContract({
|
|
481
|
+
address: address2,
|
|
482
|
+
abi,
|
|
483
|
+
functionName,
|
|
484
|
+
args
|
|
485
|
+
});
|
|
486
|
+
};
|
|
268
487
|
return {
|
|
269
488
|
vmType: "EVM" /* EVM */,
|
|
270
|
-
transport: (0,
|
|
489
|
+
transport: (0, import_viem3.custom)(wallet.transport),
|
|
271
490
|
getChainId: async () => wallet.getChainId(),
|
|
272
491
|
address,
|
|
273
492
|
sendTransaction,
|
|
274
493
|
signTypedData,
|
|
275
|
-
switchChain
|
|
494
|
+
switchChain,
|
|
495
|
+
readContract
|
|
276
496
|
};
|
|
277
497
|
};
|
|
278
498
|
|
|
279
|
-
// src/core/
|
|
280
|
-
var
|
|
281
|
-
var
|
|
282
|
-
|
|
283
|
-
// src/core/executeOrder/normalizeNative.ts
|
|
284
|
-
var import_intents_sdk3 = require("@shogun-sdk/intents-sdk");
|
|
285
|
-
function normalizeNative(chainId, address) {
|
|
286
|
-
if ((0, import_intents_sdk3.isEvmChain)(chainId) && isNativeAddress(address)) {
|
|
287
|
-
const chain = SupportedChains.find((c) => c.id === chainId);
|
|
288
|
-
if (!chain?.wrapped)
|
|
289
|
-
throw new Error(`Wrapped token not found for chainId ${chainId}`);
|
|
290
|
-
return chain.wrapped;
|
|
291
|
-
}
|
|
292
|
-
return address;
|
|
293
|
-
}
|
|
499
|
+
// src/core/execute/handleEvmExecution.ts
|
|
500
|
+
var import_intents_sdk10 = require("@shogun-sdk/intents-sdk");
|
|
501
|
+
var import_viem6 = require("viem");
|
|
294
502
|
|
|
295
|
-
// src/core/
|
|
503
|
+
// src/core/execute/stageMessages.ts
|
|
296
504
|
var DEFAULT_STAGE_MESSAGES = {
|
|
297
505
|
processing: "Preparing transaction for execution",
|
|
298
506
|
approving: "Approving token allowance",
|
|
299
507
|
approved: "Token approved successfully",
|
|
300
|
-
signing: "Signing
|
|
301
|
-
submitting: "Submitting
|
|
302
|
-
|
|
303
|
-
|
|
508
|
+
signing: "Signing transaction for submission",
|
|
509
|
+
submitting: "Submitting transaction",
|
|
510
|
+
initiated: "Transaction initiated.",
|
|
511
|
+
success: "Transaction Executed successfully",
|
|
512
|
+
success_limit: "Limit order has been submitted successfully.",
|
|
513
|
+
shogun_processing: "Shogun is processing your transaction",
|
|
514
|
+
error: "Transaction failed during submission"
|
|
304
515
|
};
|
|
305
516
|
|
|
306
|
-
// src/core/
|
|
307
|
-
var
|
|
517
|
+
// src/core/execute/buildOrder.ts
|
|
518
|
+
var import_intents_sdk7 = require("@shogun-sdk/intents-sdk");
|
|
519
|
+
var import_viem4 = require("viem");
|
|
520
|
+
|
|
521
|
+
// src/utils/order.ts
|
|
522
|
+
var OrderExecutionType = /* @__PURE__ */ ((OrderExecutionType2) => {
|
|
523
|
+
OrderExecutionType2["LIMIT"] = "limit";
|
|
524
|
+
OrderExecutionType2["MARKET"] = "market";
|
|
525
|
+
return OrderExecutionType2;
|
|
526
|
+
})(OrderExecutionType || {});
|
|
527
|
+
|
|
528
|
+
// src/core/execute/buildOrder.ts
|
|
308
529
|
async function buildOrder({
|
|
309
530
|
quote,
|
|
310
531
|
accountAddress,
|
|
311
532
|
destination,
|
|
312
533
|
deadline,
|
|
313
|
-
isSingleChain
|
|
534
|
+
isSingleChain,
|
|
535
|
+
orderType,
|
|
536
|
+
options
|
|
314
537
|
}) {
|
|
315
538
|
const { tokenIn, tokenOut } = quote;
|
|
539
|
+
let amountOutMin = BigInt(quote.internal.estimatedAmountOutReduced);
|
|
540
|
+
if (orderType === "limit" /* LIMIT */ && options && "executionPrice" in options) {
|
|
541
|
+
const executionPrice = Number(options.executionPrice);
|
|
542
|
+
if (Number.isFinite(executionPrice) && executionPrice > 0) {
|
|
543
|
+
const decimalsIn = tokenIn.decimals ?? 18;
|
|
544
|
+
const decimalsOut = tokenOut.decimals ?? 18;
|
|
545
|
+
const formattedAmountIn = Number((0, import_viem4.formatUnits)(BigInt(quote.amountIn.toString()), decimalsIn));
|
|
546
|
+
const rawAmountOut = formattedAmountIn * executionPrice;
|
|
547
|
+
amountOutMin = (0, import_viem4.parseUnits)(rawAmountOut.toString(), decimalsOut);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
316
550
|
if (isSingleChain) {
|
|
317
|
-
return await
|
|
551
|
+
return await import_intents_sdk7.SingleChainOrder.create({
|
|
318
552
|
user: accountAddress,
|
|
319
553
|
chainId: tokenIn.chainId,
|
|
320
554
|
tokenIn: tokenIn.address,
|
|
321
555
|
tokenOut: tokenOut.address,
|
|
322
556
|
amountIn: quote.amountIn,
|
|
323
|
-
amountOutMin
|
|
557
|
+
amountOutMin,
|
|
324
558
|
deadline,
|
|
325
559
|
destinationAddress: destination
|
|
326
560
|
});
|
|
327
561
|
}
|
|
328
|
-
return await
|
|
562
|
+
return await import_intents_sdk7.CrossChainOrder.create({
|
|
329
563
|
user: accountAddress,
|
|
330
564
|
sourceChainId: tokenIn.chainId,
|
|
331
565
|
sourceTokenAddress: tokenIn.address,
|
|
@@ -334,12 +568,166 @@ async function buildOrder({
|
|
|
334
568
|
destinationTokenAddress: tokenOut.address,
|
|
335
569
|
destinationAddress: destination,
|
|
336
570
|
deadline,
|
|
337
|
-
destinationTokenMinAmount:
|
|
571
|
+
destinationTokenMinAmount: amountOutMin,
|
|
338
572
|
minStablecoinAmount: quote.minStablecoinsAmount
|
|
339
573
|
});
|
|
340
574
|
}
|
|
341
575
|
|
|
342
|
-
// src/
|
|
576
|
+
// src/utils/pollOrderStatus.ts
|
|
577
|
+
var import_intents_sdk8 = require("@shogun-sdk/intents-sdk");
|
|
578
|
+
async function pollOrderStatus(address, orderId, options = {}) {
|
|
579
|
+
const { intervalMs = 2e3, timeoutMs = 3e5, authToken } = options;
|
|
580
|
+
const startTime = Date.now();
|
|
581
|
+
const isDebug = process.env.NODE_ENV !== "production";
|
|
582
|
+
const isEvmAddress = /^0x[a-fA-F0-9]{40}$/.test(address);
|
|
583
|
+
const isSuiAddress = /^0x[a-fA-F0-9]{64}$/.test(address);
|
|
584
|
+
const isSolanaAddress = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address);
|
|
585
|
+
let queryParam;
|
|
586
|
+
if (isEvmAddress) queryParam = `evmWallets=${address}`;
|
|
587
|
+
else if (isSuiAddress) queryParam = `suiWallets=${address}`;
|
|
588
|
+
else if (isSolanaAddress) queryParam = `solanaWallets=${address}`;
|
|
589
|
+
else throw new Error(`Unrecognized wallet address format: ${address}`);
|
|
590
|
+
const queryUrl = `${import_intents_sdk8.AUCTIONEER_URL}/user_intent?${queryParam}`;
|
|
591
|
+
return new Promise((resolve, reject) => {
|
|
592
|
+
const pollInterval = setInterval(async () => {
|
|
593
|
+
try {
|
|
594
|
+
if (Date.now() - startTime > timeoutMs) {
|
|
595
|
+
clearInterval(pollInterval);
|
|
596
|
+
return resolve("Timeout");
|
|
597
|
+
}
|
|
598
|
+
const res = await fetch(queryUrl, {
|
|
599
|
+
method: "GET",
|
|
600
|
+
headers: {
|
|
601
|
+
"Content-Type": "application/json",
|
|
602
|
+
// The /user_intent status endpoint requires the SIWE JWT; without it
|
|
603
|
+
// the request 401s after the order has already been submitted.
|
|
604
|
+
...authToken ? { Authorization: `Bearer ${authToken}` } : {}
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
if (!res.ok) {
|
|
608
|
+
clearInterval(pollInterval);
|
|
609
|
+
return reject(
|
|
610
|
+
new Error(`Failed to fetch orders: ${res.status} ${res.statusText}`)
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
const json = await res.json();
|
|
614
|
+
const data = json?.data ?? {};
|
|
615
|
+
const allOrders = [
|
|
616
|
+
...data.crossChainDcaOrders ?? [],
|
|
617
|
+
...data.crossChainLimitOrders ?? [],
|
|
618
|
+
...data.singleChainDcaOrders ?? [],
|
|
619
|
+
...data.singleChainLimitOrders ?? []
|
|
620
|
+
];
|
|
621
|
+
const targetOrder = allOrders.find((o) => o.orderId === orderId);
|
|
622
|
+
if (!targetOrder) {
|
|
623
|
+
if (isDebug)
|
|
624
|
+
console.debug(`[pollOrderStatus] [${orderId}] Not found yet`);
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
const { orderStatus } = targetOrder;
|
|
628
|
+
if (isDebug) {
|
|
629
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
630
|
+
console.debug(`targetOrder`, targetOrder);
|
|
631
|
+
console.debug(
|
|
632
|
+
`[pollOrderStatus] [${orderId}] status=${orderStatus} (elapsed ${elapsed}s)`
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
if (["Fulfilled", "Cancelled", "Outdated"].includes(orderStatus)) {
|
|
636
|
+
clearInterval(pollInterval);
|
|
637
|
+
return resolve(orderStatus);
|
|
638
|
+
}
|
|
639
|
+
} catch (error) {
|
|
640
|
+
clearInterval(pollInterval);
|
|
641
|
+
return reject(error);
|
|
642
|
+
}
|
|
643
|
+
}, intervalMs);
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// src/core/execute/handleOrderPollingResult.ts
|
|
648
|
+
async function handleOrderPollingResult({
|
|
649
|
+
status,
|
|
650
|
+
orderId,
|
|
651
|
+
chainId,
|
|
652
|
+
update,
|
|
653
|
+
messageFor
|
|
654
|
+
}) {
|
|
655
|
+
switch (status) {
|
|
656
|
+
case "Fulfilled":
|
|
657
|
+
update("success", messageFor("success"));
|
|
658
|
+
return {
|
|
659
|
+
status: true,
|
|
660
|
+
orderId,
|
|
661
|
+
chainId,
|
|
662
|
+
finalStatus: status,
|
|
663
|
+
stage: "success"
|
|
664
|
+
};
|
|
665
|
+
case "Cancelled":
|
|
666
|
+
update("error", "Order was cancelled before fulfillment");
|
|
667
|
+
break;
|
|
668
|
+
case "Timeout":
|
|
669
|
+
update("error", "Order polling timed out");
|
|
670
|
+
break;
|
|
671
|
+
case "NotFound":
|
|
672
|
+
default:
|
|
673
|
+
update("error", "Order not found");
|
|
674
|
+
break;
|
|
675
|
+
}
|
|
676
|
+
return {
|
|
677
|
+
status: false,
|
|
678
|
+
orderId,
|
|
679
|
+
chainId,
|
|
680
|
+
finalStatus: status,
|
|
681
|
+
stage: "error"
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// src/core/execute/ensurePermit2Allowance.ts
|
|
686
|
+
var import_viem5 = require("viem");
|
|
687
|
+
var import_intents_sdk9 = require("@shogun-sdk/intents-sdk");
|
|
688
|
+
async function ensurePermit2Allowance({
|
|
689
|
+
chainId,
|
|
690
|
+
tokenIn,
|
|
691
|
+
wallet,
|
|
692
|
+
accountAddress,
|
|
693
|
+
requiredAmount,
|
|
694
|
+
increaseByDelta = false
|
|
695
|
+
}) {
|
|
696
|
+
const spender = import_intents_sdk9.PERMIT2_ADDRESS[chainId];
|
|
697
|
+
let currentAllowance = 0n;
|
|
698
|
+
try {
|
|
699
|
+
if (!wallet.readContract) {
|
|
700
|
+
throw new Error("Wallet does not implement readContract()");
|
|
701
|
+
}
|
|
702
|
+
currentAllowance = await wallet.readContract({
|
|
703
|
+
address: tokenIn,
|
|
704
|
+
abi: import_viem5.erc20Abi,
|
|
705
|
+
functionName: "allowance",
|
|
706
|
+
args: [accountAddress, spender]
|
|
707
|
+
});
|
|
708
|
+
} catch (error) {
|
|
709
|
+
console.warn(`[Permit2] Failed to read allowance for ${tokenIn}`, error);
|
|
710
|
+
}
|
|
711
|
+
const approvalAmount = increaseByDelta ? currentAllowance + requiredAmount : import_viem5.maxUint256;
|
|
712
|
+
console.debug(
|
|
713
|
+
`[Permit2] Approving ${approvalAmount} for ${tokenIn} (current: ${currentAllowance}, required: ${requiredAmount})`
|
|
714
|
+
);
|
|
715
|
+
await wallet.sendTransaction({
|
|
716
|
+
to: tokenIn,
|
|
717
|
+
from: accountAddress,
|
|
718
|
+
data: (0, import_viem5.encodeFunctionData)({
|
|
719
|
+
abi: import_viem5.erc20Abi,
|
|
720
|
+
functionName: "approve",
|
|
721
|
+
args: [spender, approvalAmount]
|
|
722
|
+
}),
|
|
723
|
+
value: 0n
|
|
724
|
+
});
|
|
725
|
+
console.info(
|
|
726
|
+
`[Permit2] Approval transaction sent for ${tokenIn} on chain ${chainId}`
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// src/core/execute/handleEvmExecution.ts
|
|
343
731
|
async function handleEvmExecution({
|
|
344
732
|
recipientAddress,
|
|
345
733
|
quote,
|
|
@@ -347,18 +735,22 @@ async function handleEvmExecution({
|
|
|
347
735
|
accountAddress,
|
|
348
736
|
wallet,
|
|
349
737
|
isSingleChain,
|
|
350
|
-
|
|
351
|
-
|
|
738
|
+
update,
|
|
739
|
+
orderType,
|
|
740
|
+
options
|
|
352
741
|
}) {
|
|
353
|
-
const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage];
|
|
742
|
+
const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage] ?? "";
|
|
743
|
+
const deadline = options?.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
|
|
354
744
|
await wallet.switchChain(chainId);
|
|
355
745
|
const tokenIn = normalizeNative(chainId, quote.tokenIn.address);
|
|
746
|
+
quote.tokenOut.address = normalizeEvmTokenAddress(quote.tokenOut.address);
|
|
356
747
|
const shouldWrapNative = isNativeAddress(quote.tokenIn.address);
|
|
357
748
|
update("processing", shouldWrapNative ? `${messageFor("processing")} (wrapping native token)` : messageFor("processing"));
|
|
358
749
|
if (shouldWrapNative) {
|
|
750
|
+
quote.tokenIn.address === tokenIn;
|
|
359
751
|
await wallet.sendTransaction({
|
|
360
752
|
to: tokenIn,
|
|
361
|
-
data: (0,
|
|
753
|
+
data: (0, import_viem6.encodeFunctionData)({
|
|
362
754
|
abi: [{ type: "function", name: "deposit", stateMutability: "payable", inputs: [], outputs: [] }],
|
|
363
755
|
functionName: "deposit",
|
|
364
756
|
args: []
|
|
@@ -367,43 +759,70 @@ async function handleEvmExecution({
|
|
|
367
759
|
from: accountAddress
|
|
368
760
|
});
|
|
369
761
|
}
|
|
370
|
-
update("
|
|
371
|
-
await
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}),
|
|
378
|
-
value: 0n,
|
|
379
|
-
from: accountAddress
|
|
762
|
+
update("processing", messageFor("approving"));
|
|
763
|
+
await ensurePermit2Allowance({
|
|
764
|
+
chainId,
|
|
765
|
+
tokenIn,
|
|
766
|
+
wallet,
|
|
767
|
+
accountAddress,
|
|
768
|
+
requiredAmount: BigInt(quote.amountIn)
|
|
380
769
|
});
|
|
381
|
-
update("
|
|
770
|
+
update("processing", messageFor("approved"));
|
|
382
771
|
const destination = recipientAddress ?? accountAddress;
|
|
383
772
|
const order = await buildOrder({
|
|
384
773
|
quote,
|
|
385
774
|
accountAddress,
|
|
386
775
|
destination,
|
|
387
776
|
deadline,
|
|
388
|
-
isSingleChain
|
|
777
|
+
isSingleChain,
|
|
778
|
+
orderType,
|
|
779
|
+
options
|
|
389
780
|
});
|
|
390
|
-
|
|
391
|
-
|
|
781
|
+
console.debug(`order`, order);
|
|
782
|
+
update("processing", messageFor("signing"));
|
|
783
|
+
const { orderTypedData, nonce } = isSingleChain ? await (0, import_intents_sdk10.getEVMSingleChainOrderTypedData)(order) : await (0, import_intents_sdk10.getEVMCrossChainOrderTypedData)(order);
|
|
784
|
+
const typedData = serializeBigIntsToStrings(orderTypedData);
|
|
392
785
|
if (!wallet.signTypedData) {
|
|
393
786
|
throw new Error("Wallet does not support EIP-712 signing");
|
|
394
787
|
}
|
|
395
|
-
const signature = await wallet.signTypedData(
|
|
396
|
-
|
|
788
|
+
const signature = await wallet.signTypedData({
|
|
789
|
+
domain: typedData.domain,
|
|
790
|
+
types: typedData.types,
|
|
791
|
+
primaryType: typedData.primaryType,
|
|
792
|
+
value: typedData.message,
|
|
793
|
+
message: typedData.message
|
|
794
|
+
});
|
|
795
|
+
update("processing", messageFor("submitting"));
|
|
397
796
|
const res = await order.sendToAuctioneer({ signature, nonce: nonce.toString() });
|
|
398
797
|
if (!res.success) {
|
|
399
798
|
throw new Error("Auctioneer submission failed");
|
|
400
799
|
}
|
|
401
|
-
update("
|
|
402
|
-
|
|
800
|
+
update("initiated", messageFor("initiated"));
|
|
801
|
+
const { intentId: orderId } = res.data;
|
|
802
|
+
update("initiated", messageFor("shogun_processing"));
|
|
803
|
+
if (orderType === "limit" /* LIMIT */) {
|
|
804
|
+
update("success", messageFor("success_limit"));
|
|
805
|
+
return {
|
|
806
|
+
status: true,
|
|
807
|
+
orderId,
|
|
808
|
+
chainId,
|
|
809
|
+
finalStatus: "OrderPlaced",
|
|
810
|
+
stage: "success"
|
|
811
|
+
};
|
|
812
|
+
} else {
|
|
813
|
+
const status = await pollOrderStatus(accountAddress, orderId, { authToken: options?.authToken });
|
|
814
|
+
return await handleOrderPollingResult({
|
|
815
|
+
status,
|
|
816
|
+
orderId,
|
|
817
|
+
chainId,
|
|
818
|
+
update,
|
|
819
|
+
messageFor
|
|
820
|
+
});
|
|
821
|
+
}
|
|
403
822
|
}
|
|
404
823
|
|
|
405
|
-
// src/core/
|
|
406
|
-
var
|
|
824
|
+
// src/core/execute/handleSolanaExecution.ts
|
|
825
|
+
var import_intents_sdk11 = require("@shogun-sdk/intents-sdk");
|
|
407
826
|
var import_web3 = require("@solana/web3.js");
|
|
408
827
|
async function handleSolanaExecution({
|
|
409
828
|
recipientAddress,
|
|
@@ -412,12 +831,14 @@ async function handleSolanaExecution({
|
|
|
412
831
|
isSingleChain,
|
|
413
832
|
update,
|
|
414
833
|
accountAddress,
|
|
415
|
-
|
|
834
|
+
orderType,
|
|
835
|
+
options
|
|
416
836
|
}) {
|
|
417
837
|
if (!wallet.rpcUrl) {
|
|
418
838
|
throw new Error("Solana wallet is missing rpcUrl");
|
|
419
839
|
}
|
|
420
|
-
const
|
|
840
|
+
const deadline = options?.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
|
|
841
|
+
const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage] ?? "";
|
|
421
842
|
update("processing", messageFor("processing"));
|
|
422
843
|
const destination = recipientAddress ?? accountAddress;
|
|
423
844
|
const order = await buildOrder({
|
|
@@ -425,7 +846,9 @@ async function handleSolanaExecution({
|
|
|
425
846
|
accountAddress,
|
|
426
847
|
destination,
|
|
427
848
|
deadline,
|
|
428
|
-
isSingleChain
|
|
849
|
+
isSingleChain,
|
|
850
|
+
orderType,
|
|
851
|
+
options
|
|
429
852
|
});
|
|
430
853
|
const txData = await getSolanaOrderInstructions({
|
|
431
854
|
order,
|
|
@@ -433,10 +856,9 @@ async function handleSolanaExecution({
|
|
|
433
856
|
rpcUrl: wallet.rpcUrl
|
|
434
857
|
});
|
|
435
858
|
const transaction = import_web3.VersionedTransaction.deserialize(Uint8Array.from(txData.txBytes));
|
|
436
|
-
update("
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
update("submitting", messageFor("submitting"));
|
|
859
|
+
update("processing", messageFor("signing"));
|
|
860
|
+
await wallet.sendTransaction(transaction);
|
|
861
|
+
update("processing", messageFor("submitting"));
|
|
440
862
|
const response = await submitToAuctioneer({
|
|
441
863
|
order,
|
|
442
864
|
isSingleChain,
|
|
@@ -445,25 +867,40 @@ async function handleSolanaExecution({
|
|
|
445
867
|
if (!response.success) {
|
|
446
868
|
throw new Error("Auctioneer submission failed");
|
|
447
869
|
}
|
|
448
|
-
update("
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
870
|
+
update("initiated", messageFor("initiated"));
|
|
871
|
+
const { intentId: orderId } = response.data;
|
|
872
|
+
update("initiated", messageFor("shogun_processing"));
|
|
873
|
+
if (orderType === "limit" /* LIMIT */) {
|
|
874
|
+
update("success", messageFor("success_limit"));
|
|
875
|
+
return {
|
|
876
|
+
status: true,
|
|
877
|
+
orderId,
|
|
878
|
+
chainId: SOLANA_CHAIN_ID,
|
|
879
|
+
finalStatus: "OrderPlaced",
|
|
880
|
+
stage: "success"
|
|
881
|
+
};
|
|
882
|
+
} else {
|
|
883
|
+
const status = await pollOrderStatus(accountAddress, orderId, { authToken: options?.authToken });
|
|
884
|
+
return await handleOrderPollingResult({
|
|
885
|
+
status,
|
|
886
|
+
orderId,
|
|
887
|
+
chainId: SOLANA_CHAIN_ID,
|
|
888
|
+
update,
|
|
889
|
+
messageFor
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
async function getSolanaOrderInstructions({
|
|
457
894
|
order,
|
|
458
895
|
isSingleChain,
|
|
459
896
|
rpcUrl
|
|
460
897
|
}) {
|
|
461
898
|
if (isSingleChain) {
|
|
462
|
-
return await (0,
|
|
899
|
+
return await (0, import_intents_sdk11.getSolanaSingleChainOrderInstructions)(order, {
|
|
463
900
|
rpcUrl
|
|
464
901
|
});
|
|
465
902
|
}
|
|
466
|
-
return await (0,
|
|
903
|
+
return await (0, import_intents_sdk11.getSolanaCrossChainOrderInstructions)(order, {
|
|
467
904
|
rpcUrl
|
|
468
905
|
});
|
|
469
906
|
}
|
|
@@ -484,52 +921,93 @@ async function submitToAuctioneer({
|
|
|
484
921
|
});
|
|
485
922
|
}
|
|
486
923
|
|
|
487
|
-
// src/core/
|
|
924
|
+
// src/core/execute/execute.ts
|
|
488
925
|
async function executeOrder({
|
|
489
926
|
quote,
|
|
490
927
|
accountAddress,
|
|
491
928
|
recipientAddress,
|
|
492
929
|
wallet,
|
|
493
930
|
onStatus,
|
|
931
|
+
orderType = "market" /* MARKET */,
|
|
494
932
|
options = {}
|
|
495
933
|
}) {
|
|
496
|
-
const
|
|
934
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
935
|
+
const log = (...args) => {
|
|
936
|
+
if (isDev) console.debug("[OneShot::executeOrder]", ...args);
|
|
937
|
+
};
|
|
497
938
|
const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage];
|
|
498
|
-
const update = (stage, message) =>
|
|
939
|
+
const update = (stage, message) => {
|
|
940
|
+
log("Stage:", stage, "| Message:", message ?? messageFor(stage));
|
|
941
|
+
onStatus?.(stage, message ?? messageFor(stage));
|
|
942
|
+
};
|
|
499
943
|
try {
|
|
944
|
+
log("Starting execution:", {
|
|
945
|
+
accountAddress,
|
|
946
|
+
recipientAddress,
|
|
947
|
+
tokenIn: quote?.tokenIn,
|
|
948
|
+
tokenOut: quote?.tokenOut
|
|
949
|
+
});
|
|
500
950
|
const adapter = normalizeWallet(wallet);
|
|
501
951
|
if (!adapter) throw new Error("No wallet provided");
|
|
502
952
|
const { tokenIn, tokenOut } = quote;
|
|
953
|
+
const srcChain = Number(tokenIn.chainId);
|
|
954
|
+
const destChain = Number(tokenOut.chainId);
|
|
955
|
+
if (!CURRENT_SUPPORTED.includes(srcChain) || !CURRENT_SUPPORTED.includes(destChain)) {
|
|
956
|
+
const unsupportedChains = [
|
|
957
|
+
!CURRENT_SUPPORTED.includes(srcChain) ? srcChain : null,
|
|
958
|
+
!CURRENT_SUPPORTED.includes(destChain) ? destChain : null
|
|
959
|
+
].filter(Boolean).join(", ");
|
|
960
|
+
const errorMsg = `Unsupported chain(s): ${unsupportedChains}`;
|
|
961
|
+
update("error", errorMsg);
|
|
962
|
+
log("Error:", errorMsg);
|
|
963
|
+
throw new Error(errorMsg);
|
|
964
|
+
}
|
|
503
965
|
const isSingleChain = tokenIn.chainId === tokenOut.chainId;
|
|
504
966
|
const chainId = Number(tokenIn.chainId);
|
|
505
|
-
update("processing"
|
|
506
|
-
if ((0,
|
|
507
|
-
|
|
967
|
+
update("processing");
|
|
968
|
+
if ((0, import_intents_sdk12.isEvmChain)(chainId)) {
|
|
969
|
+
log("Detected EVM chain:", chainId);
|
|
970
|
+
const result = await handleEvmExecution({
|
|
508
971
|
recipientAddress,
|
|
509
972
|
quote,
|
|
510
973
|
chainId,
|
|
511
974
|
accountAddress,
|
|
512
975
|
wallet: adapter,
|
|
513
976
|
isSingleChain,
|
|
514
|
-
|
|
515
|
-
|
|
977
|
+
update,
|
|
978
|
+
orderType,
|
|
979
|
+
options
|
|
516
980
|
});
|
|
981
|
+
log("EVM execution result:", result);
|
|
982
|
+
return result;
|
|
517
983
|
}
|
|
518
|
-
if (chainId ===
|
|
519
|
-
|
|
984
|
+
if (chainId === import_intents_sdk12.ChainID.Solana) {
|
|
985
|
+
log("Detected Solana chain");
|
|
986
|
+
const result = await handleSolanaExecution({
|
|
520
987
|
recipientAddress,
|
|
521
988
|
quote,
|
|
522
989
|
accountAddress,
|
|
523
990
|
wallet: adapter,
|
|
524
991
|
isSingleChain,
|
|
525
|
-
|
|
526
|
-
|
|
992
|
+
update,
|
|
993
|
+
orderType,
|
|
994
|
+
options
|
|
527
995
|
});
|
|
996
|
+
log("Solana execution result:", result);
|
|
997
|
+
return result;
|
|
528
998
|
}
|
|
529
|
-
|
|
530
|
-
|
|
999
|
+
const unsupported = `Unsupported chain: ${chainId}`;
|
|
1000
|
+
update("error", unsupported);
|
|
1001
|
+
log("Error:", unsupported);
|
|
1002
|
+
return { status: false, message: unsupported, stage: "error" };
|
|
531
1003
|
} catch (error) {
|
|
532
|
-
|
|
1004
|
+
let message = "An unknown error occurred";
|
|
1005
|
+
if (error && typeof error === "object") {
|
|
1006
|
+
const err = error;
|
|
1007
|
+
message = err.details ?? err.message ?? message;
|
|
1008
|
+
} else if (typeof error === "string") {
|
|
1009
|
+
message = error;
|
|
1010
|
+
}
|
|
533
1011
|
update("error", message);
|
|
534
1012
|
return { status: false, message, stage: "error" };
|
|
535
1013
|
}
|
|
@@ -540,349 +1018,972 @@ function normalizeWallet(wallet) {
|
|
|
540
1018
|
return wallet;
|
|
541
1019
|
}
|
|
542
1020
|
|
|
543
|
-
// src/
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
const
|
|
550
|
-
|
|
1021
|
+
// src/core/orders/getOrders.ts
|
|
1022
|
+
var import_intents_sdk13 = require("@shogun-sdk/intents-sdk");
|
|
1023
|
+
async function getOrders({
|
|
1024
|
+
evmAddress,
|
|
1025
|
+
solAddress
|
|
1026
|
+
}) {
|
|
1027
|
+
const wallets = [evmAddress, solAddress].filter(
|
|
1028
|
+
(wallet) => Boolean(wallet)
|
|
1029
|
+
);
|
|
1030
|
+
if (wallets.length === 0) {
|
|
1031
|
+
throw new Error("At least one wallet address (EVM, Solana) must be provided.");
|
|
1032
|
+
}
|
|
1033
|
+
const results = await Promise.all(
|
|
1034
|
+
wallets.map((wallet) => (0, import_intents_sdk13.fetchUserOrders)({ wallet }))
|
|
1035
|
+
);
|
|
1036
|
+
return results.reduce(
|
|
1037
|
+
(merged, orders) => {
|
|
1038
|
+
merged.singleChainLimitOrders.push(...orders.singleChainLimitOrders);
|
|
1039
|
+
merged.crossChainLimitOrders.push(...orders.crossChainLimitOrders);
|
|
1040
|
+
return merged;
|
|
1041
|
+
},
|
|
1042
|
+
{ singleChainLimitOrders: [], crossChainLimitOrders: [] }
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
// src/core/orders/cancelOrder.ts
|
|
1047
|
+
var import_intents_sdk14 = require("@shogun-sdk/intents-sdk");
|
|
1048
|
+
var import_web32 = require("@solana/web3.js");
|
|
1049
|
+
var import_viem8 = require("viem");
|
|
1050
|
+
var inFlightCancels = /* @__PURE__ */ new Map();
|
|
1051
|
+
function cancelIntentsOrder(args) {
|
|
1052
|
+
const srcChain = "srcChainId" in args.order ? args.order.srcChainId : args.order.chainId;
|
|
1053
|
+
const key = `${srcChain}:${args.order.orderId}`;
|
|
1054
|
+
const existing = inFlightCancels.get(key);
|
|
1055
|
+
if (existing) return existing;
|
|
1056
|
+
const pending = performCancel(args).finally(() => {
|
|
1057
|
+
inFlightCancels.delete(key);
|
|
1058
|
+
});
|
|
1059
|
+
inFlightCancels.set(key, pending);
|
|
1060
|
+
return pending;
|
|
1061
|
+
}
|
|
1062
|
+
async function performCancel({
|
|
1063
|
+
order,
|
|
1064
|
+
wallet,
|
|
1065
|
+
sol_rpc
|
|
1066
|
+
}) {
|
|
1067
|
+
const isCrossChain = "srcChainId" in order && "destChainId" in order && order.srcChainId !== order.destChainId;
|
|
1068
|
+
const srcChain = "srcChainId" in order ? order.srcChainId : order.chainId;
|
|
1069
|
+
const isSolanaOrder = srcChain === import_intents_sdk14.ChainID.Solana;
|
|
1070
|
+
if (isSolanaOrder) {
|
|
1071
|
+
if (!isCrossChain) {
|
|
1072
|
+
const { versionedMessageBytes: versionedMessageBytes2 } = await (0, import_intents_sdk14.cancelSingleChainOrderInstructionsAsBytes)(
|
|
1073
|
+
order.orderId,
|
|
1074
|
+
order.user,
|
|
1075
|
+
{ rpcUrl: sol_rpc }
|
|
1076
|
+
);
|
|
1077
|
+
const message2 = import_web32.VersionedMessage.deserialize(
|
|
1078
|
+
versionedMessageBytes2
|
|
1079
|
+
);
|
|
1080
|
+
const tx2 = new import_web32.VersionedTransaction(message2);
|
|
1081
|
+
return await wallet.sendTransaction(tx2);
|
|
1082
|
+
}
|
|
1083
|
+
const { versionedMessageBytes } = await (0, import_intents_sdk14.cancelCrossChainOrderInstructionsAsBytes)(
|
|
1084
|
+
order.orderId,
|
|
1085
|
+
order.user,
|
|
1086
|
+
{ rpcUrl: sol_rpc }
|
|
1087
|
+
);
|
|
1088
|
+
const message = import_web32.VersionedMessage.deserialize(
|
|
1089
|
+
versionedMessageBytes
|
|
1090
|
+
);
|
|
1091
|
+
const tx = new import_web32.VersionedTransaction(message);
|
|
1092
|
+
return await wallet.sendTransaction(tx);
|
|
1093
|
+
}
|
|
1094
|
+
const chainId = srcChain;
|
|
1095
|
+
const { readContract } = wallet;
|
|
1096
|
+
if (!readContract) {
|
|
1097
|
+
throw new Error("Wallet does not support readContract");
|
|
1098
|
+
}
|
|
1099
|
+
const nonce = BigInt(order.nonce ?? "0");
|
|
1100
|
+
const nonceWordPos = nonce >> 8n;
|
|
1101
|
+
const nonceBitPos = nonce - nonceWordPos * 256n;
|
|
1102
|
+
if (isCrossChain) {
|
|
1103
|
+
const [orderData = [false, false], currentNonceBitmap = 0n] = await Promise.all([
|
|
1104
|
+
readContract({
|
|
1105
|
+
address: import_intents_sdk14.CROSS_CHAIN_GUARD_ADDRESSES[chainId],
|
|
1106
|
+
abi: [
|
|
1107
|
+
{
|
|
1108
|
+
inputs: [{ name: "orderId", type: "bytes32" }],
|
|
1109
|
+
name: "orderData",
|
|
1110
|
+
outputs: [
|
|
1111
|
+
{ name: "initialized", type: "bool" },
|
|
1112
|
+
{ name: "deactivated", type: "bool" }
|
|
1113
|
+
],
|
|
1114
|
+
stateMutability: "view",
|
|
1115
|
+
type: "function"
|
|
1116
|
+
}
|
|
1117
|
+
],
|
|
1118
|
+
functionName: "orderData",
|
|
1119
|
+
args: [order.orderId]
|
|
1120
|
+
}),
|
|
1121
|
+
readContract({
|
|
1122
|
+
address: import_intents_sdk14.PERMIT2_ADDRESS[chainId],
|
|
1123
|
+
abi: [
|
|
1124
|
+
{
|
|
1125
|
+
inputs: [
|
|
1126
|
+
{ name: "owner", type: "address" },
|
|
1127
|
+
{ name: "wordPos", type: "uint256" }
|
|
1128
|
+
],
|
|
1129
|
+
name: "nonceBitmap",
|
|
1130
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
1131
|
+
stateMutability: "view",
|
|
1132
|
+
type: "function"
|
|
1133
|
+
}
|
|
1134
|
+
],
|
|
1135
|
+
functionName: "nonceBitmap",
|
|
1136
|
+
args: [order.user, nonceWordPos]
|
|
1137
|
+
})
|
|
1138
|
+
]);
|
|
1139
|
+
const [initialized, deactivated] = orderData;
|
|
1140
|
+
if (initialized) {
|
|
1141
|
+
if (deactivated) {
|
|
1142
|
+
throw new Error("Order is already deactivated");
|
|
1143
|
+
}
|
|
1144
|
+
return await wallet.sendTransaction({
|
|
1145
|
+
to: import_intents_sdk14.CROSS_CHAIN_GUARD_ADDRESSES[order.srcChainId],
|
|
1146
|
+
data: (0, import_viem8.encodeFunctionData)({
|
|
1147
|
+
abi: [
|
|
1148
|
+
{
|
|
1149
|
+
inputs: [
|
|
1150
|
+
{
|
|
1151
|
+
components: [
|
|
1152
|
+
{ name: "user", type: "address" },
|
|
1153
|
+
{ name: "tokenIn", type: "address" },
|
|
1154
|
+
{ name: "srcChainId", type: "uint256" },
|
|
1155
|
+
{ name: "deadline", type: "uint256" },
|
|
1156
|
+
{ name: "amountIn", type: "uint256" },
|
|
1157
|
+
{ name: "minStablecoinsAmount", type: "uint256" },
|
|
1158
|
+
{ name: "executionDetailsHash", type: "bytes32" },
|
|
1159
|
+
{ name: "nonce", type: "uint256" }
|
|
1160
|
+
],
|
|
1161
|
+
name: "orderInfo",
|
|
1162
|
+
type: "tuple"
|
|
1163
|
+
}
|
|
1164
|
+
],
|
|
1165
|
+
name: "cancelOrder",
|
|
1166
|
+
outputs: [],
|
|
1167
|
+
stateMutability: "nonpayable",
|
|
1168
|
+
type: "function"
|
|
1169
|
+
}
|
|
1170
|
+
],
|
|
1171
|
+
functionName: "cancelOrder",
|
|
1172
|
+
args: [
|
|
1173
|
+
{
|
|
1174
|
+
user: order.user,
|
|
1175
|
+
tokenIn: order.tokenIn,
|
|
1176
|
+
srcChainId: BigInt(order.srcChainId),
|
|
1177
|
+
deadline: BigInt(order.deadline),
|
|
1178
|
+
amountIn: BigInt(order.amountIn),
|
|
1179
|
+
minStablecoinsAmount: BigInt(order.minStablecoinsAmount),
|
|
1180
|
+
executionDetailsHash: order.executionDetailsHash,
|
|
1181
|
+
nonce
|
|
1182
|
+
}
|
|
1183
|
+
]
|
|
1184
|
+
}),
|
|
1185
|
+
value: BigInt(0),
|
|
1186
|
+
from: order.user
|
|
1187
|
+
});
|
|
1188
|
+
} else {
|
|
1189
|
+
if ((currentNonceBitmap & 1n << nonceBitPos) !== 0n) {
|
|
1190
|
+
throw new Error("Nonce is already invalidated");
|
|
1191
|
+
}
|
|
1192
|
+
const mask2 = 1n << nonceBitPos;
|
|
1193
|
+
return await wallet.sendTransaction({
|
|
1194
|
+
to: import_intents_sdk14.PERMIT2_ADDRESS[order.srcChainId],
|
|
1195
|
+
data: (0, import_viem8.encodeFunctionData)({
|
|
1196
|
+
abi: [
|
|
1197
|
+
{
|
|
1198
|
+
inputs: [
|
|
1199
|
+
{ name: "wordPos", type: "uint256" },
|
|
1200
|
+
{ name: "mask", type: "uint256" }
|
|
1201
|
+
],
|
|
1202
|
+
name: "invalidateUnorderedNonces",
|
|
1203
|
+
outputs: [],
|
|
1204
|
+
stateMutability: "nonpayable",
|
|
1205
|
+
type: "function"
|
|
1206
|
+
}
|
|
1207
|
+
],
|
|
1208
|
+
functionName: "invalidateUnorderedNonces",
|
|
1209
|
+
args: [nonceWordPos, mask2]
|
|
1210
|
+
}),
|
|
1211
|
+
value: BigInt(0),
|
|
1212
|
+
from: order.user
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
const [wasManuallyInitialized, currentBitmap = 0n] = await Promise.all([
|
|
1217
|
+
readContract({
|
|
1218
|
+
address: import_intents_sdk14.SINGLE_CHAIN_GUARD_ADDRESSES[chainId],
|
|
1219
|
+
abi: [
|
|
1220
|
+
{
|
|
1221
|
+
inputs: [{ name: "orderHash", type: "bytes32" }],
|
|
1222
|
+
name: "orderManuallyInitialized",
|
|
1223
|
+
outputs: [{ name: "", type: "bool" }],
|
|
1224
|
+
stateMutability: "view",
|
|
1225
|
+
type: "function"
|
|
1226
|
+
}
|
|
1227
|
+
],
|
|
1228
|
+
functionName: "orderManuallyInitialized",
|
|
1229
|
+
args: [order.orderId]
|
|
1230
|
+
}),
|
|
1231
|
+
readContract({
|
|
1232
|
+
address: import_intents_sdk14.PERMIT2_ADDRESS[chainId],
|
|
1233
|
+
abi: [
|
|
1234
|
+
{
|
|
1235
|
+
inputs: [
|
|
1236
|
+
{ name: "owner", type: "address" },
|
|
1237
|
+
{ name: "wordPos", type: "uint256" }
|
|
1238
|
+
],
|
|
1239
|
+
name: "nonceBitmap",
|
|
1240
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
1241
|
+
stateMutability: "view",
|
|
1242
|
+
type: "function"
|
|
1243
|
+
}
|
|
1244
|
+
],
|
|
1245
|
+
functionName: "nonceBitmap",
|
|
1246
|
+
args: [order.user, nonceWordPos]
|
|
1247
|
+
})
|
|
1248
|
+
]);
|
|
1249
|
+
if (wasManuallyInitialized) {
|
|
1250
|
+
return await wallet.sendTransaction({
|
|
1251
|
+
to: import_intents_sdk14.SINGLE_CHAIN_GUARD_ADDRESSES[chainId],
|
|
1252
|
+
data: (0, import_viem8.encodeFunctionData)({
|
|
1253
|
+
abi: [
|
|
1254
|
+
{
|
|
1255
|
+
inputs: [
|
|
1256
|
+
{
|
|
1257
|
+
name: "order",
|
|
1258
|
+
type: "tuple",
|
|
1259
|
+
components: [
|
|
1260
|
+
{ name: "amountIn", type: "uint256" },
|
|
1261
|
+
{ name: "tokenIn", type: "address" },
|
|
1262
|
+
{ name: "deadline", type: "uint256" },
|
|
1263
|
+
{ name: "nonce", type: "uint256" },
|
|
1264
|
+
{ name: "encodedExternalCallData", type: "bytes" },
|
|
1265
|
+
{
|
|
1266
|
+
name: "extraTransfers",
|
|
1267
|
+
type: "tuple[]",
|
|
1268
|
+
components: [
|
|
1269
|
+
{ name: "amount", type: "uint256" },
|
|
1270
|
+
{ name: "receiver", type: "address" },
|
|
1271
|
+
{ name: "token", type: "address" }
|
|
1272
|
+
]
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
name: "requestedOutput",
|
|
1276
|
+
type: "tuple",
|
|
1277
|
+
components: [
|
|
1278
|
+
{ name: "amount", type: "uint256" },
|
|
1279
|
+
{ name: "receiver", type: "address" },
|
|
1280
|
+
{ name: "token", type: "address" }
|
|
1281
|
+
]
|
|
1282
|
+
},
|
|
1283
|
+
{ name: "user", type: "address" }
|
|
1284
|
+
]
|
|
1285
|
+
}
|
|
1286
|
+
],
|
|
1287
|
+
name: "cancelManuallyCreatedOrder",
|
|
1288
|
+
outputs: [],
|
|
1289
|
+
stateMutability: "nonpayable",
|
|
1290
|
+
type: "function"
|
|
1291
|
+
}
|
|
1292
|
+
],
|
|
1293
|
+
functionName: "cancelManuallyCreatedOrder",
|
|
1294
|
+
args: [
|
|
1295
|
+
{
|
|
1296
|
+
amountIn: BigInt(order.amountIn),
|
|
1297
|
+
tokenIn: order.tokenIn,
|
|
1298
|
+
deadline: BigInt(order.deadline),
|
|
1299
|
+
nonce,
|
|
1300
|
+
encodedExternalCallData: "0x",
|
|
1301
|
+
extraTransfers: (order.extraTransfers || []).map((t) => ({
|
|
1302
|
+
amount: BigInt(t.amount),
|
|
1303
|
+
receiver: t.receiver,
|
|
1304
|
+
token: t.token
|
|
1305
|
+
})),
|
|
1306
|
+
requestedOutput: {
|
|
1307
|
+
amount: BigInt(order.amountOutMin),
|
|
1308
|
+
receiver: order.destinationAddress,
|
|
1309
|
+
token: order.tokenOut
|
|
1310
|
+
},
|
|
1311
|
+
user: order.user
|
|
1312
|
+
}
|
|
1313
|
+
]
|
|
1314
|
+
}),
|
|
1315
|
+
value: 0n,
|
|
1316
|
+
from: order.user
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
const mask = 1n << nonceBitPos;
|
|
1320
|
+
if ((currentBitmap & mask) !== 0n) {
|
|
1321
|
+
throw new Error("Nonce is already invalidated");
|
|
1322
|
+
}
|
|
1323
|
+
return await wallet.sendTransaction({
|
|
1324
|
+
to: import_intents_sdk14.PERMIT2_ADDRESS[chainId],
|
|
1325
|
+
data: (0, import_viem8.encodeFunctionData)({
|
|
1326
|
+
abi: [
|
|
1327
|
+
{
|
|
1328
|
+
inputs: [
|
|
1329
|
+
{ name: "wordPos", type: "uint256" },
|
|
1330
|
+
{ name: "mask", type: "uint256" }
|
|
1331
|
+
],
|
|
1332
|
+
name: "invalidateUnorderedNonces",
|
|
1333
|
+
outputs: [],
|
|
1334
|
+
stateMutability: "nonpayable",
|
|
1335
|
+
type: "function"
|
|
1336
|
+
}
|
|
1337
|
+
],
|
|
1338
|
+
functionName: "invalidateUnorderedNonces",
|
|
1339
|
+
args: [nonceWordPos, mask]
|
|
1340
|
+
}),
|
|
1341
|
+
value: 0n,
|
|
1342
|
+
from: order.user
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
// src/core/client.ts
|
|
1347
|
+
var SwapSDK = class {
|
|
1348
|
+
constructor(config) {
|
|
1349
|
+
__publicField(this, "apiKey");
|
|
1350
|
+
/**
|
|
1351
|
+
* Fetches metadata for one or more tokens from the Shogun Token Search API.
|
|
1352
|
+
*
|
|
1353
|
+
* ---
|
|
1354
|
+
* ### Overview
|
|
1355
|
+
* `getTokensData` retrieves normalized token information — such as symbol, name,
|
|
1356
|
+
* decimals, logo URI, and verified status — for a given list of token addresses.
|
|
1357
|
+
*
|
|
1358
|
+
* It supports both **EVM** and **SVM (Solana)** tokens, returning metadata from
|
|
1359
|
+
* Shogun’s unified token registry.
|
|
1360
|
+
*
|
|
1361
|
+
* ---
|
|
1362
|
+
* @example
|
|
1363
|
+
* ```ts
|
|
1364
|
+
* const tokens = await getTokensData([
|
|
1365
|
+
* "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
|
|
1366
|
+
* "So11111111111111111111111111111111111111112", // SOL
|
|
1367
|
+
* ]);
|
|
1368
|
+
*
|
|
1369
|
+
* console.log(tokens);
|
|
1370
|
+
* [
|
|
1371
|
+
* { symbol: "USDC", name: "USD Coin", chainId: 1, decimals: 6, ... },
|
|
1372
|
+
* { symbol: "SOL", name: "Solana", chainId: 101, decimals: 9, ... }
|
|
1373
|
+
* ]
|
|
1374
|
+
* ```
|
|
1375
|
+
*
|
|
1376
|
+
* @param addresses - An array of token addresses (EVM or SVM) to fetch metadata for.
|
|
1377
|
+
* @returns A promise resolving to an array of {@link TokenInfo} objects.
|
|
1378
|
+
*
|
|
1379
|
+
* @throws Will throw an error if the network request fails or the API responds with a non-OK status.
|
|
1380
|
+
*/
|
|
1381
|
+
__publicField(this, "getTokensData", getTokensData.bind(this));
|
|
1382
|
+
if (!config.apiKey) {
|
|
1383
|
+
throw new Error("SwapSDK: Missing API key");
|
|
1384
|
+
}
|
|
1385
|
+
this.apiKey = config.apiKey;
|
|
1386
|
+
if (this.apiKey) void this.apiKey;
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Retrieves a swap quote for the given input and output tokens.
|
|
1390
|
+
*
|
|
1391
|
+
* @param params - Quote parameters including source/destination tokens and amount.
|
|
1392
|
+
* @returns A normalized `SwapQuoteResponse` containing output amount, route, and metadata.
|
|
1393
|
+
*/
|
|
1394
|
+
async getQuote(params) {
|
|
1395
|
+
return getQuote(params);
|
|
1396
|
+
}
|
|
1397
|
+
/**
|
|
1398
|
+
* Fetches token balances for the specified user wallet(s).
|
|
1399
|
+
*
|
|
1400
|
+
* Supports both EVM and SVM (Solana) wallet addresses.
|
|
1401
|
+
*
|
|
1402
|
+
* @param params - Wallet address and optional chain filters.
|
|
1403
|
+
* @param options - Optional abort signal for cancellation.
|
|
1404
|
+
* @returns A unified balance response with per-chain token details.
|
|
1405
|
+
*/
|
|
1406
|
+
async getBalances(params, options) {
|
|
1407
|
+
return getBalances(params, options);
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Retrieves a list of verified tokens based on search query or chain filter.
|
|
1411
|
+
*
|
|
1412
|
+
* @param params - Search parameters (query, chain ID, pagination options).
|
|
1413
|
+
* @returns Paginated `TokenSearchResponse` containing token metadata.
|
|
1414
|
+
*/
|
|
1415
|
+
async getTokenList(params) {
|
|
1416
|
+
return getTokenList(params);
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Executes a prepared swap quote using the provided wallet and configuration.
|
|
1420
|
+
*
|
|
1421
|
+
* Handles:
|
|
1422
|
+
* - Token approval (if required)
|
|
1423
|
+
* - Transaction signing and broadcasting
|
|
1424
|
+
* - Confirmation polling and stage-based status updates
|
|
1425
|
+
*
|
|
1426
|
+
* Supports both:
|
|
1427
|
+
* - Market orders (with optional deadline)
|
|
1428
|
+
* - Limit orders (requires executionPrice and deadline)
|
|
1429
|
+
*
|
|
1430
|
+
* @param quote - The swap quote to execute, containing route and metadata.
|
|
1431
|
+
* @param accountAddress - The user's wallet address executing the swap.
|
|
1432
|
+
* @param recipientAddress - Optional recipient address for the output tokens (defaults to sender).
|
|
1433
|
+
* @param wallet - Adapted wallet instance (EVM/Solana) or a standard Viem `WalletClient`.
|
|
1434
|
+
* @param onStatus - Optional callback for receiving execution stage updates and messages.
|
|
1435
|
+
* @param orderType - Defines whether this is a market or limit order.
|
|
1436
|
+
* @param options - Execution parameters (different per order type).
|
|
1437
|
+
*
|
|
1438
|
+
* @returns A finalized execution result containing transaction hash, status, and any returned data.
|
|
1439
|
+
*
|
|
1440
|
+
* @example
|
|
1441
|
+
* ```ts
|
|
1442
|
+
* * Market order
|
|
1443
|
+
* const result = await sdk.executeTransaction({
|
|
1444
|
+
* quote,
|
|
1445
|
+
* accountAddress: "0x123...",
|
|
1446
|
+
* wallet,
|
|
1447
|
+
* orderType: OrderExecutionType.MARKET,
|
|
1448
|
+
* options: { deadline: 1800 },
|
|
1449
|
+
* onStatus: (stage, msg) => console.log(stage, msg),
|
|
1450
|
+
* });
|
|
1451
|
+
*
|
|
1452
|
+
* * Limit order
|
|
1453
|
+
* const result = await sdk.executeTransaction({
|
|
1454
|
+
* quote,
|
|
1455
|
+
* accountAddress: "0x123...",
|
|
1456
|
+
* wallet,
|
|
1457
|
+
* orderType: OrderExecutionType.LIMIT,
|
|
1458
|
+
* options: { executionPrice: "0.0021", deadline: 3600 },
|
|
1459
|
+
* });
|
|
1460
|
+
* ```
|
|
1461
|
+
*/
|
|
1462
|
+
async executeTransaction({
|
|
1463
|
+
quote,
|
|
1464
|
+
accountAddress,
|
|
1465
|
+
recipientAddress,
|
|
1466
|
+
wallet,
|
|
1467
|
+
onStatus,
|
|
1468
|
+
orderType,
|
|
1469
|
+
options
|
|
1470
|
+
}) {
|
|
1471
|
+
return executeOrder({
|
|
1472
|
+
quote,
|
|
1473
|
+
wallet,
|
|
1474
|
+
accountAddress,
|
|
1475
|
+
recipientAddress,
|
|
1476
|
+
onStatus,
|
|
1477
|
+
orderType,
|
|
1478
|
+
options
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1481
|
+
/**
|
|
1482
|
+
* Fetches all user orders (Market, Limit, Cross-chain) for connected wallets.
|
|
1483
|
+
*
|
|
1484
|
+
* ---
|
|
1485
|
+
* ### Overview
|
|
1486
|
+
* Retrieves both **single-chain** and **cross-chain** orders from the Shogun Intents API.
|
|
1487
|
+
* Works across EVM, Solana
|
|
1488
|
+
*
|
|
1489
|
+
* ---
|
|
1490
|
+
* @example
|
|
1491
|
+
* ```ts
|
|
1492
|
+
* const orders = await sdk.getOrders({
|
|
1493
|
+
* evmAddress: "0x123...",
|
|
1494
|
+
* solAddress: "9d12hF...abc",
|
|
1495
|
+
* });
|
|
1496
|
+
*
|
|
1497
|
+
* console.log(orders.singleChainLimitOrders);
|
|
1498
|
+
* ```
|
|
1499
|
+
*
|
|
1500
|
+
* @param params - Wallet addresses to fetch orders for (EVM, Solana).
|
|
1501
|
+
* @returns A structured {@link ApiUserOrders} object containing all user orders.
|
|
1502
|
+
*/
|
|
1503
|
+
async getOrders(params) {
|
|
1504
|
+
return getOrders(params);
|
|
1505
|
+
}
|
|
1506
|
+
/**
|
|
1507
|
+
* Cancels an order (single-chain or cross-chain) on EVM or Solana.
|
|
1508
|
+
*
|
|
1509
|
+
* Internally routes to the correct guard contract or Solana program to
|
|
1510
|
+
* deactivate the order or invalidate its nonce.
|
|
1511
|
+
*
|
|
1512
|
+
* @param params.order - Order payload returned from the Intents API.
|
|
1513
|
+
* @param params.wallet - Adapted wallet used to submit the cancellation transaction.
|
|
1514
|
+
* @param params.solRpc - Solana RPC URL used for SVM cancellations.
|
|
1515
|
+
*
|
|
1516
|
+
* @returns A transaction signature or hash from the underlying wallet.
|
|
1517
|
+
*/
|
|
1518
|
+
async cancelOrder(params) {
|
|
1519
|
+
return cancelIntentsOrder({
|
|
1520
|
+
order: params.order,
|
|
1521
|
+
wallet: params.wallet,
|
|
1522
|
+
sol_rpc: params.solRpc
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
};
|
|
1526
|
+
|
|
1527
|
+
// src/react/SwapProvider.tsx
|
|
1528
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1529
|
+
var SwapContext = (0, import_react.createContext)(null);
|
|
1530
|
+
var SwapProvider = ({ config, children }) => {
|
|
1531
|
+
const sdk = (0, import_react.useMemo)(() => new SwapSDK(config), [config.apiKey]);
|
|
1532
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SwapContext.Provider, { value: sdk, children });
|
|
1533
|
+
};
|
|
1534
|
+
var useSwap = () => {
|
|
1535
|
+
const ctx = (0, import_react.useContext)(SwapContext);
|
|
1536
|
+
if (!ctx) {
|
|
1537
|
+
throw new Error("useSwap must be used within <SwapProvider>");
|
|
1538
|
+
}
|
|
1539
|
+
return ctx;
|
|
1540
|
+
};
|
|
1541
|
+
|
|
1542
|
+
// src/react/useQuote.ts
|
|
1543
|
+
var import_react2 = require("react");
|
|
1544
|
+
var import_swr = __toESM(require("swr"), 1);
|
|
1545
|
+
var QUOTE_KEY = "quote-global";
|
|
1546
|
+
function isValidQuoteParams(p) {
|
|
1547
|
+
if (!p) return false;
|
|
1548
|
+
const { tokenIn, tokenOut, amount } = p;
|
|
1549
|
+
if (!tokenIn || !tokenOut || !amount) return false;
|
|
1550
|
+
const n = Number(amount);
|
|
1551
|
+
return Number.isFinite(n) && n > 0;
|
|
1552
|
+
}
|
|
1553
|
+
function useQuote(initialParams) {
|
|
1554
|
+
const sdk = useSwap();
|
|
1555
|
+
const abortRef = (0, import_react2.useRef)(null);
|
|
1556
|
+
const paramsRef = globalThis.__QUOTE_PARAMS_REF__ ?? (globalThis.__QUOTE_PARAMS_REF__ = { current: initialParams ?? null });
|
|
551
1557
|
(0, import_react2.useEffect)(() => {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
1558
|
+
if (initialParams && JSON.stringify(paramsRef.current) !== JSON.stringify(initialParams)) {
|
|
1559
|
+
paramsRef.current = initialParams;
|
|
1560
|
+
void (0, import_swr.mutate)(QUOTE_KEY);
|
|
1561
|
+
}
|
|
1562
|
+
}, [initialParams]);
|
|
1563
|
+
const fetcher = (0, import_react2.useCallback)(async () => {
|
|
1564
|
+
const activeParams = paramsRef.current;
|
|
1565
|
+
if (!isValidQuoteParams(activeParams)) return null;
|
|
1566
|
+
if (abortRef.current) abortRef.current.abort();
|
|
1567
|
+
const controller = new AbortController();
|
|
1568
|
+
abortRef.current = controller;
|
|
1569
|
+
try {
|
|
1570
|
+
return await sdk.getQuote(activeParams);
|
|
1571
|
+
} catch (err) {
|
|
1572
|
+
if (err instanceof DOMException && err.name === "AbortError") return null;
|
|
1573
|
+
if (err instanceof Error) throw err;
|
|
1574
|
+
throw new Error(String(err));
|
|
1575
|
+
}
|
|
1576
|
+
}, [sdk]);
|
|
1577
|
+
const {
|
|
1578
|
+
data,
|
|
1579
|
+
error,
|
|
1580
|
+
isValidating: loading,
|
|
1581
|
+
mutate: mutateQuote
|
|
1582
|
+
} = (0, import_swr.default)(QUOTE_KEY, fetcher, {
|
|
1583
|
+
revalidateOnFocus: false,
|
|
1584
|
+
shouldRetryOnError: false,
|
|
1585
|
+
keepPreviousData: true
|
|
1586
|
+
});
|
|
1587
|
+
const refetch = (0, import_react2.useCallback)(async () => {
|
|
1588
|
+
await mutateQuote();
|
|
1589
|
+
}, [mutateQuote]);
|
|
1590
|
+
const setParams = (0, import_react2.useCallback)(
|
|
1591
|
+
(next) => {
|
|
1592
|
+
paramsRef.current = next;
|
|
1593
|
+
void (0, import_swr.mutate)(QUOTE_KEY);
|
|
1594
|
+
},
|
|
1595
|
+
[]
|
|
1596
|
+
);
|
|
1597
|
+
return (0, import_react2.useMemo)(
|
|
1598
|
+
() => ({
|
|
1599
|
+
data,
|
|
1600
|
+
loading,
|
|
1601
|
+
error: error instanceof Error ? error.message : null,
|
|
1602
|
+
refetch,
|
|
1603
|
+
setParams,
|
|
1604
|
+
get activeParams() {
|
|
1605
|
+
return paramsRef.current;
|
|
1606
|
+
}
|
|
1607
|
+
}),
|
|
1608
|
+
[data, loading, error, refetch, setParams]
|
|
1609
|
+
);
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
// src/react/useExecuteTransaction.ts
|
|
1613
|
+
var import_react4 = require("react");
|
|
1614
|
+
|
|
1615
|
+
// src/react/useBalances.ts
|
|
1616
|
+
var import_react3 = require("react");
|
|
1617
|
+
var import_swr2 = __toESM(require("swr"), 1);
|
|
1618
|
+
var BALANCE_KEY = "balances-global";
|
|
1619
|
+
function useBalances(initialParams, options) {
|
|
1620
|
+
const sdk = useSwap();
|
|
1621
|
+
const abortRef = (0, import_react3.useRef)(null);
|
|
1622
|
+
const lastDataRef = (0, import_react3.useRef)(null);
|
|
1623
|
+
const paramsRef = globalThis.__BALANCES_PARAMS_REF__ ?? (globalThis.__BALANCES_PARAMS_REF__ = { current: initialParams ?? null });
|
|
1624
|
+
const fetcher = (0, import_react3.useCallback)(async () => {
|
|
1625
|
+
const activeParams = paramsRef.current;
|
|
1626
|
+
if (!activeParams) return null;
|
|
1627
|
+
if (abortRef.current) abortRef.current.abort();
|
|
1628
|
+
const controller = new AbortController();
|
|
1629
|
+
abortRef.current = controller;
|
|
1630
|
+
try {
|
|
1631
|
+
const balances = await sdk.getBalances(activeParams, { signal: controller.signal });
|
|
1632
|
+
lastDataRef.current = balances;
|
|
1633
|
+
return balances;
|
|
1634
|
+
} catch (err) {
|
|
1635
|
+
if (err instanceof DOMException && err.name === "AbortError") return lastDataRef.current;
|
|
1636
|
+
if (err instanceof Error) throw err;
|
|
1637
|
+
throw new Error(String(err));
|
|
1638
|
+
}
|
|
1639
|
+
}, [sdk, paramsRef]);
|
|
1640
|
+
const {
|
|
1641
|
+
data,
|
|
1642
|
+
error,
|
|
1643
|
+
// `isLoading` is only true on the initial fetch (no data yet), so background
|
|
1644
|
+
// refreshes on `refreshInterval` don't toggle it and flicker the UI.
|
|
1645
|
+
isLoading: loading,
|
|
1646
|
+
isValidating,
|
|
1647
|
+
mutate: mutateBalances
|
|
1648
|
+
} = (0, import_swr2.default)(BALANCE_KEY, fetcher, {
|
|
1649
|
+
revalidateOnFocus: false,
|
|
1650
|
+
shouldRetryOnError: false,
|
|
1651
|
+
keepPreviousData: true,
|
|
1652
|
+
// 0 (default) disables polling; a positive value re-fetches on that interval.
|
|
1653
|
+
refreshInterval: options?.refreshInterval ?? 0
|
|
1654
|
+
});
|
|
1655
|
+
const refetch = (0, import_react3.useCallback)(async () => {
|
|
1656
|
+
await mutateBalances();
|
|
1657
|
+
}, [mutateBalances]);
|
|
1658
|
+
const setParams = (0, import_react3.useCallback)((next) => {
|
|
1659
|
+
paramsRef.current = next;
|
|
1660
|
+
void (0, import_swr2.mutate)(BALANCE_KEY);
|
|
1661
|
+
}, [paramsRef]);
|
|
1662
|
+
(0, import_react3.useEffect)(() => {
|
|
1663
|
+
if (initialParams) {
|
|
1664
|
+
const prevParams = paramsRef.current;
|
|
1665
|
+
const paramsChanged = !prevParams || prevParams.addresses.svm !== initialParams.addresses.svm || prevParams.addresses.evm !== initialParams.addresses.evm;
|
|
1666
|
+
if (paramsChanged) {
|
|
1667
|
+
paramsRef.current = initialParams;
|
|
1668
|
+
void (0, import_swr2.mutate)(BALANCE_KEY);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
}, [initialParams, paramsRef]);
|
|
1672
|
+
return (0, import_react3.useMemo)(
|
|
1673
|
+
() => ({
|
|
1674
|
+
data,
|
|
1675
|
+
loading,
|
|
1676
|
+
// True during background revalidation (e.g. `refreshInterval` polls) while
|
|
1677
|
+
// previous `data` stays visible — use for a subtle refresh indicator.
|
|
1678
|
+
isValidating,
|
|
1679
|
+
error: error instanceof Error ? error : null,
|
|
1680
|
+
refetch,
|
|
1681
|
+
setParams,
|
|
1682
|
+
get activeParams() {
|
|
1683
|
+
return paramsRef.current;
|
|
1684
|
+
}
|
|
1685
|
+
}),
|
|
1686
|
+
[data, loading, isValidating, error, refetch, setParams, paramsRef]
|
|
1687
|
+
);
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
// src/react/useExecuteTransaction.ts
|
|
1691
|
+
function useExecuteTransaction() {
|
|
1692
|
+
const sdk = useSwap();
|
|
1693
|
+
const [isLoading, setLoading] = (0, import_react4.useState)(false);
|
|
1694
|
+
const [stage, setStage] = (0, import_react4.useState)(null);
|
|
1695
|
+
const [message, setMessage] = (0, import_react4.useState)(null);
|
|
1696
|
+
const [error, setError] = (0, import_react4.useState)(null);
|
|
1697
|
+
const [result, setResult] = (0, import_react4.useState)(null);
|
|
1698
|
+
const { refetch: refetchQuote } = useQuote();
|
|
1699
|
+
const { refetch: refetchBalances } = useBalances();
|
|
1700
|
+
const execute = (0, import_react4.useCallback)(
|
|
557
1701
|
async ({
|
|
558
1702
|
quote,
|
|
559
1703
|
accountAddress,
|
|
560
1704
|
recipientAddress,
|
|
561
1705
|
wallet,
|
|
562
|
-
|
|
1706
|
+
orderType,
|
|
1707
|
+
options
|
|
563
1708
|
}) => {
|
|
564
|
-
if (!quote || !wallet) {
|
|
565
|
-
throw new Error("Quote and wallet are required for order execution.");
|
|
566
|
-
}
|
|
567
1709
|
setLoading(true);
|
|
568
1710
|
setError(null);
|
|
569
|
-
|
|
570
|
-
|
|
1711
|
+
setResult(null);
|
|
1712
|
+
const onStatus = (s, msg) => {
|
|
1713
|
+
setStage(s);
|
|
1714
|
+
setMessage(msg ?? "");
|
|
1715
|
+
};
|
|
571
1716
|
try {
|
|
572
|
-
const
|
|
573
|
-
const onStatus = (stage, msg) => {
|
|
574
|
-
if (!isMounted.current) return;
|
|
575
|
-
setStatus(stage);
|
|
576
|
-
if (msg) setMessage(msg);
|
|
577
|
-
};
|
|
578
|
-
const result = await executeOrder({
|
|
1717
|
+
const res = await sdk.executeTransaction({
|
|
579
1718
|
quote,
|
|
1719
|
+
wallet,
|
|
580
1720
|
accountAddress,
|
|
581
1721
|
recipientAddress,
|
|
582
|
-
wallet,
|
|
583
1722
|
onStatus,
|
|
584
|
-
|
|
1723
|
+
orderType,
|
|
1724
|
+
options
|
|
585
1725
|
});
|
|
586
|
-
if (
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
return result;
|
|
591
|
-
} catch (err) {
|
|
592
|
-
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
593
|
-
if (isMounted.current) {
|
|
594
|
-
setError(errorObj);
|
|
595
|
-
setStatus("error");
|
|
596
|
-
setMessage(errorObj.message);
|
|
597
|
-
setData({
|
|
598
|
-
status: false,
|
|
599
|
-
stage: "error",
|
|
600
|
-
message: errorObj.message
|
|
1726
|
+
if (res && typeof res === "object" && "status" in res && "stage" in res && typeof res.stage === "string") {
|
|
1727
|
+
setResult({
|
|
1728
|
+
...res,
|
|
1729
|
+
stage: res.stage
|
|
601
1730
|
});
|
|
1731
|
+
} else {
|
|
1732
|
+
setResult(res);
|
|
602
1733
|
}
|
|
603
|
-
return
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
1734
|
+
return res;
|
|
1735
|
+
} catch (err) {
|
|
1736
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1737
|
+
setError(msg);
|
|
1738
|
+
throw err;
|
|
608
1739
|
} finally {
|
|
609
|
-
|
|
1740
|
+
await Promise.allSettled([refetchQuote(), refetchBalances()]);
|
|
1741
|
+
setLoading(false);
|
|
610
1742
|
}
|
|
611
1743
|
},
|
|
612
|
-
[]
|
|
1744
|
+
[sdk, refetchQuote, refetchBalances]
|
|
613
1745
|
);
|
|
614
1746
|
return {
|
|
615
|
-
/** Executes the swap order. */
|
|
616
1747
|
execute,
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
/** Human-readable status message. */
|
|
1748
|
+
isLoading,
|
|
1749
|
+
stage,
|
|
620
1750
|
message,
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
/** Raw SDK response data. */
|
|
624
|
-
data,
|
|
625
|
-
/** Captured error (if execution failed). */
|
|
626
|
-
error
|
|
1751
|
+
error,
|
|
1752
|
+
result
|
|
627
1753
|
};
|
|
628
1754
|
}
|
|
629
1755
|
|
|
630
|
-
// src/react/
|
|
631
|
-
var
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
tokenIn: normalizedTokenIn,
|
|
651
|
-
tokenOut: params.tokenOut.address,
|
|
652
|
-
amount: params.amount
|
|
653
|
-
});
|
|
654
|
-
const inputSlippage = params.slippage ?? 5;
|
|
655
|
-
const slippageDecimal = inputSlippage / 100;
|
|
656
|
-
const slippage = Math.min(Math.max(slippageDecimal, 0), 0.5);
|
|
657
|
-
let warning;
|
|
658
|
-
if (slippage > 0.1) {
|
|
659
|
-
warning = `\u26A0\uFE0F High slippage tolerance (${(slippage * 100).toFixed(2)}%) \u2014 price may vary significantly.`;
|
|
660
|
-
}
|
|
661
|
-
const estimatedAmountOut = BigInt(data.estimatedAmountOutReduced);
|
|
662
|
-
const slippageBps = BigInt(Math.round(slippage * 1e4));
|
|
663
|
-
const estimatedAmountOutAfterSlippage = estimatedAmountOut * (10000n - slippageBps) / 10000n;
|
|
664
|
-
const pricePerInputToken = estimatedAmountOut * 10n ** BigInt(params.tokenIn.decimals ?? 18) / BigInt(params.amount);
|
|
665
|
-
return {
|
|
666
|
-
amountOut: estimatedAmountOut,
|
|
667
|
-
amountOutUsd: data.estimatedAmountOutUsd,
|
|
668
|
-
amountInUsd: data.amountInUsd,
|
|
669
|
-
minStablecoinsAmount: data.estimatedAmountInAsMinStablecoinAmount,
|
|
670
|
-
tokenIn: {
|
|
671
|
-
address: params.tokenIn.address,
|
|
672
|
-
decimals: params.tokenIn.decimals ?? 18,
|
|
673
|
-
chainId: params.sourceChainId
|
|
674
|
-
},
|
|
675
|
-
tokenOut: {
|
|
676
|
-
address: params.tokenOut.address,
|
|
677
|
-
decimals: params.tokenOut.decimals ?? 18,
|
|
678
|
-
chainId: params.destChainId
|
|
679
|
-
},
|
|
680
|
-
amountIn: params.amount,
|
|
681
|
-
pricePerInputToken,
|
|
682
|
-
slippage,
|
|
683
|
-
internal: {
|
|
684
|
-
...data,
|
|
685
|
-
estimatedAmountOutReduced: estimatedAmountOutAfterSlippage
|
|
686
|
-
},
|
|
687
|
-
warning
|
|
688
|
-
};
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
// src/react/useQuote.ts
|
|
692
|
-
function useQuote(params, options) {
|
|
693
|
-
const [data, setData] = (0, import_react3.useState)(null);
|
|
694
|
-
const [loading, setLoading] = (0, import_react3.useState)(false);
|
|
695
|
-
const [error, setError] = (0, import_react3.useState)(null);
|
|
696
|
-
const [warning, setWarning] = (0, import_react3.useState)(null);
|
|
697
|
-
const debounceMs = options?.debounceMs ?? 250;
|
|
698
|
-
const autoRefreshMs = options?.autoRefreshMs;
|
|
699
|
-
const abortRef = (0, import_react3.useRef)(null);
|
|
700
|
-
const debounceRef = (0, import_react3.useRef)(null);
|
|
701
|
-
const mounted = (0, import_react3.useRef)(false);
|
|
702
|
-
(0, import_react3.useEffect)(() => {
|
|
703
|
-
mounted.current = true;
|
|
704
|
-
return () => {
|
|
705
|
-
mounted.current = false;
|
|
706
|
-
abortRef.current?.abort();
|
|
707
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
708
|
-
};
|
|
709
|
-
}, []);
|
|
710
|
-
const fetchQuote = (0, import_react3.useCallback)(
|
|
711
|
-
async () => {
|
|
712
|
-
if (!params) return;
|
|
1756
|
+
// src/react/useTokenList.ts
|
|
1757
|
+
var import_react5 = require("react");
|
|
1758
|
+
function useTokenList() {
|
|
1759
|
+
const [tokens, setTokens] = (0, import_react5.useState)([]);
|
|
1760
|
+
const [loading, setLoading] = (0, import_react5.useState)(false);
|
|
1761
|
+
const [error, setError] = (0, import_react5.useState)(null);
|
|
1762
|
+
const [hasMore, setHasMore] = (0, import_react5.useState)(true);
|
|
1763
|
+
const [page, setPage] = (0, import_react5.useState)(1);
|
|
1764
|
+
const sdk = useSwap();
|
|
1765
|
+
const pageRef = (0, import_react5.useRef)(1);
|
|
1766
|
+
const hasMoreRef = (0, import_react5.useRef)(true);
|
|
1767
|
+
const lastQuery = (0, import_react5.useRef)({});
|
|
1768
|
+
const cache = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
1769
|
+
const isLoadingRef = (0, import_react5.useRef)(false);
|
|
1770
|
+
pageRef.current = page;
|
|
1771
|
+
hasMoreRef.current = hasMore;
|
|
1772
|
+
const loadTokens = (0, import_react5.useCallback)(
|
|
1773
|
+
async (params) => {
|
|
1774
|
+
const { q, networkId, reset } = params;
|
|
1775
|
+
if (isLoadingRef.current && !reset) return;
|
|
713
1776
|
try {
|
|
1777
|
+
let currentPage = pageRef.current;
|
|
1778
|
+
if (reset) {
|
|
1779
|
+
currentPage = 1;
|
|
1780
|
+
setTokens([]);
|
|
1781
|
+
setPage(1);
|
|
1782
|
+
setHasMore(true);
|
|
1783
|
+
pageRef.current = 1;
|
|
1784
|
+
hasMoreRef.current = true;
|
|
1785
|
+
}
|
|
1786
|
+
if (!reset && !hasMoreRef.current) return;
|
|
1787
|
+
isLoadingRef.current = true;
|
|
714
1788
|
setLoading(true);
|
|
715
|
-
setWarning(null);
|
|
716
|
-
const result = await getQuote(params);
|
|
717
|
-
const serializeResult = serializeBigIntsToStrings(result);
|
|
718
|
-
if (!mounted.current) return;
|
|
719
|
-
setData((prev) => {
|
|
720
|
-
if (JSON.stringify(prev) === JSON.stringify(serializeResult)) return prev;
|
|
721
|
-
return serializeResult;
|
|
722
|
-
});
|
|
723
|
-
setWarning(result.warning ?? null);
|
|
724
1789
|
setError(null);
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
1790
|
+
const cacheKey = JSON.stringify({
|
|
1791
|
+
q: q?.toLowerCase() ?? "",
|
|
1792
|
+
networkId: networkId ?? void 0,
|
|
1793
|
+
page: currentPage
|
|
1794
|
+
});
|
|
1795
|
+
if (cache.current.has(cacheKey)) {
|
|
1796
|
+
const cached = cache.current.get(cacheKey);
|
|
1797
|
+
setTokens((prev) => reset ? cached.results : [...prev, ...cached.results]);
|
|
1798
|
+
if (!reset && cached.results.length > 0) {
|
|
1799
|
+
const nextPage = currentPage + 1;
|
|
1800
|
+
setPage(nextPage);
|
|
1801
|
+
pageRef.current = nextPage;
|
|
1802
|
+
}
|
|
1803
|
+
isLoadingRef.current = false;
|
|
1804
|
+
setLoading(false);
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
const apiParams = {
|
|
1808
|
+
q,
|
|
1809
|
+
page: currentPage,
|
|
1810
|
+
limit: 20,
|
|
1811
|
+
...networkId !== void 0 ? { networkId } : {}
|
|
1812
|
+
};
|
|
1813
|
+
const res = await sdk.getTokenList(apiParams);
|
|
1814
|
+
cache.current.set(cacheKey, res);
|
|
1815
|
+
setTokens((prev) => reset ? res.results : [...prev, ...res.results]);
|
|
1816
|
+
const isLastPage = res.results.length === 0 || res.results.length < 20 || res.count && currentPage * 20 >= res.count;
|
|
1817
|
+
setHasMore(!isLastPage);
|
|
1818
|
+
hasMoreRef.current = !isLastPage;
|
|
1819
|
+
if (!reset && !isLastPage) {
|
|
1820
|
+
const nextPage = currentPage + 1;
|
|
1821
|
+
setPage(nextPage);
|
|
1822
|
+
pageRef.current = nextPage;
|
|
1823
|
+
}
|
|
1824
|
+
lastQuery.current = { q, networkId };
|
|
1825
|
+
} catch (e) {
|
|
1826
|
+
console.error("useTokenList error:", e);
|
|
1827
|
+
setError("Failed to load tokens");
|
|
729
1828
|
} finally {
|
|
730
|
-
|
|
1829
|
+
setLoading(false);
|
|
1830
|
+
isLoadingRef.current = false;
|
|
731
1831
|
}
|
|
732
1832
|
},
|
|
733
|
-
[
|
|
734
|
-
);
|
|
735
|
-
(0, import_react3.useEffect)(() => {
|
|
736
|
-
if (!params) return;
|
|
737
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
738
|
-
debounceRef.current = setTimeout(() => {
|
|
739
|
-
fetchQuote();
|
|
740
|
-
}, debounceMs);
|
|
741
|
-
return () => {
|
|
742
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
743
|
-
abortRef.current?.abort();
|
|
744
|
-
};
|
|
745
|
-
}, [params, debounceMs, fetchQuote]);
|
|
746
|
-
(0, import_react3.useEffect)(() => {
|
|
747
|
-
if (!autoRefreshMs || !params) return;
|
|
748
|
-
const interval = setInterval(() => fetchQuote(), autoRefreshMs);
|
|
749
|
-
return () => clearInterval(interval);
|
|
750
|
-
}, [autoRefreshMs, params, fetchQuote]);
|
|
751
|
-
return (0, import_react3.useMemo)(
|
|
752
|
-
() => ({
|
|
753
|
-
data,
|
|
754
|
-
loading,
|
|
755
|
-
error,
|
|
756
|
-
warning,
|
|
757
|
-
refetch: () => fetchQuote()
|
|
758
|
-
}),
|
|
759
|
-
[data, loading, error, warning, fetchQuote]
|
|
1833
|
+
[sdk]
|
|
760
1834
|
);
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
const { signal } = options ?? {};
|
|
771
|
-
if (!addresses?.evm && !addresses?.svm) {
|
|
772
|
-
throw new Error("At least one address (EVM or SVM) must be provided.");
|
|
773
|
-
}
|
|
774
|
-
const payload = JSON.stringify({
|
|
775
|
-
addresses,
|
|
776
|
-
cursorEvm,
|
|
777
|
-
cursorSvm
|
|
778
|
-
});
|
|
779
|
-
const start = performance.now();
|
|
780
|
-
const response = await fetch(`${import_intents_sdk9.TOKEN_SEARCH_API_BASE_URL}/tokens/balances`, {
|
|
781
|
-
method: "POST",
|
|
782
|
-
headers: {
|
|
783
|
-
accept: "application/json",
|
|
784
|
-
"Content-Type": "application/json"
|
|
785
|
-
},
|
|
786
|
-
body: payload,
|
|
787
|
-
signal
|
|
788
|
-
}).catch((err) => {
|
|
789
|
-
if (err.name === "AbortError") {
|
|
790
|
-
throw new Error("Balance request was cancelled.");
|
|
791
|
-
}
|
|
792
|
-
throw err;
|
|
793
|
-
});
|
|
794
|
-
if (!response.ok) {
|
|
795
|
-
const text = await response.text().catch(() => "");
|
|
796
|
-
throw new Error(`Failed to fetch balances: ${response.status} ${text}`);
|
|
797
|
-
}
|
|
798
|
-
const data = await response.json().catch(() => {
|
|
799
|
-
throw new Error("Invalid JSON response from balances API.");
|
|
800
|
-
});
|
|
801
|
-
const duration = (performance.now() - start).toFixed(1);
|
|
802
|
-
if (process.env.NODE_ENV !== "production") {
|
|
803
|
-
console.debug(`[Shogun SDK] Fetched balances in ${duration}ms`);
|
|
804
|
-
}
|
|
805
|
-
const evmItems = data.evm?.items ?? [];
|
|
806
|
-
const svmItems = data.svm?.items ?? [];
|
|
807
|
-
const combined = [...evmItems, ...svmItems];
|
|
1835
|
+
const resetTokens = (0, import_react5.useCallback)(() => {
|
|
1836
|
+
setTokens([]);
|
|
1837
|
+
setPage(1);
|
|
1838
|
+
setHasMore(true);
|
|
1839
|
+
pageRef.current = 1;
|
|
1840
|
+
hasMoreRef.current = true;
|
|
1841
|
+
cache.current.clear();
|
|
1842
|
+
lastQuery.current = {};
|
|
1843
|
+
}, []);
|
|
808
1844
|
return {
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
1845
|
+
tokens,
|
|
1846
|
+
loading,
|
|
1847
|
+
error,
|
|
1848
|
+
hasMore,
|
|
1849
|
+
page,
|
|
1850
|
+
lastQuery: lastQuery.current,
|
|
1851
|
+
loadTokens,
|
|
1852
|
+
resetTokens
|
|
812
1853
|
};
|
|
813
1854
|
}
|
|
814
1855
|
|
|
815
|
-
// src/react/
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
const
|
|
819
|
-
const [
|
|
820
|
-
const
|
|
821
|
-
const
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
1856
|
+
// src/react/useTokensData.ts
|
|
1857
|
+
var import_react6 = require("react");
|
|
1858
|
+
function useTokensData(addresses) {
|
|
1859
|
+
const sdk = useSwap();
|
|
1860
|
+
const [data, setData] = (0, import_react6.useState)([]);
|
|
1861
|
+
const [loading, setLoading] = (0, import_react6.useState)(false);
|
|
1862
|
+
const [error, setError] = (0, import_react6.useState)(null);
|
|
1863
|
+
(0, import_react6.useEffect)(() => {
|
|
1864
|
+
if (!addresses?.length) return;
|
|
1865
|
+
let isMounted = true;
|
|
1866
|
+
setLoading(true);
|
|
1867
|
+
setError(null);
|
|
1868
|
+
sdk.getTokensData(addresses).then((res) => {
|
|
1869
|
+
if (isMounted) setData(res);
|
|
1870
|
+
}).catch((e) => {
|
|
1871
|
+
if (isMounted) setError(e instanceof Error ? e : new Error(String(e)));
|
|
1872
|
+
}).finally(() => {
|
|
1873
|
+
if (isMounted) setLoading(false);
|
|
1874
|
+
});
|
|
1875
|
+
return () => {
|
|
1876
|
+
isMounted = false;
|
|
831
1877
|
};
|
|
832
|
-
}, [
|
|
833
|
-
|
|
834
|
-
|
|
1878
|
+
}, [sdk, JSON.stringify(addresses)]);
|
|
1879
|
+
return { data, loading, error };
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
// src/react/useOrders.ts
|
|
1883
|
+
var import_react7 = require("react");
|
|
1884
|
+
var import_swr3 = __toESM(require("swr"), 1);
|
|
1885
|
+
var ORDERS_KEY = "orders-global";
|
|
1886
|
+
function hasValidAddress(addrs) {
|
|
1887
|
+
if (!addrs) return false;
|
|
1888
|
+
return Boolean(addrs.evmAddress || addrs.solAddress || addrs.suiAddress);
|
|
1889
|
+
}
|
|
1890
|
+
function useOrders(initialAddresses) {
|
|
1891
|
+
const sdk = useSwap();
|
|
1892
|
+
const abortRef = (0, import_react7.useRef)(null);
|
|
1893
|
+
const addrRef = globalThis.__ORDERS_ADDR_REF__ ?? (globalThis.__ORDERS_ADDR_REF__ = { current: initialAddresses ?? null });
|
|
1894
|
+
(0, import_react7.useEffect)(() => {
|
|
1895
|
+
if (initialAddresses && JSON.stringify(addrRef.current) !== JSON.stringify(initialAddresses)) {
|
|
1896
|
+
addrRef.current = initialAddresses;
|
|
1897
|
+
void (0, import_swr3.mutate)(ORDERS_KEY);
|
|
1898
|
+
}
|
|
1899
|
+
}, [initialAddresses]);
|
|
1900
|
+
const fetcher = (0, import_react7.useCallback)(async () => {
|
|
1901
|
+
const active = addrRef.current;
|
|
1902
|
+
if (!hasValidAddress(active)) return null;
|
|
835
1903
|
if (abortRef.current) abortRef.current.abort();
|
|
836
1904
|
const controller = new AbortController();
|
|
837
1905
|
abortRef.current = controller;
|
|
838
|
-
setLoading(true);
|
|
839
|
-
setError(null);
|
|
840
1906
|
try {
|
|
841
|
-
|
|
842
|
-
setData((prev) => {
|
|
843
|
-
if (JSON.stringify(prev) === JSON.stringify(result)) return prev;
|
|
844
|
-
return result;
|
|
845
|
-
});
|
|
846
|
-
return result;
|
|
1907
|
+
return await sdk.getOrders(active ?? {});
|
|
847
1908
|
} catch (err) {
|
|
848
|
-
if (err.name === "AbortError") return;
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
throw e;
|
|
852
|
-
} finally {
|
|
853
|
-
setLoading(false);
|
|
1909
|
+
if (err instanceof DOMException && err.name === "AbortError") return null;
|
|
1910
|
+
if (err instanceof Error) throw err;
|
|
1911
|
+
throw new Error(String(err));
|
|
854
1912
|
}
|
|
855
|
-
}, [
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
1913
|
+
}, [sdk]);
|
|
1914
|
+
const {
|
|
1915
|
+
data,
|
|
1916
|
+
error,
|
|
1917
|
+
isValidating: loading,
|
|
1918
|
+
mutate: mutateOrders
|
|
1919
|
+
} = (0, import_swr3.default)(ORDERS_KEY, fetcher, {
|
|
1920
|
+
revalidateOnFocus: false,
|
|
1921
|
+
revalidateOnReconnect: false,
|
|
1922
|
+
shouldRetryOnError: false,
|
|
1923
|
+
keepPreviousData: true
|
|
1924
|
+
});
|
|
1925
|
+
const refetch = (0, import_react7.useCallback)(async () => {
|
|
1926
|
+
await mutateOrders();
|
|
1927
|
+
}, [mutateOrders]);
|
|
1928
|
+
const setAddresses = (0, import_react7.useCallback)(
|
|
1929
|
+
(next) => {
|
|
1930
|
+
if (!next) return;
|
|
1931
|
+
addrRef.current = next;
|
|
1932
|
+
void (0, import_swr3.mutate)(ORDERS_KEY);
|
|
1933
|
+
},
|
|
1934
|
+
[]
|
|
1935
|
+
);
|
|
1936
|
+
return (0, import_react7.useMemo)(
|
|
864
1937
|
() => ({
|
|
865
|
-
/** Latest fetched balance data */
|
|
866
1938
|
data,
|
|
867
|
-
/** Whether the hook is currently fetching */
|
|
868
1939
|
loading,
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
1940
|
+
error: error instanceof Error ? error.message : null,
|
|
1941
|
+
refetch,
|
|
1942
|
+
setAddresses,
|
|
1943
|
+
get activeAddresses() {
|
|
1944
|
+
return addrRef.current;
|
|
1945
|
+
}
|
|
873
1946
|
}),
|
|
874
|
-
[data, loading, error,
|
|
1947
|
+
[data, loading, error, refetch, setAddresses]
|
|
875
1948
|
);
|
|
876
1949
|
}
|
|
877
1950
|
|
|
878
|
-
// src/react/
|
|
879
|
-
var
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
1951
|
+
// src/react/useCancelOrder.ts
|
|
1952
|
+
var import_react8 = require("react");
|
|
1953
|
+
var import_swr4 = require("swr");
|
|
1954
|
+
function useCancelOrder() {
|
|
1955
|
+
const sdk = useSwap();
|
|
1956
|
+
const [isLoading, setLoading] = (0, import_react8.useState)(false);
|
|
1957
|
+
const [error, setError] = (0, import_react8.useState)(null);
|
|
1958
|
+
const [result, setResult] = (0, import_react8.useState)(null);
|
|
1959
|
+
const cancel = (0, import_react8.useCallback)(
|
|
1960
|
+
async (params) => {
|
|
1961
|
+
setLoading(true);
|
|
1962
|
+
setError(null);
|
|
1963
|
+
setResult(null);
|
|
1964
|
+
try {
|
|
1965
|
+
const tx = await sdk.cancelOrder({
|
|
1966
|
+
order: params.order,
|
|
1967
|
+
wallet: params.wallet,
|
|
1968
|
+
solRpc: params.solRpc
|
|
1969
|
+
});
|
|
1970
|
+
setResult(tx);
|
|
1971
|
+
void (0, import_swr4.mutate)("orders-global");
|
|
1972
|
+
return tx;
|
|
1973
|
+
} catch (err) {
|
|
1974
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1975
|
+
setError(msg);
|
|
1976
|
+
throw err;
|
|
1977
|
+
} finally {
|
|
1978
|
+
setLoading(false);
|
|
1979
|
+
}
|
|
1980
|
+
},
|
|
1981
|
+
[sdk]
|
|
1982
|
+
);
|
|
1983
|
+
return {
|
|
1984
|
+
cancel,
|
|
1985
|
+
isLoading,
|
|
1986
|
+
error,
|
|
1987
|
+
result
|
|
1988
|
+
};
|
|
1989
|
+
}
|