@shogun-sdk/swap 0.0.2-test.3 → 0.0.2-test.30
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 +984 -157
- package/dist/core.d.cts +4 -136
- package/dist/core.d.ts +4 -136
- package/dist/core.js +983 -129
- package/dist/index-CXY6BUx9.d.ts +394 -0
- package/dist/index-CtZ-hgYk.d.cts +394 -0
- package/dist/index.cjs +973 -527
- package/dist/index.d.cts +4 -9
- package/dist/index.d.ts +4 -9
- package/dist/index.js +972 -496
- package/dist/react.cjs +1517 -484
- package/dist/react.d.cts +283 -89
- package/dist/react.d.ts +283 -89
- package/dist/react.js +1510 -467
- 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/index.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
return intentsGetTokenList(params);
|
|
5
|
-
}
|
|
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);
|
|
6
4
|
|
|
7
5
|
// src/core/getQuote.ts
|
|
8
6
|
import { QuoteProvider } from "@shogun-sdk/intents-sdk";
|
|
9
7
|
import { parseUnits } from "viem";
|
|
10
8
|
|
|
11
|
-
// src/core/
|
|
12
|
-
import { isEvmChain } from "@shogun-sdk/intents-sdk";
|
|
9
|
+
// src/core/execute/normalizeNative.ts
|
|
10
|
+
import { isEvmChain as isEvmChain2 } from "@shogun-sdk/intents-sdk";
|
|
13
11
|
|
|
14
12
|
// src/utils/address.ts
|
|
13
|
+
import { zeroAddress } from "viem";
|
|
15
14
|
var NATIVE_TOKEN = {
|
|
16
15
|
ETH: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
|
|
17
16
|
SOL: "So11111111111111111111111111111111111111111",
|
|
@@ -22,13 +21,32 @@ var isNativeAddress = (tokenAddress) => {
|
|
|
22
21
|
const normalizedTokenAddress = tokenAddress.toLowerCase();
|
|
23
22
|
return !!tokenAddress && NATIVE_ADDRESSES.includes(normalizedTokenAddress);
|
|
24
23
|
};
|
|
24
|
+
function normalizeEvmTokenAddress(address) {
|
|
25
|
+
const lower = address.toLowerCase();
|
|
26
|
+
return lower === NATIVE_TOKEN.ETH.toLowerCase() ? zeroAddress : address;
|
|
27
|
+
}
|
|
25
28
|
|
|
26
29
|
// src/utils/chain.ts
|
|
27
|
-
import { ChainID } from "@shogun-sdk/intents-sdk";
|
|
28
|
-
var SOLANA_CHAIN_ID =
|
|
29
|
-
var
|
|
30
|
+
import { ChainID as BaseChainID, isEvmChain as isEvmChainIntent } from "@shogun-sdk/intents-sdk";
|
|
31
|
+
var SOLANA_CHAIN_ID = BaseChainID.Solana;
|
|
32
|
+
var CURRENT_SUPPORTED = [
|
|
33
|
+
BaseChainID.Solana,
|
|
34
|
+
BaseChainID.BSC,
|
|
35
|
+
BaseChainID.Base,
|
|
36
|
+
BaseChainID.MONAD
|
|
37
|
+
];
|
|
38
|
+
var ChainId = Object.entries(BaseChainID).reduce(
|
|
39
|
+
(acc, [key, value]) => {
|
|
40
|
+
if (typeof value === "number" && CURRENT_SUPPORTED.includes(value)) {
|
|
41
|
+
acc[key] = value;
|
|
42
|
+
}
|
|
43
|
+
return acc;
|
|
44
|
+
},
|
|
45
|
+
{}
|
|
46
|
+
);
|
|
47
|
+
var SupportedChainsInternal = [
|
|
30
48
|
{
|
|
31
|
-
id:
|
|
49
|
+
id: BaseChainID.Arbitrum,
|
|
32
50
|
name: "Arbitrum",
|
|
33
51
|
isEVM: true,
|
|
34
52
|
wrapped: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
@@ -37,7 +55,7 @@ var SupportedChains = [
|
|
|
37
55
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
38
56
|
},
|
|
39
57
|
{
|
|
40
|
-
id:
|
|
58
|
+
id: BaseChainID.Optimism,
|
|
41
59
|
name: "Optimism",
|
|
42
60
|
isEVM: true,
|
|
43
61
|
wrapped: "0x4200000000000000000000000000000000000006",
|
|
@@ -46,7 +64,7 @@ var SupportedChains = [
|
|
|
46
64
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
47
65
|
},
|
|
48
66
|
{
|
|
49
|
-
id:
|
|
67
|
+
id: BaseChainID.Base,
|
|
50
68
|
name: "Base",
|
|
51
69
|
isEVM: true,
|
|
52
70
|
wrapped: "0x4200000000000000000000000000000000000006",
|
|
@@ -55,7 +73,7 @@ var SupportedChains = [
|
|
|
55
73
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
56
74
|
},
|
|
57
75
|
{
|
|
58
|
-
id:
|
|
76
|
+
id: BaseChainID.Hyperliquid,
|
|
59
77
|
name: "Hyperliquid",
|
|
60
78
|
isEVM: true,
|
|
61
79
|
wrapped: "0x5555555555555555555555555555555555555555",
|
|
@@ -64,7 +82,7 @@ var SupportedChains = [
|
|
|
64
82
|
tokenAddress: NATIVE_TOKEN.ETH
|
|
65
83
|
},
|
|
66
84
|
{
|
|
67
|
-
id:
|
|
85
|
+
id: BaseChainID.BSC,
|
|
68
86
|
name: "BSC",
|
|
69
87
|
isEVM: true,
|
|
70
88
|
wrapped: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
|
|
@@ -80,8 +98,21 @@ var SupportedChains = [
|
|
|
80
98
|
symbol: "SOL",
|
|
81
99
|
decimals: 9,
|
|
82
100
|
tokenAddress: NATIVE_TOKEN.SOL
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: BaseChainID.MONAD,
|
|
104
|
+
name: "Monad",
|
|
105
|
+
isEVM: true,
|
|
106
|
+
wrapped: "0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A",
|
|
107
|
+
symbol: "MON",
|
|
108
|
+
decimals: 18,
|
|
109
|
+
tokenAddress: NATIVE_TOKEN.ETH
|
|
83
110
|
}
|
|
84
111
|
];
|
|
112
|
+
var SupportedChains = SupportedChainsInternal.filter(
|
|
113
|
+
(c) => CURRENT_SUPPORTED.includes(c.id)
|
|
114
|
+
);
|
|
115
|
+
var isEvmChain = isEvmChainIntent;
|
|
85
116
|
|
|
86
117
|
// src/utils/viem.ts
|
|
87
118
|
function isViemWalletClient(wallet) {
|
|
@@ -109,9 +140,9 @@ function serializeBigIntsToStrings(obj) {
|
|
|
109
140
|
return obj;
|
|
110
141
|
}
|
|
111
142
|
|
|
112
|
-
// src/core/
|
|
143
|
+
// src/core/execute/normalizeNative.ts
|
|
113
144
|
function normalizeNative(chainId, address) {
|
|
114
|
-
if (
|
|
145
|
+
if (isEvmChain2(chainId) && isNativeAddress(address)) {
|
|
115
146
|
const chain = SupportedChains.find((c) => c.id === chainId);
|
|
116
147
|
if (!chain?.wrapped)
|
|
117
148
|
throw new Error(`Wrapped token not found for chainId ${chainId}`);
|
|
@@ -122,13 +153,14 @@ function normalizeNative(chainId, address) {
|
|
|
122
153
|
|
|
123
154
|
// src/core/getQuote.ts
|
|
124
155
|
async function getQuote(params) {
|
|
156
|
+
const amount = BigInt(params.amount);
|
|
125
157
|
if (!params.tokenIn?.address || !params.tokenOut?.address) {
|
|
126
158
|
throw new Error("Both tokenIn and tokenOut must include an address.");
|
|
127
159
|
}
|
|
128
160
|
if (!params.sourceChainId || !params.destChainId) {
|
|
129
161
|
throw new Error("Both sourceChainId and destChainId are required.");
|
|
130
162
|
}
|
|
131
|
-
if (
|
|
163
|
+
if (amount <= 0n) {
|
|
132
164
|
throw new Error("Amount must be greater than 0.");
|
|
133
165
|
}
|
|
134
166
|
const normalizedTokenIn = normalizeNative(params.sourceChainId, params.tokenIn.address);
|
|
@@ -137,24 +169,27 @@ async function getQuote(params) {
|
|
|
137
169
|
destChainId: params.destChainId,
|
|
138
170
|
tokenIn: normalizedTokenIn,
|
|
139
171
|
tokenOut: params.tokenOut.address,
|
|
140
|
-
amount
|
|
172
|
+
amount
|
|
141
173
|
});
|
|
142
|
-
const
|
|
143
|
-
const slippageDecimal = inputSlippage / 100;
|
|
144
|
-
const slippage = Math.min(Math.max(slippageDecimal, 0), 0.5);
|
|
174
|
+
const slippagePercent = Math.min(Math.max(params.slippage ?? 0.5, 0), 50);
|
|
145
175
|
let warning;
|
|
146
|
-
if (
|
|
147
|
-
warning = `\u26A0\uFE0F High slippage tolerance (${
|
|
176
|
+
if (slippagePercent > 10) {
|
|
177
|
+
warning = `\u26A0\uFE0F High slippage tolerance (${slippagePercent.toFixed(2)}%) \u2014 price may vary significantly.`;
|
|
148
178
|
}
|
|
149
|
-
const estimatedAmountOut = BigInt(data.
|
|
150
|
-
const slippageBps = BigInt(Math.round(
|
|
179
|
+
const estimatedAmountOut = BigInt(data.estimatedAmountOut);
|
|
180
|
+
const slippageBps = BigInt(Math.round(slippagePercent * 100));
|
|
151
181
|
const estimatedAmountOutAfterSlippage = estimatedAmountOut * (10000n - slippageBps) / 10000n;
|
|
182
|
+
const pricePerTokenOutInUsd = data.estimatedAmountOutUsd / Number(data.estimatedAmountOut);
|
|
183
|
+
const amountOutUsdAfterSlippage = Number(estimatedAmountOutAfterSlippage) * pricePerTokenOutInUsd;
|
|
184
|
+
const minStablecoinsAmountValue = BigInt(data.estimatedAmountInAsMinStablecoinAmount);
|
|
185
|
+
const minStablecoinsAmountAfterSlippage = minStablecoinsAmountValue * (10000n - slippageBps) / 10000n;
|
|
152
186
|
const pricePerInputToken = estimatedAmountOut * 10n ** BigInt(params.tokenIn.decimals ?? 18) / BigInt(params.amount);
|
|
153
187
|
return {
|
|
154
|
-
amountOut:
|
|
155
|
-
amountOutUsd:
|
|
188
|
+
amountOut: estimatedAmountOutAfterSlippage,
|
|
189
|
+
amountOutUsd: amountOutUsdAfterSlippage,
|
|
156
190
|
amountInUsd: data.amountInUsd,
|
|
157
|
-
|
|
191
|
+
// Input USD stays the same
|
|
192
|
+
minStablecoinsAmount: minStablecoinsAmountAfterSlippage,
|
|
158
193
|
tokenIn: {
|
|
159
194
|
address: params.tokenIn.address,
|
|
160
195
|
decimals: params.tokenIn.decimals ?? 18,
|
|
@@ -165,12 +200,13 @@ async function getQuote(params) {
|
|
|
165
200
|
decimals: params.tokenOut.decimals ?? 18,
|
|
166
201
|
chainId: params.destChainId
|
|
167
202
|
},
|
|
168
|
-
amountIn: params.amount,
|
|
203
|
+
amountIn: BigInt(params.amount),
|
|
169
204
|
pricePerInputToken,
|
|
170
|
-
slippage,
|
|
205
|
+
slippage: slippagePercent,
|
|
171
206
|
internal: {
|
|
172
207
|
...data,
|
|
173
|
-
estimatedAmountOutReduced: estimatedAmountOutAfterSlippage
|
|
208
|
+
estimatedAmountOutReduced: estimatedAmountOutAfterSlippage,
|
|
209
|
+
estimatedAmountOutUsdReduced: amountOutUsdAfterSlippage
|
|
174
210
|
},
|
|
175
211
|
warning
|
|
176
212
|
};
|
|
@@ -188,7 +224,7 @@ function buildQuoteParams({
|
|
|
188
224
|
tokenOut,
|
|
189
225
|
sourceChainId,
|
|
190
226
|
destChainId,
|
|
191
|
-
amount: parseUnits(amount.toString(), tokenIn.decimals ?? 18),
|
|
227
|
+
amount: parseUnits(amount.toString(), tokenIn.decimals ?? 18).toString(),
|
|
192
228
|
slippage
|
|
193
229
|
};
|
|
194
230
|
}
|
|
@@ -235,107 +271,73 @@ async function getBalances(params, options) {
|
|
|
235
271
|
const evmItems = data.evm?.items ?? [];
|
|
236
272
|
const svmItems = data.svm?.items ?? [];
|
|
237
273
|
const combined = [...evmItems, ...svmItems];
|
|
274
|
+
const filtered = combined.filter(
|
|
275
|
+
(b) => CURRENT_SUPPORTED.includes(b.chainId)
|
|
276
|
+
);
|
|
238
277
|
return {
|
|
239
|
-
results:
|
|
278
|
+
results: filtered,
|
|
240
279
|
nextCursorEvm: data.evm?.cursor ?? null,
|
|
241
280
|
nextCursorSvm: data.svm?.cursor ?? null
|
|
242
281
|
};
|
|
243
282
|
}
|
|
244
283
|
|
|
245
|
-
// src/core/
|
|
246
|
-
import {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const res = await connection.getSignatureStatus(txHash, { searchTransactionHistory: true });
|
|
264
|
-
if (res?.value?.confirmationStatus === "confirmed" || res?.value?.confirmationStatus === "finalized") {
|
|
265
|
-
return txHash;
|
|
266
|
-
}
|
|
267
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
268
|
-
}
|
|
269
|
-
throw new Error(`Transaction not confirmed after ${maxRetries * (delayMs / 1e3)}s`);
|
|
270
|
-
};
|
|
271
|
-
const signTypedData = async () => {
|
|
272
|
-
throw new Error("signTypedData not implemented for Solana");
|
|
273
|
-
};
|
|
274
|
-
const switchChain = async (newChainId) => {
|
|
275
|
-
_chainId = newChainId;
|
|
276
|
-
};
|
|
284
|
+
// src/core/token-list.ts
|
|
285
|
+
import { TOKEN_SEARCH_API_BASE_URL as TOKEN_SEARCH_API_BASE_URL2 } from "@shogun-sdk/intents-sdk";
|
|
286
|
+
async function getTokenList(params) {
|
|
287
|
+
const url = new URL(`${TOKEN_SEARCH_API_BASE_URL2}/tokens/search`);
|
|
288
|
+
if (params.q) url.searchParams.append("q", params.q);
|
|
289
|
+
if (params.networkId) url.searchParams.append("networkId", String(params.networkId));
|
|
290
|
+
if (params.page) url.searchParams.append("page", String(params.page));
|
|
291
|
+
if (params.limit) url.searchParams.append("limit", String(params.limit));
|
|
292
|
+
const res = await fetch(url.toString(), {
|
|
293
|
+
signal: params.signal
|
|
294
|
+
});
|
|
295
|
+
if (!res.ok) {
|
|
296
|
+
throw new Error(`Failed to fetch tokens: ${res.status} ${res.statusText}`);
|
|
297
|
+
}
|
|
298
|
+
const data = await res.json();
|
|
299
|
+
const filteredResults = data.results.filter(
|
|
300
|
+
(token) => CURRENT_SUPPORTED.includes(token.chainId)
|
|
301
|
+
);
|
|
277
302
|
return {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
sendTransaction,
|
|
282
|
-
switchChain,
|
|
283
|
-
signTypedData,
|
|
284
|
-
rpcUrl
|
|
303
|
+
...data,
|
|
304
|
+
results: filteredResults,
|
|
305
|
+
count: filteredResults.length
|
|
285
306
|
};
|
|
286
|
-
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// src/core/token.ts
|
|
310
|
+
import { TOKEN_SEARCH_API_BASE_URL as TOKEN_SEARCH_API_BASE_URL3 } from "@shogun-sdk/intents-sdk";
|
|
311
|
+
async function getTokensData(addresses) {
|
|
312
|
+
if (!addresses?.length) return [];
|
|
313
|
+
const response = await fetch(`${TOKEN_SEARCH_API_BASE_URL3}/tokens/tokens`, {
|
|
314
|
+
method: "POST",
|
|
315
|
+
headers: {
|
|
316
|
+
"Content-Type": "application/json",
|
|
317
|
+
accept: "*/*"
|
|
318
|
+
},
|
|
319
|
+
body: JSON.stringify({ addresses })
|
|
320
|
+
});
|
|
321
|
+
if (!response.ok) {
|
|
322
|
+
throw new Error(`Failed to fetch token data: ${response.statusText}`);
|
|
323
|
+
}
|
|
324
|
+
const data = await response.json();
|
|
325
|
+
const filtered = data.filter((t) => CURRENT_SUPPORTED.includes(Number(t.chainId)));
|
|
326
|
+
return filtered;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// src/core/execute/execute.ts
|
|
330
|
+
import { ChainID as ChainID2, isEvmChain as isEvmChain3 } from "@shogun-sdk/intents-sdk";
|
|
331
|
+
import "viem";
|
|
287
332
|
|
|
288
333
|
// src/wallet-adapter/evm-wallet-adapter/adapter.ts
|
|
289
|
-
import "ethers/lib/ethers.js";
|
|
290
|
-
import { hexValue } from "ethers/lib/utils.js";
|
|
291
334
|
import {
|
|
292
|
-
custom
|
|
335
|
+
custom,
|
|
336
|
+
publicActions
|
|
293
337
|
} from "viem";
|
|
294
338
|
function isEVMTransaction(tx) {
|
|
295
339
|
return typeof tx.from === "string";
|
|
296
340
|
}
|
|
297
|
-
var adaptEthersSigner = (signer, transport) => {
|
|
298
|
-
const signTypedData = async (signData) => {
|
|
299
|
-
const typedSigner = signer;
|
|
300
|
-
return await typedSigner._signTypedData(
|
|
301
|
-
signData.domain,
|
|
302
|
-
signData.types,
|
|
303
|
-
signData.value
|
|
304
|
-
);
|
|
305
|
-
};
|
|
306
|
-
const sendTransaction = async (transaction) => {
|
|
307
|
-
if (!isEVMTransaction(transaction)) {
|
|
308
|
-
throw new Error("Expected EVMTransaction but got SolanaTransaction");
|
|
309
|
-
}
|
|
310
|
-
const tx = await signer.sendTransaction({
|
|
311
|
-
from: transaction.from,
|
|
312
|
-
to: transaction.to,
|
|
313
|
-
data: transaction.data,
|
|
314
|
-
value: transaction.value
|
|
315
|
-
});
|
|
316
|
-
return tx.hash;
|
|
317
|
-
};
|
|
318
|
-
const switchChain = async (chainId) => {
|
|
319
|
-
try {
|
|
320
|
-
await window.ethereum.request({
|
|
321
|
-
method: "wallet_switchEthereumChain",
|
|
322
|
-
params: [{ chainId: hexValue(chainId) }]
|
|
323
|
-
});
|
|
324
|
-
} catch (error) {
|
|
325
|
-
console.error("Failed to switch chain:", error);
|
|
326
|
-
throw error;
|
|
327
|
-
}
|
|
328
|
-
};
|
|
329
|
-
return {
|
|
330
|
-
vmType: "EVM" /* EVM */,
|
|
331
|
-
transport,
|
|
332
|
-
getChainId: async () => signer.getChainId(),
|
|
333
|
-
address: async () => signer.getAddress(),
|
|
334
|
-
sendTransaction,
|
|
335
|
-
signTypedData,
|
|
336
|
-
switchChain
|
|
337
|
-
};
|
|
338
|
-
};
|
|
339
341
|
var adaptViemWallet = (wallet) => {
|
|
340
342
|
const signTypedData = async (signData) => {
|
|
341
343
|
return await wallet.signTypedData({
|
|
@@ -350,15 +352,26 @@ var adaptViemWallet = (wallet) => {
|
|
|
350
352
|
if (!isEVMTransaction(transaction)) {
|
|
351
353
|
throw new Error("Expected EVMTransaction but got SolanaTransaction");
|
|
352
354
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
355
|
+
if (wallet.transport.type === "http") {
|
|
356
|
+
const request = await wallet.prepareTransactionRequest({
|
|
357
|
+
to: transaction.to,
|
|
358
|
+
data: transaction.data,
|
|
359
|
+
value: transaction.value,
|
|
360
|
+
chain: wallet.chain
|
|
361
|
+
});
|
|
362
|
+
const serializedTransaction = await wallet.signTransaction(request);
|
|
363
|
+
const tx = await wallet.sendRawTransaction({ serializedTransaction });
|
|
364
|
+
return tx;
|
|
365
|
+
} else {
|
|
366
|
+
const hash = await wallet.sendTransaction({
|
|
367
|
+
to: transaction.to,
|
|
368
|
+
data: transaction.data,
|
|
369
|
+
value: transaction.value,
|
|
370
|
+
chain: wallet.chain,
|
|
371
|
+
account: wallet.account
|
|
372
|
+
});
|
|
373
|
+
return hash;
|
|
374
|
+
}
|
|
362
375
|
};
|
|
363
376
|
const switchChain = async (chainId) => {
|
|
364
377
|
try {
|
|
@@ -381,6 +394,20 @@ var adaptViemWallet = (wallet) => {
|
|
|
381
394
|
if (!addr) throw new Error("No address found");
|
|
382
395
|
return addr;
|
|
383
396
|
};
|
|
397
|
+
const readContract = async ({
|
|
398
|
+
address: address2,
|
|
399
|
+
abi,
|
|
400
|
+
functionName,
|
|
401
|
+
args = []
|
|
402
|
+
}) => {
|
|
403
|
+
const publicClient = wallet.extend(publicActions);
|
|
404
|
+
return await publicClient.readContract({
|
|
405
|
+
address: address2,
|
|
406
|
+
abi,
|
|
407
|
+
functionName,
|
|
408
|
+
args
|
|
409
|
+
});
|
|
410
|
+
};
|
|
384
411
|
return {
|
|
385
412
|
vmType: "EVM" /* EVM */,
|
|
386
413
|
transport: custom(wallet.transport),
|
|
@@ -388,39 +415,65 @@ var adaptViemWallet = (wallet) => {
|
|
|
388
415
|
address,
|
|
389
416
|
sendTransaction,
|
|
390
417
|
signTypedData,
|
|
391
|
-
switchChain
|
|
418
|
+
switchChain,
|
|
419
|
+
readContract
|
|
392
420
|
};
|
|
393
421
|
};
|
|
394
422
|
|
|
395
|
-
// src/core/
|
|
423
|
+
// src/core/execute/handleEvmExecution.ts
|
|
396
424
|
import {
|
|
397
425
|
getEVMSingleChainOrderTypedData,
|
|
398
|
-
getEVMCrossChainOrderTypedData
|
|
399
|
-
PERMIT2_ADDRESS
|
|
426
|
+
getEVMCrossChainOrderTypedData
|
|
400
427
|
} from "@shogun-sdk/intents-sdk";
|
|
401
|
-
import { encodeFunctionData
|
|
428
|
+
import { encodeFunctionData as encodeFunctionData2 } from "viem";
|
|
402
429
|
|
|
403
|
-
// src/core/
|
|
430
|
+
// src/core/execute/stageMessages.ts
|
|
404
431
|
var DEFAULT_STAGE_MESSAGES = {
|
|
405
432
|
processing: "Preparing transaction for execution",
|
|
406
433
|
approving: "Approving token allowance",
|
|
407
434
|
approved: "Token approved successfully",
|
|
408
|
-
signing: "Signing
|
|
409
|
-
submitting: "Submitting
|
|
410
|
-
|
|
411
|
-
|
|
435
|
+
signing: "Signing transaction for submission",
|
|
436
|
+
submitting: "Submitting transaction",
|
|
437
|
+
initiated: "Transaction initiated.",
|
|
438
|
+
success: "Transaction Executed successfully",
|
|
439
|
+
success_limit: "Limit order has been submitted successfully.",
|
|
440
|
+
shogun_processing: "Shogun is processing your transaction",
|
|
441
|
+
error: "Transaction failed during submission"
|
|
412
442
|
};
|
|
413
443
|
|
|
414
|
-
// src/core/
|
|
444
|
+
// src/core/execute/buildOrder.ts
|
|
415
445
|
import { CrossChainOrder, SingleChainOrder } from "@shogun-sdk/intents-sdk";
|
|
446
|
+
import { formatUnits, parseUnits as parseUnits2 } from "viem";
|
|
447
|
+
|
|
448
|
+
// src/utils/order.ts
|
|
449
|
+
var OrderExecutionType = /* @__PURE__ */ ((OrderExecutionType2) => {
|
|
450
|
+
OrderExecutionType2["LIMIT"] = "limit";
|
|
451
|
+
OrderExecutionType2["MARKET"] = "market";
|
|
452
|
+
return OrderExecutionType2;
|
|
453
|
+
})(OrderExecutionType || {});
|
|
454
|
+
|
|
455
|
+
// src/core/execute/buildOrder.ts
|
|
416
456
|
async function buildOrder({
|
|
417
457
|
quote,
|
|
418
458
|
accountAddress,
|
|
419
459
|
destination,
|
|
420
460
|
deadline,
|
|
421
|
-
isSingleChain
|
|
461
|
+
isSingleChain,
|
|
462
|
+
orderType,
|
|
463
|
+
options
|
|
422
464
|
}) {
|
|
423
465
|
const { tokenIn, tokenOut } = quote;
|
|
466
|
+
let amountOutMin = BigInt(quote.internal.estimatedAmountOutReduced);
|
|
467
|
+
if (orderType === "limit" /* LIMIT */ && options && "executionPrice" in options) {
|
|
468
|
+
const executionPrice = Number(options.executionPrice);
|
|
469
|
+
if (Number.isFinite(executionPrice) && executionPrice > 0) {
|
|
470
|
+
const decimalsIn = tokenIn.decimals ?? 18;
|
|
471
|
+
const decimalsOut = tokenOut.decimals ?? 18;
|
|
472
|
+
const formattedAmountIn = Number(formatUnits(BigInt(quote.amountIn.toString()), decimalsIn));
|
|
473
|
+
const rawAmountOut = formattedAmountIn * executionPrice;
|
|
474
|
+
amountOutMin = parseUnits2(rawAmountOut.toString(), decimalsOut);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
424
477
|
if (isSingleChain) {
|
|
425
478
|
return await SingleChainOrder.create({
|
|
426
479
|
user: accountAddress,
|
|
@@ -428,7 +481,7 @@ async function buildOrder({
|
|
|
428
481
|
tokenIn: tokenIn.address,
|
|
429
482
|
tokenOut: tokenOut.address,
|
|
430
483
|
amountIn: quote.amountIn,
|
|
431
|
-
amountOutMin
|
|
484
|
+
amountOutMin,
|
|
432
485
|
deadline,
|
|
433
486
|
destinationAddress: destination
|
|
434
487
|
});
|
|
@@ -442,12 +495,161 @@ async function buildOrder({
|
|
|
442
495
|
destinationTokenAddress: tokenOut.address,
|
|
443
496
|
destinationAddress: destination,
|
|
444
497
|
deadline,
|
|
445
|
-
destinationTokenMinAmount:
|
|
498
|
+
destinationTokenMinAmount: amountOutMin,
|
|
446
499
|
minStablecoinAmount: quote.minStablecoinsAmount
|
|
447
500
|
});
|
|
448
501
|
}
|
|
449
502
|
|
|
450
|
-
// src/
|
|
503
|
+
// src/utils/pollOrderStatus.ts
|
|
504
|
+
import { AUCTIONEER_URL } from "@shogun-sdk/intents-sdk";
|
|
505
|
+
async function pollOrderStatus(address, orderId, options = {}) {
|
|
506
|
+
const { intervalMs = 2e3, timeoutMs = 3e5 } = options;
|
|
507
|
+
const startTime = Date.now();
|
|
508
|
+
const isDebug = process.env.NODE_ENV !== "production";
|
|
509
|
+
const isEvmAddress = /^0x[a-fA-F0-9]{40}$/.test(address);
|
|
510
|
+
const isSuiAddress = /^0x[a-fA-F0-9]{64}$/.test(address);
|
|
511
|
+
const isSolanaAddress = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address);
|
|
512
|
+
let queryParam;
|
|
513
|
+
if (isEvmAddress) queryParam = `evmWallets=${address}`;
|
|
514
|
+
else if (isSuiAddress) queryParam = `suiWallets=${address}`;
|
|
515
|
+
else if (isSolanaAddress) queryParam = `solanaWallets=${address}`;
|
|
516
|
+
else throw new Error(`Unrecognized wallet address format: ${address}`);
|
|
517
|
+
const queryUrl = `${AUCTIONEER_URL}/user_intent?${queryParam}`;
|
|
518
|
+
return new Promise((resolve, reject) => {
|
|
519
|
+
const pollInterval = setInterval(async () => {
|
|
520
|
+
try {
|
|
521
|
+
if (Date.now() - startTime > timeoutMs) {
|
|
522
|
+
clearInterval(pollInterval);
|
|
523
|
+
return resolve("Timeout");
|
|
524
|
+
}
|
|
525
|
+
const res = await fetch(queryUrl, {
|
|
526
|
+
method: "GET",
|
|
527
|
+
headers: { "Content-Type": "application/json" }
|
|
528
|
+
});
|
|
529
|
+
if (!res.ok) {
|
|
530
|
+
clearInterval(pollInterval);
|
|
531
|
+
return reject(
|
|
532
|
+
new Error(`Failed to fetch orders: ${res.status} ${res.statusText}`)
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
const json = await res.json();
|
|
536
|
+
const data = json?.data ?? {};
|
|
537
|
+
const allOrders = [
|
|
538
|
+
...data.crossChainDcaOrders ?? [],
|
|
539
|
+
...data.crossChainLimitOrders ?? [],
|
|
540
|
+
...data.singleChainDcaOrders ?? [],
|
|
541
|
+
...data.singleChainLimitOrders ?? []
|
|
542
|
+
];
|
|
543
|
+
const targetOrder = allOrders.find((o) => o.orderId === orderId);
|
|
544
|
+
if (!targetOrder) {
|
|
545
|
+
if (isDebug)
|
|
546
|
+
console.debug(`[pollOrderStatus] [${orderId}] Not found yet`);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
const { orderStatus } = targetOrder;
|
|
550
|
+
if (isDebug) {
|
|
551
|
+
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
552
|
+
console.debug(`targetOrder`, targetOrder);
|
|
553
|
+
console.debug(
|
|
554
|
+
`[pollOrderStatus] [${orderId}] status=${orderStatus} (elapsed ${elapsed}s)`
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
if (["Fulfilled", "Cancelled", "Outdated"].includes(orderStatus)) {
|
|
558
|
+
clearInterval(pollInterval);
|
|
559
|
+
return resolve(orderStatus);
|
|
560
|
+
}
|
|
561
|
+
} catch (error) {
|
|
562
|
+
clearInterval(pollInterval);
|
|
563
|
+
return reject(error);
|
|
564
|
+
}
|
|
565
|
+
}, intervalMs);
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// src/core/execute/handleOrderPollingResult.ts
|
|
570
|
+
async function handleOrderPollingResult({
|
|
571
|
+
status,
|
|
572
|
+
orderId,
|
|
573
|
+
chainId,
|
|
574
|
+
update,
|
|
575
|
+
messageFor
|
|
576
|
+
}) {
|
|
577
|
+
switch (status) {
|
|
578
|
+
case "Fulfilled":
|
|
579
|
+
update("success", messageFor("success"));
|
|
580
|
+
return {
|
|
581
|
+
status: true,
|
|
582
|
+
orderId,
|
|
583
|
+
chainId,
|
|
584
|
+
finalStatus: status,
|
|
585
|
+
stage: "success"
|
|
586
|
+
};
|
|
587
|
+
case "Cancelled":
|
|
588
|
+
update("error", "Order was cancelled before fulfillment");
|
|
589
|
+
break;
|
|
590
|
+
case "Timeout":
|
|
591
|
+
update("error", "Order polling timed out");
|
|
592
|
+
break;
|
|
593
|
+
case "NotFound":
|
|
594
|
+
default:
|
|
595
|
+
update("error", "Order not found");
|
|
596
|
+
break;
|
|
597
|
+
}
|
|
598
|
+
return {
|
|
599
|
+
status: false,
|
|
600
|
+
orderId,
|
|
601
|
+
chainId,
|
|
602
|
+
finalStatus: status,
|
|
603
|
+
stage: "error"
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// src/core/execute/ensurePermit2Allowance.ts
|
|
608
|
+
import { encodeFunctionData, erc20Abi, maxUint256 } from "viem";
|
|
609
|
+
import { PERMIT2_ADDRESS } from "@shogun-sdk/intents-sdk";
|
|
610
|
+
async function ensurePermit2Allowance({
|
|
611
|
+
chainId,
|
|
612
|
+
tokenIn,
|
|
613
|
+
wallet,
|
|
614
|
+
accountAddress,
|
|
615
|
+
requiredAmount,
|
|
616
|
+
increaseByDelta = false
|
|
617
|
+
}) {
|
|
618
|
+
const spender = PERMIT2_ADDRESS[chainId];
|
|
619
|
+
let currentAllowance = 0n;
|
|
620
|
+
try {
|
|
621
|
+
if (!wallet.readContract) {
|
|
622
|
+
throw new Error("Wallet does not implement readContract()");
|
|
623
|
+
}
|
|
624
|
+
currentAllowance = await wallet.readContract({
|
|
625
|
+
address: tokenIn,
|
|
626
|
+
abi: erc20Abi,
|
|
627
|
+
functionName: "allowance",
|
|
628
|
+
args: [accountAddress, spender]
|
|
629
|
+
});
|
|
630
|
+
} catch (error) {
|
|
631
|
+
console.warn(`[Permit2] Failed to read allowance for ${tokenIn}`, error);
|
|
632
|
+
}
|
|
633
|
+
const approvalAmount = increaseByDelta ? currentAllowance + requiredAmount : maxUint256;
|
|
634
|
+
console.debug(
|
|
635
|
+
`[Permit2] Approving ${approvalAmount} for ${tokenIn} (current: ${currentAllowance}, required: ${requiredAmount})`
|
|
636
|
+
);
|
|
637
|
+
await wallet.sendTransaction({
|
|
638
|
+
to: tokenIn,
|
|
639
|
+
from: accountAddress,
|
|
640
|
+
data: encodeFunctionData({
|
|
641
|
+
abi: erc20Abi,
|
|
642
|
+
functionName: "approve",
|
|
643
|
+
args: [spender, approvalAmount]
|
|
644
|
+
}),
|
|
645
|
+
value: 0n
|
|
646
|
+
});
|
|
647
|
+
console.info(
|
|
648
|
+
`[Permit2] Approval transaction sent for ${tokenIn} on chain ${chainId}`
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// src/core/execute/handleEvmExecution.ts
|
|
451
653
|
async function handleEvmExecution({
|
|
452
654
|
recipientAddress,
|
|
453
655
|
quote,
|
|
@@ -455,18 +657,22 @@ async function handleEvmExecution({
|
|
|
455
657
|
accountAddress,
|
|
456
658
|
wallet,
|
|
457
659
|
isSingleChain,
|
|
458
|
-
|
|
459
|
-
|
|
660
|
+
update,
|
|
661
|
+
orderType,
|
|
662
|
+
options
|
|
460
663
|
}) {
|
|
461
|
-
const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage];
|
|
664
|
+
const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage] ?? "";
|
|
665
|
+
const deadline = options?.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
|
|
462
666
|
await wallet.switchChain(chainId);
|
|
463
667
|
const tokenIn = normalizeNative(chainId, quote.tokenIn.address);
|
|
668
|
+
quote.tokenOut.address = normalizeEvmTokenAddress(quote.tokenOut.address);
|
|
464
669
|
const shouldWrapNative = isNativeAddress(quote.tokenIn.address);
|
|
465
670
|
update("processing", shouldWrapNative ? `${messageFor("processing")} (wrapping native token)` : messageFor("processing"));
|
|
466
671
|
if (shouldWrapNative) {
|
|
672
|
+
quote.tokenIn.address === tokenIn;
|
|
467
673
|
await wallet.sendTransaction({
|
|
468
674
|
to: tokenIn,
|
|
469
|
-
data:
|
|
675
|
+
data: encodeFunctionData2({
|
|
470
676
|
abi: [{ type: "function", name: "deposit", stateMutability: "payable", inputs: [], outputs: [] }],
|
|
471
677
|
functionName: "deposit",
|
|
472
678
|
args: []
|
|
@@ -475,48 +681,74 @@ async function handleEvmExecution({
|
|
|
475
681
|
from: accountAddress
|
|
476
682
|
});
|
|
477
683
|
}
|
|
478
|
-
update("
|
|
479
|
-
await
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
}),
|
|
486
|
-
value: 0n,
|
|
487
|
-
from: accountAddress
|
|
684
|
+
update("processing", messageFor("approving"));
|
|
685
|
+
await ensurePermit2Allowance({
|
|
686
|
+
chainId,
|
|
687
|
+
tokenIn,
|
|
688
|
+
wallet,
|
|
689
|
+
accountAddress,
|
|
690
|
+
requiredAmount: BigInt(quote.amountIn)
|
|
488
691
|
});
|
|
489
|
-
update("
|
|
692
|
+
update("processing", messageFor("approved"));
|
|
490
693
|
const destination = recipientAddress ?? accountAddress;
|
|
491
694
|
const order = await buildOrder({
|
|
492
695
|
quote,
|
|
493
696
|
accountAddress,
|
|
494
697
|
destination,
|
|
495
698
|
deadline,
|
|
496
|
-
isSingleChain
|
|
699
|
+
isSingleChain,
|
|
700
|
+
orderType,
|
|
701
|
+
options
|
|
497
702
|
});
|
|
498
|
-
|
|
703
|
+
console.debug(`order`, order);
|
|
704
|
+
update("processing", messageFor("signing"));
|
|
499
705
|
const { orderTypedData, nonce } = isSingleChain ? await getEVMSingleChainOrderTypedData(order) : await getEVMCrossChainOrderTypedData(order);
|
|
706
|
+
const typedData = serializeBigIntsToStrings(orderTypedData);
|
|
500
707
|
if (!wallet.signTypedData) {
|
|
501
708
|
throw new Error("Wallet does not support EIP-712 signing");
|
|
502
709
|
}
|
|
503
|
-
const signature = await wallet.signTypedData(
|
|
504
|
-
|
|
710
|
+
const signature = await wallet.signTypedData({
|
|
711
|
+
domain: typedData.domain,
|
|
712
|
+
types: typedData.types,
|
|
713
|
+
primaryType: typedData.primaryType,
|
|
714
|
+
value: typedData.message,
|
|
715
|
+
message: typedData.message
|
|
716
|
+
});
|
|
717
|
+
update("processing", messageFor("submitting"));
|
|
505
718
|
const res = await order.sendToAuctioneer({ signature, nonce: nonce.toString() });
|
|
506
719
|
if (!res.success) {
|
|
507
720
|
throw new Error("Auctioneer submission failed");
|
|
508
721
|
}
|
|
509
|
-
update("
|
|
510
|
-
|
|
722
|
+
update("initiated", messageFor("initiated"));
|
|
723
|
+
const { intentId: orderId } = res.data;
|
|
724
|
+
update("initiated", messageFor("shogun_processing"));
|
|
725
|
+
if (orderType === "limit" /* LIMIT */) {
|
|
726
|
+
update("success", messageFor("success_limit"));
|
|
727
|
+
return {
|
|
728
|
+
status: true,
|
|
729
|
+
orderId,
|
|
730
|
+
chainId,
|
|
731
|
+
finalStatus: "OrderPlaced",
|
|
732
|
+
stage: "success"
|
|
733
|
+
};
|
|
734
|
+
} else {
|
|
735
|
+
const status = await pollOrderStatus(accountAddress, orderId);
|
|
736
|
+
return await handleOrderPollingResult({
|
|
737
|
+
status,
|
|
738
|
+
orderId,
|
|
739
|
+
chainId,
|
|
740
|
+
update,
|
|
741
|
+
messageFor
|
|
742
|
+
});
|
|
743
|
+
}
|
|
511
744
|
}
|
|
512
745
|
|
|
513
|
-
// src/core/
|
|
746
|
+
// src/core/execute/handleSolanaExecution.ts
|
|
514
747
|
import {
|
|
515
|
-
ChainID as ChainID3,
|
|
516
748
|
getSolanaSingleChainOrderInstructions,
|
|
517
749
|
getSolanaCrossChainOrderInstructions
|
|
518
750
|
} from "@shogun-sdk/intents-sdk";
|
|
519
|
-
import { VersionedTransaction
|
|
751
|
+
import { VersionedTransaction } from "@solana/web3.js";
|
|
520
752
|
async function handleSolanaExecution({
|
|
521
753
|
recipientAddress,
|
|
522
754
|
quote,
|
|
@@ -524,12 +756,14 @@ async function handleSolanaExecution({
|
|
|
524
756
|
isSingleChain,
|
|
525
757
|
update,
|
|
526
758
|
accountAddress,
|
|
527
|
-
|
|
759
|
+
orderType,
|
|
760
|
+
options
|
|
528
761
|
}) {
|
|
529
762
|
if (!wallet.rpcUrl) {
|
|
530
763
|
throw new Error("Solana wallet is missing rpcUrl");
|
|
531
764
|
}
|
|
532
|
-
const
|
|
765
|
+
const deadline = options?.deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
|
|
766
|
+
const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage] ?? "";
|
|
533
767
|
update("processing", messageFor("processing"));
|
|
534
768
|
const destination = recipientAddress ?? accountAddress;
|
|
535
769
|
const order = await buildOrder({
|
|
@@ -537,18 +771,19 @@ async function handleSolanaExecution({
|
|
|
537
771
|
accountAddress,
|
|
538
772
|
destination,
|
|
539
773
|
deadline,
|
|
540
|
-
isSingleChain
|
|
774
|
+
isSingleChain,
|
|
775
|
+
orderType,
|
|
776
|
+
options
|
|
541
777
|
});
|
|
542
778
|
const txData = await getSolanaOrderInstructions({
|
|
543
779
|
order,
|
|
544
780
|
isSingleChain,
|
|
545
781
|
rpcUrl: wallet.rpcUrl
|
|
546
782
|
});
|
|
547
|
-
const transaction =
|
|
548
|
-
update("
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
update("submitting", messageFor("submitting"));
|
|
783
|
+
const transaction = VersionedTransaction.deserialize(Uint8Array.from(txData.txBytes));
|
|
784
|
+
update("processing", messageFor("signing"));
|
|
785
|
+
await wallet.sendTransaction(transaction);
|
|
786
|
+
update("processing", messageFor("submitting"));
|
|
552
787
|
const response = await submitToAuctioneer({
|
|
553
788
|
order,
|
|
554
789
|
isSingleChain,
|
|
@@ -557,13 +792,28 @@ async function handleSolanaExecution({
|
|
|
557
792
|
if (!response.success) {
|
|
558
793
|
throw new Error("Auctioneer submission failed");
|
|
559
794
|
}
|
|
560
|
-
update("
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
795
|
+
update("initiated", messageFor("initiated"));
|
|
796
|
+
const { intentId: orderId } = response.data;
|
|
797
|
+
update("initiated", messageFor("shogun_processing"));
|
|
798
|
+
if (orderType === "limit" /* LIMIT */) {
|
|
799
|
+
update("success", messageFor("success_limit"));
|
|
800
|
+
return {
|
|
801
|
+
status: true,
|
|
802
|
+
orderId,
|
|
803
|
+
chainId: SOLANA_CHAIN_ID,
|
|
804
|
+
finalStatus: "OrderPlaced",
|
|
805
|
+
stage: "success"
|
|
806
|
+
};
|
|
807
|
+
} else {
|
|
808
|
+
const status = await pollOrderStatus(accountAddress, orderId);
|
|
809
|
+
return await handleOrderPollingResult({
|
|
810
|
+
status,
|
|
811
|
+
orderId,
|
|
812
|
+
chainId: SOLANA_CHAIN_ID,
|
|
813
|
+
update,
|
|
814
|
+
messageFor
|
|
815
|
+
});
|
|
816
|
+
}
|
|
567
817
|
}
|
|
568
818
|
async function getSolanaOrderInstructions({
|
|
569
819
|
order,
|
|
@@ -596,52 +846,93 @@ async function submitToAuctioneer({
|
|
|
596
846
|
});
|
|
597
847
|
}
|
|
598
848
|
|
|
599
|
-
// src/core/
|
|
849
|
+
// src/core/execute/execute.ts
|
|
600
850
|
async function executeOrder({
|
|
601
851
|
quote,
|
|
602
852
|
accountAddress,
|
|
603
853
|
recipientAddress,
|
|
604
854
|
wallet,
|
|
605
855
|
onStatus,
|
|
856
|
+
orderType = "market" /* MARKET */,
|
|
606
857
|
options = {}
|
|
607
858
|
}) {
|
|
608
|
-
const
|
|
859
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
860
|
+
const log = (...args) => {
|
|
861
|
+
if (isDev) console.debug("[OneShot::executeOrder]", ...args);
|
|
862
|
+
};
|
|
609
863
|
const messageFor = (stage) => DEFAULT_STAGE_MESSAGES[stage];
|
|
610
|
-
const update = (stage, message) =>
|
|
864
|
+
const update = (stage, message) => {
|
|
865
|
+
log("Stage:", stage, "| Message:", message ?? messageFor(stage));
|
|
866
|
+
onStatus?.(stage, message ?? messageFor(stage));
|
|
867
|
+
};
|
|
611
868
|
try {
|
|
869
|
+
log("Starting execution:", {
|
|
870
|
+
accountAddress,
|
|
871
|
+
recipientAddress,
|
|
872
|
+
tokenIn: quote?.tokenIn,
|
|
873
|
+
tokenOut: quote?.tokenOut
|
|
874
|
+
});
|
|
612
875
|
const adapter = normalizeWallet(wallet);
|
|
613
876
|
if (!adapter) throw new Error("No wallet provided");
|
|
614
877
|
const { tokenIn, tokenOut } = quote;
|
|
878
|
+
const srcChain = Number(tokenIn.chainId);
|
|
879
|
+
const destChain = Number(tokenOut.chainId);
|
|
880
|
+
if (!CURRENT_SUPPORTED.includes(srcChain) || !CURRENT_SUPPORTED.includes(destChain)) {
|
|
881
|
+
const unsupportedChains = [
|
|
882
|
+
!CURRENT_SUPPORTED.includes(srcChain) ? srcChain : null,
|
|
883
|
+
!CURRENT_SUPPORTED.includes(destChain) ? destChain : null
|
|
884
|
+
].filter(Boolean).join(", ");
|
|
885
|
+
const errorMsg = `Unsupported chain(s): ${unsupportedChains}`;
|
|
886
|
+
update("error", errorMsg);
|
|
887
|
+
log("Error:", errorMsg);
|
|
888
|
+
throw new Error(errorMsg);
|
|
889
|
+
}
|
|
615
890
|
const isSingleChain = tokenIn.chainId === tokenOut.chainId;
|
|
616
891
|
const chainId = Number(tokenIn.chainId);
|
|
617
|
-
update("processing"
|
|
618
|
-
if (
|
|
619
|
-
|
|
892
|
+
update("processing");
|
|
893
|
+
if (isEvmChain3(chainId)) {
|
|
894
|
+
log("Detected EVM chain:", chainId);
|
|
895
|
+
const result = await handleEvmExecution({
|
|
620
896
|
recipientAddress,
|
|
621
897
|
quote,
|
|
622
898
|
chainId,
|
|
623
899
|
accountAddress,
|
|
624
900
|
wallet: adapter,
|
|
625
901
|
isSingleChain,
|
|
626
|
-
|
|
627
|
-
|
|
902
|
+
update,
|
|
903
|
+
orderType,
|
|
904
|
+
options
|
|
628
905
|
});
|
|
906
|
+
log("EVM execution result:", result);
|
|
907
|
+
return result;
|
|
629
908
|
}
|
|
630
|
-
if (chainId ===
|
|
631
|
-
|
|
909
|
+
if (chainId === ChainID2.Solana) {
|
|
910
|
+
log("Detected Solana chain");
|
|
911
|
+
const result = await handleSolanaExecution({
|
|
632
912
|
recipientAddress,
|
|
633
913
|
quote,
|
|
634
914
|
accountAddress,
|
|
635
915
|
wallet: adapter,
|
|
636
916
|
isSingleChain,
|
|
637
|
-
|
|
638
|
-
|
|
917
|
+
update,
|
|
918
|
+
orderType,
|
|
919
|
+
options
|
|
639
920
|
});
|
|
921
|
+
log("Solana execution result:", result);
|
|
922
|
+
return result;
|
|
640
923
|
}
|
|
641
|
-
|
|
642
|
-
|
|
924
|
+
const unsupported = `Unsupported chain: ${chainId}`;
|
|
925
|
+
update("error", unsupported);
|
|
926
|
+
log("Error:", unsupported);
|
|
927
|
+
return { status: false, message: unsupported, stage: "error" };
|
|
643
928
|
} catch (error) {
|
|
644
|
-
|
|
929
|
+
let message = "An unknown error occurred";
|
|
930
|
+
if (error && typeof error === "object") {
|
|
931
|
+
const err = error;
|
|
932
|
+
message = err.details ?? err.message ?? message;
|
|
933
|
+
} else if (typeof error === "string") {
|
|
934
|
+
message = error;
|
|
935
|
+
}
|
|
645
936
|
update("error", message);
|
|
646
937
|
return { status: false, message, stage: "error" };
|
|
647
938
|
}
|
|
@@ -652,317 +943,502 @@ function normalizeWallet(wallet) {
|
|
|
652
943
|
return wallet;
|
|
653
944
|
}
|
|
654
945
|
|
|
655
|
-
// src/
|
|
656
|
-
import {
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
const
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
946
|
+
// src/core/orders/getOrders.ts
|
|
947
|
+
import { fetchUserOrders } from "@shogun-sdk/intents-sdk";
|
|
948
|
+
async function getOrders({
|
|
949
|
+
evmAddress,
|
|
950
|
+
solAddress
|
|
951
|
+
}) {
|
|
952
|
+
if (!evmAddress && !solAddress) {
|
|
953
|
+
throw new Error("At least one wallet address (EVM, Solana) must be provided.");
|
|
954
|
+
}
|
|
955
|
+
const orders = await fetchUserOrders(evmAddress, solAddress);
|
|
956
|
+
return orders;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// src/core/orders/cancelOrder.ts
|
|
960
|
+
import {
|
|
961
|
+
cancelCrossChainOrderInstructionsAsBytes,
|
|
962
|
+
cancelSingleChainOrderInstructionsAsBytes,
|
|
963
|
+
ChainID as ChainID3,
|
|
964
|
+
CROSS_CHAIN_GUARD_ADDRESSES,
|
|
965
|
+
PERMIT2_ADDRESS as PERMIT2_ADDRESS2,
|
|
966
|
+
SINGLE_CHAIN_GUARD_ADDRESSES
|
|
967
|
+
} from "@shogun-sdk/intents-sdk";
|
|
968
|
+
import {
|
|
969
|
+
VersionedMessage,
|
|
970
|
+
VersionedTransaction as VersionedTransaction2
|
|
971
|
+
} from "@solana/web3.js";
|
|
972
|
+
import { encodeFunctionData as encodeFunctionData3 } from "viem";
|
|
973
|
+
async function cancelIntentsOrder({
|
|
974
|
+
order,
|
|
975
|
+
wallet,
|
|
976
|
+
sol_rpc
|
|
977
|
+
}) {
|
|
978
|
+
const isCrossChain = "srcChainId" in order && "destChainId" in order && order.srcChainId !== order.destChainId;
|
|
979
|
+
const srcChain = "srcChainId" in order ? order.srcChainId : order.chainId;
|
|
980
|
+
const isSolanaOrder = srcChain === ChainID3.Solana;
|
|
981
|
+
if (isSolanaOrder) {
|
|
982
|
+
if (!isCrossChain) {
|
|
983
|
+
const { versionedMessageBytes: versionedMessageBytes2 } = await cancelSingleChainOrderInstructionsAsBytes(
|
|
984
|
+
order.orderId,
|
|
985
|
+
order.user,
|
|
986
|
+
{ rpcUrl: sol_rpc }
|
|
987
|
+
);
|
|
988
|
+
const message2 = VersionedMessage.deserialize(
|
|
989
|
+
versionedMessageBytes2
|
|
990
|
+
);
|
|
991
|
+
const tx2 = new VersionedTransaction2(message2);
|
|
992
|
+
return await wallet.sendTransaction(tx2);
|
|
679
993
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
994
|
+
const { versionedMessageBytes } = await cancelCrossChainOrderInstructionsAsBytes(
|
|
995
|
+
order.orderId,
|
|
996
|
+
order.user,
|
|
997
|
+
{ rpcUrl: sol_rpc }
|
|
998
|
+
);
|
|
999
|
+
const message = VersionedMessage.deserialize(
|
|
1000
|
+
versionedMessageBytes
|
|
1001
|
+
);
|
|
1002
|
+
const tx = new VersionedTransaction2(message);
|
|
1003
|
+
return await wallet.sendTransaction(tx);
|
|
1004
|
+
}
|
|
1005
|
+
const chainId = srcChain;
|
|
1006
|
+
const { readContract } = wallet;
|
|
1007
|
+
if (!readContract) {
|
|
1008
|
+
throw new Error("Wallet does not support readContract");
|
|
1009
|
+
}
|
|
1010
|
+
const nonce = BigInt(order.nonce ?? "0");
|
|
1011
|
+
const nonceWordPos = nonce >> 8n;
|
|
1012
|
+
const nonceBitPos = nonce - nonceWordPos * 256n;
|
|
1013
|
+
if (isCrossChain) {
|
|
1014
|
+
const [orderData = [false, false], currentNonceBitmap = 0n] = await Promise.all([
|
|
1015
|
+
readContract({
|
|
1016
|
+
address: CROSS_CHAIN_GUARD_ADDRESSES[chainId],
|
|
1017
|
+
abi: [
|
|
1018
|
+
{
|
|
1019
|
+
inputs: [{ name: "orderId", type: "bytes32" }],
|
|
1020
|
+
name: "orderData",
|
|
1021
|
+
outputs: [
|
|
1022
|
+
{ name: "initialized", type: "bool" },
|
|
1023
|
+
{ name: "deactivated", type: "bool" }
|
|
1024
|
+
],
|
|
1025
|
+
stateMutability: "view",
|
|
1026
|
+
type: "function"
|
|
1027
|
+
}
|
|
1028
|
+
],
|
|
1029
|
+
functionName: "orderData",
|
|
1030
|
+
args: [order.orderId]
|
|
1031
|
+
}),
|
|
1032
|
+
readContract({
|
|
1033
|
+
address: PERMIT2_ADDRESS2[chainId],
|
|
1034
|
+
abi: [
|
|
1035
|
+
{
|
|
1036
|
+
inputs: [
|
|
1037
|
+
{ name: "owner", type: "address" },
|
|
1038
|
+
{ name: "wordPos", type: "uint256" }
|
|
1039
|
+
],
|
|
1040
|
+
name: "nonceBitmap",
|
|
1041
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
1042
|
+
stateMutability: "view",
|
|
1043
|
+
type: "function"
|
|
1044
|
+
}
|
|
1045
|
+
],
|
|
1046
|
+
functionName: "nonceBitmap",
|
|
1047
|
+
args: [order.user, nonceWordPos]
|
|
1048
|
+
})
|
|
1049
|
+
]);
|
|
1050
|
+
const [initialized, deactivated] = orderData;
|
|
1051
|
+
if (initialized) {
|
|
1052
|
+
if (deactivated) {
|
|
1053
|
+
throw new Error("Order is already deactivated");
|
|
690
1054
|
}
|
|
691
|
-
|
|
692
|
-
|
|
1055
|
+
return await wallet.sendTransaction({
|
|
1056
|
+
to: CROSS_CHAIN_GUARD_ADDRESSES[order.srcChainId],
|
|
1057
|
+
data: encodeFunctionData3({
|
|
1058
|
+
abi: [
|
|
1059
|
+
{
|
|
1060
|
+
inputs: [
|
|
1061
|
+
{
|
|
1062
|
+
components: [
|
|
1063
|
+
{ name: "user", type: "address" },
|
|
1064
|
+
{ name: "tokenIn", type: "address" },
|
|
1065
|
+
{ name: "srcChainId", type: "uint256" },
|
|
1066
|
+
{ name: "deadline", type: "uint256" },
|
|
1067
|
+
{ name: "amountIn", type: "uint256" },
|
|
1068
|
+
{ name: "minStablecoinsAmount", type: "uint256" },
|
|
1069
|
+
{ name: "executionDetailsHash", type: "bytes32" },
|
|
1070
|
+
{ name: "nonce", type: "uint256" }
|
|
1071
|
+
],
|
|
1072
|
+
name: "orderInfo",
|
|
1073
|
+
type: "tuple"
|
|
1074
|
+
}
|
|
1075
|
+
],
|
|
1076
|
+
name: "cancelOrder",
|
|
1077
|
+
outputs: [],
|
|
1078
|
+
stateMutability: "nonpayable",
|
|
1079
|
+
type: "function"
|
|
1080
|
+
}
|
|
1081
|
+
],
|
|
1082
|
+
functionName: "cancelOrder",
|
|
1083
|
+
args: [
|
|
1084
|
+
{
|
|
1085
|
+
user: order.user,
|
|
1086
|
+
tokenIn: order.tokenIn,
|
|
1087
|
+
srcChainId: BigInt(order.srcChainId),
|
|
1088
|
+
deadline: BigInt(order.deadline),
|
|
1089
|
+
amountIn: BigInt(order.amountIn),
|
|
1090
|
+
minStablecoinsAmount: BigInt(order.minStablecoinsAmount),
|
|
1091
|
+
executionDetailsHash: order.executionDetailsHash,
|
|
1092
|
+
nonce
|
|
1093
|
+
}
|
|
1094
|
+
]
|
|
1095
|
+
}),
|
|
1096
|
+
value: BigInt(0),
|
|
1097
|
+
from: order.user
|
|
1098
|
+
});
|
|
1099
|
+
} else {
|
|
1100
|
+
if ((currentNonceBitmap & 1n << nonceBitPos) !== 0n) {
|
|
1101
|
+
throw new Error("Nonce is already invalidated");
|
|
1102
|
+
}
|
|
1103
|
+
const mask2 = 1n << nonceBitPos;
|
|
1104
|
+
return await wallet.sendTransaction({
|
|
1105
|
+
to: PERMIT2_ADDRESS2[order.srcChainId],
|
|
1106
|
+
data: encodeFunctionData3({
|
|
1107
|
+
abi: [
|
|
1108
|
+
{
|
|
1109
|
+
inputs: [
|
|
1110
|
+
{ name: "wordPos", type: "uint256" },
|
|
1111
|
+
{ name: "mask", type: "uint256" }
|
|
1112
|
+
],
|
|
1113
|
+
name: "invalidateUnorderedNonces",
|
|
1114
|
+
outputs: [],
|
|
1115
|
+
stateMutability: "nonpayable",
|
|
1116
|
+
type: "function"
|
|
1117
|
+
}
|
|
1118
|
+
],
|
|
1119
|
+
functionName: "invalidateUnorderedNonces",
|
|
1120
|
+
args: [nonceWordPos, mask2]
|
|
1121
|
+
}),
|
|
1122
|
+
value: BigInt(0),
|
|
1123
|
+
from: order.user
|
|
1124
|
+
});
|
|
693
1125
|
}
|
|
694
1126
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
return useMemo(
|
|
710
|
-
() => ({
|
|
711
|
-
/** Current fetched data (cached when possible) */
|
|
712
|
-
data,
|
|
713
|
-
/** Whether a request is in progress */
|
|
714
|
-
loading,
|
|
715
|
-
/** Error object if a request failed */
|
|
716
|
-
error,
|
|
717
|
-
/** Manually refetch the token list */
|
|
718
|
-
refetch: () => fetchTokens(),
|
|
719
|
-
/** Clear all cached token results (shared across hook instances) */
|
|
720
|
-
clearCache: () => tokenCache.clear()
|
|
1127
|
+
const [wasManuallyInitialized, currentBitmap = 0n] = await Promise.all([
|
|
1128
|
+
readContract({
|
|
1129
|
+
address: SINGLE_CHAIN_GUARD_ADDRESSES[chainId],
|
|
1130
|
+
abi: [
|
|
1131
|
+
{
|
|
1132
|
+
inputs: [{ name: "orderHash", type: "bytes32" }],
|
|
1133
|
+
name: "orderManuallyInitialized",
|
|
1134
|
+
outputs: [{ name: "", type: "bool" }],
|
|
1135
|
+
stateMutability: "view",
|
|
1136
|
+
type: "function"
|
|
1137
|
+
}
|
|
1138
|
+
],
|
|
1139
|
+
functionName: "orderManuallyInitialized",
|
|
1140
|
+
args: [order.orderId]
|
|
721
1141
|
}),
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
const isMounted = useRef2(true);
|
|
735
|
-
useEffect2(() => {
|
|
736
|
-
return () => {
|
|
737
|
-
isMounted.current = false;
|
|
738
|
-
};
|
|
739
|
-
}, []);
|
|
740
|
-
const execute = useCallback(
|
|
741
|
-
async ({
|
|
742
|
-
quote,
|
|
743
|
-
accountAddress,
|
|
744
|
-
recipientAddress,
|
|
745
|
-
wallet,
|
|
746
|
-
deadline
|
|
747
|
-
}) => {
|
|
748
|
-
if (!quote || !wallet) {
|
|
749
|
-
throw new Error("Quote and wallet are required for order execution.");
|
|
750
|
-
}
|
|
751
|
-
setLoading(true);
|
|
752
|
-
setError(null);
|
|
753
|
-
setData(null);
|
|
754
|
-
setMessage(null);
|
|
755
|
-
try {
|
|
756
|
-
const effectiveDeadline = deadline ?? Math.floor(Date.now() / 1e3) + 20 * 60;
|
|
757
|
-
const onStatus = (stage, msg) => {
|
|
758
|
-
if (!isMounted.current) return;
|
|
759
|
-
setStatus(stage);
|
|
760
|
-
if (msg) setMessage(msg);
|
|
761
|
-
};
|
|
762
|
-
const result = await executeOrder({
|
|
763
|
-
quote,
|
|
764
|
-
accountAddress,
|
|
765
|
-
recipientAddress,
|
|
766
|
-
wallet,
|
|
767
|
-
onStatus,
|
|
768
|
-
options: { deadline: effectiveDeadline }
|
|
769
|
-
});
|
|
770
|
-
if (!isMounted.current) return result;
|
|
771
|
-
setData(result);
|
|
772
|
-
setStatus(result.stage);
|
|
773
|
-
setMessage("Order executed successfully");
|
|
774
|
-
return result;
|
|
775
|
-
} catch (err) {
|
|
776
|
-
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
777
|
-
if (isMounted.current) {
|
|
778
|
-
setError(errorObj);
|
|
779
|
-
setStatus("error");
|
|
780
|
-
setMessage(errorObj.message);
|
|
781
|
-
setData({
|
|
782
|
-
status: false,
|
|
783
|
-
stage: "error",
|
|
784
|
-
message: errorObj.message
|
|
785
|
-
});
|
|
1142
|
+
readContract({
|
|
1143
|
+
address: PERMIT2_ADDRESS2[chainId],
|
|
1144
|
+
abi: [
|
|
1145
|
+
{
|
|
1146
|
+
inputs: [
|
|
1147
|
+
{ name: "owner", type: "address" },
|
|
1148
|
+
{ name: "wordPos", type: "uint256" }
|
|
1149
|
+
],
|
|
1150
|
+
name: "nonceBitmap",
|
|
1151
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
1152
|
+
stateMutability: "view",
|
|
1153
|
+
type: "function"
|
|
786
1154
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
1155
|
+
],
|
|
1156
|
+
functionName: "nonceBitmap",
|
|
1157
|
+
args: [order.user, nonceWordPos]
|
|
1158
|
+
})
|
|
1159
|
+
]);
|
|
1160
|
+
if (wasManuallyInitialized) {
|
|
1161
|
+
return await wallet.sendTransaction({
|
|
1162
|
+
to: SINGLE_CHAIN_GUARD_ADDRESSES[chainId],
|
|
1163
|
+
data: encodeFunctionData3({
|
|
1164
|
+
abi: [
|
|
1165
|
+
{
|
|
1166
|
+
inputs: [
|
|
1167
|
+
{
|
|
1168
|
+
name: "order",
|
|
1169
|
+
type: "tuple",
|
|
1170
|
+
components: [
|
|
1171
|
+
{ name: "amountIn", type: "uint256" },
|
|
1172
|
+
{ name: "tokenIn", type: "address" },
|
|
1173
|
+
{ name: "deadline", type: "uint256" },
|
|
1174
|
+
{ name: "nonce", type: "uint256" },
|
|
1175
|
+
{ name: "encodedExternalCallData", type: "bytes" },
|
|
1176
|
+
{
|
|
1177
|
+
name: "extraTransfers",
|
|
1178
|
+
type: "tuple[]",
|
|
1179
|
+
components: [
|
|
1180
|
+
{ name: "amount", type: "uint256" },
|
|
1181
|
+
{ name: "receiver", type: "address" },
|
|
1182
|
+
{ name: "token", type: "address" }
|
|
1183
|
+
]
|
|
1184
|
+
},
|
|
1185
|
+
{
|
|
1186
|
+
name: "requestedOutput",
|
|
1187
|
+
type: "tuple",
|
|
1188
|
+
components: [
|
|
1189
|
+
{ name: "amount", type: "uint256" },
|
|
1190
|
+
{ name: "receiver", type: "address" },
|
|
1191
|
+
{ name: "token", type: "address" }
|
|
1192
|
+
]
|
|
1193
|
+
},
|
|
1194
|
+
{ name: "user", type: "address" }
|
|
1195
|
+
]
|
|
1196
|
+
}
|
|
1197
|
+
],
|
|
1198
|
+
name: "cancelManuallyCreatedOrder",
|
|
1199
|
+
outputs: [],
|
|
1200
|
+
stateMutability: "nonpayable",
|
|
1201
|
+
type: "function"
|
|
1202
|
+
}
|
|
1203
|
+
],
|
|
1204
|
+
functionName: "cancelManuallyCreatedOrder",
|
|
1205
|
+
args: [
|
|
1206
|
+
{
|
|
1207
|
+
amountIn: BigInt(order.amountIn),
|
|
1208
|
+
tokenIn: order.tokenIn,
|
|
1209
|
+
deadline: BigInt(order.deadline),
|
|
1210
|
+
nonce,
|
|
1211
|
+
encodedExternalCallData: "0x",
|
|
1212
|
+
extraTransfers: (order.extraTransfers || []).map((t) => ({
|
|
1213
|
+
amount: BigInt(t.amount),
|
|
1214
|
+
receiver: t.receiver,
|
|
1215
|
+
token: t.token
|
|
1216
|
+
})),
|
|
1217
|
+
requestedOutput: {
|
|
1218
|
+
amount: BigInt(order.amountOutMin),
|
|
1219
|
+
receiver: order.destinationAddress,
|
|
1220
|
+
token: order.tokenOut
|
|
1221
|
+
},
|
|
1222
|
+
user: order.user
|
|
1223
|
+
}
|
|
1224
|
+
]
|
|
1225
|
+
}),
|
|
1226
|
+
value: 0n,
|
|
1227
|
+
from: order.user
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
const mask = 1n << nonceBitPos;
|
|
1231
|
+
if ((currentBitmap & mask) !== 0n) {
|
|
1232
|
+
throw new Error("Nonce is already invalidated");
|
|
1233
|
+
}
|
|
1234
|
+
return await wallet.sendTransaction({
|
|
1235
|
+
to: PERMIT2_ADDRESS2[chainId],
|
|
1236
|
+
data: encodeFunctionData3({
|
|
1237
|
+
abi: [
|
|
1238
|
+
{
|
|
1239
|
+
inputs: [
|
|
1240
|
+
{ name: "wordPos", type: "uint256" },
|
|
1241
|
+
{ name: "mask", type: "uint256" }
|
|
1242
|
+
],
|
|
1243
|
+
name: "invalidateUnorderedNonces",
|
|
1244
|
+
outputs: [],
|
|
1245
|
+
stateMutability: "nonpayable",
|
|
1246
|
+
type: "function"
|
|
1247
|
+
}
|
|
1248
|
+
],
|
|
1249
|
+
functionName: "invalidateUnorderedNonces",
|
|
1250
|
+
args: [nonceWordPos, mask]
|
|
881
1251
|
}),
|
|
882
|
-
|
|
883
|
-
|
|
1252
|
+
value: 0n,
|
|
1253
|
+
from: order.user
|
|
1254
|
+
});
|
|
884
1255
|
}
|
|
885
1256
|
|
|
886
|
-
// src/
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
throw
|
|
924
|
-
} finally {
|
|
925
|
-
setLoading(false);
|
|
1257
|
+
// src/core/client.ts
|
|
1258
|
+
var SwapSDK = class {
|
|
1259
|
+
constructor(config) {
|
|
1260
|
+
__publicField(this, "apiKey");
|
|
1261
|
+
/**
|
|
1262
|
+
* Fetches metadata for one or more tokens from the Shogun Token Search API.
|
|
1263
|
+
*
|
|
1264
|
+
* ---
|
|
1265
|
+
* ### Overview
|
|
1266
|
+
* `getTokensData` retrieves normalized token information — such as symbol, name,
|
|
1267
|
+
* decimals, logo URI, and verified status — for a given list of token addresses.
|
|
1268
|
+
*
|
|
1269
|
+
* It supports both **EVM** and **SVM (Solana)** tokens, returning metadata from
|
|
1270
|
+
* Shogun’s unified token registry.
|
|
1271
|
+
*
|
|
1272
|
+
* ---
|
|
1273
|
+
* @example
|
|
1274
|
+
* ```ts
|
|
1275
|
+
* const tokens = await getTokensData([
|
|
1276
|
+
* "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
|
|
1277
|
+
* "So11111111111111111111111111111111111111112", // SOL
|
|
1278
|
+
* ]);
|
|
1279
|
+
*
|
|
1280
|
+
* console.log(tokens);
|
|
1281
|
+
* [
|
|
1282
|
+
* { symbol: "USDC", name: "USD Coin", chainId: 1, decimals: 6, ... },
|
|
1283
|
+
* { symbol: "SOL", name: "Solana", chainId: 101, decimals: 9, ... }
|
|
1284
|
+
* ]
|
|
1285
|
+
* ```
|
|
1286
|
+
*
|
|
1287
|
+
* @param addresses - An array of token addresses (EVM or SVM) to fetch metadata for.
|
|
1288
|
+
* @returns A promise resolving to an array of {@link TokenInfo} objects.
|
|
1289
|
+
*
|
|
1290
|
+
* @throws Will throw an error if the network request fails or the API responds with a non-OK status.
|
|
1291
|
+
*/
|
|
1292
|
+
__publicField(this, "getTokensData", getTokensData.bind(this));
|
|
1293
|
+
if (!config.apiKey) {
|
|
1294
|
+
throw new Error("SwapSDK: Missing API key");
|
|
926
1295
|
}
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
1296
|
+
this.apiKey = config.apiKey;
|
|
1297
|
+
if (this.apiKey) void this.apiKey;
|
|
1298
|
+
}
|
|
1299
|
+
/**
|
|
1300
|
+
* Retrieves a swap quote for the given input and output tokens.
|
|
1301
|
+
*
|
|
1302
|
+
* @param params - Quote parameters including source/destination tokens and amount.
|
|
1303
|
+
* @returns A normalized `SwapQuoteResponse` containing output amount, route, and metadata.
|
|
1304
|
+
*/
|
|
1305
|
+
async getQuote(params) {
|
|
1306
|
+
return getQuote(params);
|
|
1307
|
+
}
|
|
1308
|
+
/**
|
|
1309
|
+
* Fetches token balances for the specified user wallet(s).
|
|
1310
|
+
*
|
|
1311
|
+
* Supports both EVM and SVM (Solana) wallet addresses.
|
|
1312
|
+
*
|
|
1313
|
+
* @param params - Wallet address and optional chain filters.
|
|
1314
|
+
* @param options - Optional abort signal for cancellation.
|
|
1315
|
+
* @returns A unified balance response with per-chain token details.
|
|
1316
|
+
*/
|
|
1317
|
+
async getBalances(params, options) {
|
|
1318
|
+
return getBalances(params, options);
|
|
1319
|
+
}
|
|
1320
|
+
/**
|
|
1321
|
+
* Retrieves a list of verified tokens based on search query or chain filter.
|
|
1322
|
+
*
|
|
1323
|
+
* @param params - Search parameters (query, chain ID, pagination options).
|
|
1324
|
+
* @returns Paginated `TokenSearchResponse` containing token metadata.
|
|
1325
|
+
*/
|
|
1326
|
+
async getTokenList(params) {
|
|
1327
|
+
return getTokenList(params);
|
|
1328
|
+
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Executes a prepared swap quote using the provided wallet and configuration.
|
|
1331
|
+
*
|
|
1332
|
+
* Handles:
|
|
1333
|
+
* - Token approval (if required)
|
|
1334
|
+
* - Transaction signing and broadcasting
|
|
1335
|
+
* - Confirmation polling and stage-based status updates
|
|
1336
|
+
*
|
|
1337
|
+
* Supports both:
|
|
1338
|
+
* - Market orders (with optional deadline)
|
|
1339
|
+
* - Limit orders (requires executionPrice and deadline)
|
|
1340
|
+
*
|
|
1341
|
+
* @param quote - The swap quote to execute, containing route and metadata.
|
|
1342
|
+
* @param accountAddress - The user's wallet address executing the swap.
|
|
1343
|
+
* @param recipientAddress - Optional recipient address for the output tokens (defaults to sender).
|
|
1344
|
+
* @param wallet - Adapted wallet instance (EVM/Solana) or a standard Viem `WalletClient`.
|
|
1345
|
+
* @param onStatus - Optional callback for receiving execution stage updates and messages.
|
|
1346
|
+
* @param orderType - Defines whether this is a market or limit order.
|
|
1347
|
+
* @param options - Execution parameters (different per order type).
|
|
1348
|
+
*
|
|
1349
|
+
* @returns A finalized execution result containing transaction hash, status, and any returned data.
|
|
1350
|
+
*
|
|
1351
|
+
* @example
|
|
1352
|
+
* ```ts
|
|
1353
|
+
* * Market order
|
|
1354
|
+
* const result = await sdk.executeTransaction({
|
|
1355
|
+
* quote,
|
|
1356
|
+
* accountAddress: "0x123...",
|
|
1357
|
+
* wallet,
|
|
1358
|
+
* orderType: OrderExecutionType.MARKET,
|
|
1359
|
+
* options: { deadline: 1800 },
|
|
1360
|
+
* onStatus: (stage, msg) => console.log(stage, msg),
|
|
1361
|
+
* });
|
|
1362
|
+
*
|
|
1363
|
+
* * Limit order
|
|
1364
|
+
* const result = await sdk.executeTransaction({
|
|
1365
|
+
* quote,
|
|
1366
|
+
* accountAddress: "0x123...",
|
|
1367
|
+
* wallet,
|
|
1368
|
+
* orderType: OrderExecutionType.LIMIT,
|
|
1369
|
+
* options: { executionPrice: "0.0021", deadline: 3600 },
|
|
1370
|
+
* });
|
|
1371
|
+
* ```
|
|
1372
|
+
*/
|
|
1373
|
+
async executeTransaction({
|
|
1374
|
+
quote,
|
|
1375
|
+
accountAddress,
|
|
1376
|
+
recipientAddress,
|
|
1377
|
+
wallet,
|
|
1378
|
+
onStatus,
|
|
1379
|
+
orderType,
|
|
1380
|
+
options
|
|
1381
|
+
}) {
|
|
1382
|
+
return executeOrder({
|
|
1383
|
+
quote,
|
|
1384
|
+
wallet,
|
|
1385
|
+
accountAddress,
|
|
1386
|
+
recipientAddress,
|
|
1387
|
+
onStatus,
|
|
1388
|
+
orderType,
|
|
1389
|
+
options
|
|
930
1390
|
});
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1391
|
+
}
|
|
1392
|
+
/**
|
|
1393
|
+
* Fetches all user orders (Market, Limit, Cross-chain) for connected wallets.
|
|
1394
|
+
*
|
|
1395
|
+
* ---
|
|
1396
|
+
* ### Overview
|
|
1397
|
+
* Retrieves both **single-chain** and **cross-chain** orders from the Shogun Intents API.
|
|
1398
|
+
* Works across EVM, Solana
|
|
1399
|
+
*
|
|
1400
|
+
* ---
|
|
1401
|
+
* @example
|
|
1402
|
+
* ```ts
|
|
1403
|
+
* const orders = await sdk.getOrders({
|
|
1404
|
+
* evmAddress: "0x123...",
|
|
1405
|
+
* solAddress: "9d12hF...abc",
|
|
1406
|
+
* });
|
|
1407
|
+
*
|
|
1408
|
+
* console.log(orders.singleChainLimitOrders);
|
|
1409
|
+
* ```
|
|
1410
|
+
*
|
|
1411
|
+
* @param params - Wallet addresses to fetch orders for (EVM, Solana).
|
|
1412
|
+
* @returns A structured {@link ApiUserOrders} object containing all user orders.
|
|
1413
|
+
*/
|
|
1414
|
+
async getOrders(params) {
|
|
1415
|
+
return getOrders(params);
|
|
1416
|
+
}
|
|
1417
|
+
/**
|
|
1418
|
+
* Cancels an order (single-chain or cross-chain) on EVM or Solana.
|
|
1419
|
+
*
|
|
1420
|
+
* Internally routes to the correct guard contract or Solana program to
|
|
1421
|
+
* deactivate the order or invalidate its nonce.
|
|
1422
|
+
*
|
|
1423
|
+
* @param params.order - Order payload returned from the Intents API.
|
|
1424
|
+
* @param params.wallet - Adapted wallet used to submit the cancellation transaction.
|
|
1425
|
+
* @param params.solRpc - Solana RPC URL used for SVM cancellations.
|
|
1426
|
+
*
|
|
1427
|
+
* @returns A transaction signature or hash from the underlying wallet.
|
|
1428
|
+
*/
|
|
1429
|
+
async cancelOrder(params) {
|
|
1430
|
+
return cancelIntentsOrder({
|
|
1431
|
+
order: params.order,
|
|
1432
|
+
wallet: params.wallet,
|
|
1433
|
+
sol_rpc: params.solRpc
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
};
|
|
949
1437
|
export {
|
|
950
|
-
|
|
951
|
-
|
|
1438
|
+
ChainId,
|
|
1439
|
+
OrderExecutionType,
|
|
952
1440
|
SupportedChains,
|
|
953
|
-
|
|
954
|
-
adaptSolanaWallet,
|
|
955
|
-
adaptViemWallet,
|
|
1441
|
+
SwapSDK,
|
|
956
1442
|
buildQuoteParams,
|
|
957
|
-
|
|
958
|
-
getBalances,
|
|
959
|
-
getQuote,
|
|
960
|
-
getTokenList,
|
|
961
|
-
isNativeAddress,
|
|
962
|
-
isViemWalletClient,
|
|
963
|
-
serializeBigIntsToStrings,
|
|
964
|
-
useBalances,
|
|
965
|
-
useExecuteOrder,
|
|
966
|
-
useQuote,
|
|
967
|
-
useTokenList
|
|
1443
|
+
isEvmChain
|
|
968
1444
|
};
|