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