@shogun-sdk/intents-sdk 1.2.6-test.14 → 1.2.6-test.16
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/index.cjs +277 -99
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +109 -22
- package/dist/index.d.ts +109 -22
- package/dist/index.js +277 -99
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
- package/src/auth/siwe.ts +10 -11
- package/src/constants.ts +32 -8
- package/src/core/evm/intent-helpers.ts +131 -8
- package/src/core/evm/intent-provider.ts +5 -4
- package/src/core/evm/permit2.ts +36 -0
- package/src/core/orders/api/fetch.ts +12 -39
- package/src/core/orders/cross-chain.ts +3 -0
- package/src/core/orders/single-chain.ts +21 -8
- package/src/core/sdk.ts +12 -6
- package/src/core/solana/intent-helpers.ts +6 -4
- package/src/core/sui/intent-helpers.ts +4 -1
- package/src/index.ts +4 -0
- package/src/types/api.ts +14 -3
- package/src/types/intent.ts +36 -2
- package/src/utils/base-validator.ts +0 -4
- package/src/utils/generate-execution-details-hash.ts +9 -32
- package/src/utils/quote/address.ts +4 -0
- package/src/utils/quote/aggregator.ts +29 -9
- package/src/utils/quote/pricing/constants.ts +33 -0
- package/src/utils/quote/pricing/decimals.ts +11 -0
- package/src/utils/quote/pricing/estimate-amount-out.ts +36 -0
- package/src/utils/quote/pricing/expenses.ts +62 -0
- package/src/utils/quote/pricing/gas.ts +28 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shogun-sdk/intents-sdk",
|
|
3
|
-
"version": "1.2.6-test.
|
|
3
|
+
"version": "1.2.6-test.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Shogun Network Intent-based cross-chain swaps SDK",
|
|
6
6
|
"author": "Shogun network",
|
|
@@ -48,7 +48,8 @@
|
|
|
48
48
|
"@types/node": "22.13.10",
|
|
49
49
|
"codama": "1.3.4",
|
|
50
50
|
"tsup": "^8.5.0",
|
|
51
|
-
"typescript": "5.8.2"
|
|
51
|
+
"typescript": "5.8.2",
|
|
52
|
+
"vitest": "^2.1.9"
|
|
52
53
|
},
|
|
53
54
|
"dependencies": {
|
|
54
55
|
"@codama/visitors-core": "^1.3.7",
|
|
@@ -89,6 +90,8 @@
|
|
|
89
90
|
"lint": "npx eslint --fix --ext .ts ./src",
|
|
90
91
|
"format": "prettier --write ./src",
|
|
91
92
|
"pre-publish": "pnpm node ./scripts/prePublish.js",
|
|
92
|
-
"generate-solana-idl": "pnpm node ./scripts/codama.js"
|
|
93
|
+
"generate-solana-idl": "pnpm node ./scripts/codama.js",
|
|
94
|
+
"test": "vitest run",
|
|
95
|
+
"test:watch": "vitest"
|
|
93
96
|
}
|
|
94
97
|
}
|
package/src/auth/siwe.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ChainID } from '../chains.js';
|
|
2
|
-
import { AUCTIONEER_URL
|
|
2
|
+
import { AUCTIONEER_URL } from '../constants.js';
|
|
3
3
|
import type { ApiResponse } from '../types/api.js';
|
|
4
4
|
|
|
5
5
|
export type FetchSiweMessageParams = {
|
|
@@ -14,13 +14,7 @@ export type FetchJWTParams = {
|
|
|
14
14
|
|
|
15
15
|
export async function fetchSiweMessage(params: FetchSiweMessageParams) {
|
|
16
16
|
const url = `${AUCTIONEER_URL}/siwe?wallet=${params.wallet}&chainId=${params.chainId}`;
|
|
17
|
-
const response = await fetch(url
|
|
18
|
-
headers: {
|
|
19
|
-
...(DEV_ACCESS_KEY
|
|
20
|
-
? { "x-dev-access": DEV_ACCESS_KEY }
|
|
21
|
-
: {})
|
|
22
|
-
}
|
|
23
|
-
});
|
|
17
|
+
const response = await fetch(url);
|
|
24
18
|
if (!response.ok) {
|
|
25
19
|
throw new Error(`Failed to fetch SIWE message: ${response.status} ${response.statusText}`);
|
|
26
20
|
}
|
|
@@ -30,12 +24,17 @@ export async function fetchSiweMessage(params: FetchSiweMessageParams) {
|
|
|
30
24
|
|
|
31
25
|
export async function fetchJWTToken(params: FetchJWTParams, token?: string) {
|
|
32
26
|
const url = `${AUCTIONEER_URL}/siwe`;
|
|
27
|
+
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
|
28
|
+
// Only forward an existing token when present; the server merges the newly verified
|
|
29
|
+
// wallet into it. Sending `Bearer undefined` would be a no-op the server discards.
|
|
30
|
+
if (token) {
|
|
31
|
+
headers.Authorization = `Bearer ${token}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
33
34
|
const response = await fetch(url, {
|
|
34
35
|
method: 'POST',
|
|
35
36
|
body: JSON.stringify(params),
|
|
36
|
-
headers
|
|
37
|
-
Authorization: `Bearer ${token}`,
|
|
38
|
-
},
|
|
37
|
+
headers,
|
|
39
38
|
});
|
|
40
39
|
|
|
41
40
|
if (!response.ok) {
|
package/src/constants.ts
CHANGED
|
@@ -79,7 +79,9 @@ export const PROD_DCA_SINGLE_CHAIN_GUARD_ADDRESSES: Record<SupportedEvmChain, Ev
|
|
|
79
79
|
[ChainID.Arbitrum]: '0x0000000000000000000000000000000000000000' as EvmAddress,
|
|
80
80
|
[ChainID.Optimism]: '0x1b754840f058b78e412156d2674b5954728cb86e' as EvmAddress,
|
|
81
81
|
[ChainID.Base]: '0x2df3ab1bb8b9d48769718003de2d8ca8e9d3c465' as EvmAddress,
|
|
82
|
-
|
|
82
|
+
// HyperEVM SingleChainGuardDca per the on-chain deployment + auctioneer config.
|
|
83
|
+
// (0x618ee9… is the SingleChainGuardLimit, which previously sat here by mistake.)
|
|
84
|
+
[ChainID.Hyperliquid]: '0xdcbb658910b5192a43b79fd8244bdf27940d3b6b' as EvmAddress,
|
|
83
85
|
[ChainID.BSC]: '0x11ef880e2e8d17c114b9eb87369ce3d1311c62e1' as EvmAddress,
|
|
84
86
|
[ChainID.MONAD]: '0x418302C3bef14D405A9B30381a9cb9d4d82EB2d4' as EvmAddress,
|
|
85
87
|
};
|
|
@@ -116,6 +118,10 @@ export const DCA_SINGLE_CHAIN_GUARD_ADDRESSES = useProdConfig
|
|
|
116
118
|
? PROD_DCA_SINGLE_CHAIN_GUARD_ADDRESSES
|
|
117
119
|
: TEST_DCA_SINGLE_CHAIN_GUARD_ADDRESSES;
|
|
118
120
|
|
|
121
|
+
// No separate test deployment is configured for cross-chain DCA guards yet; prod values
|
|
122
|
+
// match the auctioneer's `cross_chain.guard_dca` config and are used in both modes.
|
|
123
|
+
export const DCA_CROSS_CHAIN_GUARD_ADDRESSES = PROD_DCA_CROSS_CHAIN_GUARD_ADDRESSES;
|
|
124
|
+
|
|
119
125
|
export const NATIVE_SOLANA_TOKEN_ADDRESS =
|
|
120
126
|
'So11111111111111111111111111111111111111111' as SolanaAddress<'So11111111111111111111111111111111111111111'>;
|
|
121
127
|
export const WRAPPED_SOL_MINT_ADDRESS =
|
|
@@ -145,9 +151,15 @@ export const SUI_GUARD_COLLATERAL_TYPE =
|
|
|
145
151
|
export const SUI_GUARD_STABLECOIN_TYPE =
|
|
146
152
|
'0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC';
|
|
147
153
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
154
|
+
// `INTENTS_AUCTIONEER_URL` overrides the default endpoint (e.g. to target a locally
|
|
155
|
+
// running auctioneer) without touching the committed `useProdConfig` flag. Guarded so it
|
|
156
|
+
// is a no-op in browser bundles where `process` is undefined.
|
|
157
|
+
const auctioneerUrlOverride =
|
|
158
|
+
typeof process !== 'undefined' ? process.env?.INTENTS_AUCTIONEER_URL : undefined;
|
|
159
|
+
|
|
160
|
+
export const AUCTIONEER_URL =
|
|
161
|
+
auctioneerUrlOverride ||
|
|
162
|
+
(useProdConfig ? 'https://auctioneer-825534211396.us-central1.run.app' : 'http://127.0.0.1:8080');
|
|
151
163
|
|
|
152
164
|
export const MAX_UINT_32 = 2n ** 32n - 1n;
|
|
153
165
|
export const MAX_UINT_64 = 2n ** 64n - 1n;
|
|
@@ -176,8 +188,20 @@ export function isNativeEvmToken(tokenAddress: string): boolean {
|
|
|
176
188
|
return NATIVE_EVM_ETH_ADDRESSES.some((addr) => addr.toLowerCase() === normalizedAddress);
|
|
177
189
|
}
|
|
178
190
|
|
|
179
|
-
export const
|
|
191
|
+
export const EVM_ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' as EvmAddress;
|
|
180
192
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
193
|
+
/** Placeholder signature (32 zero bytes) for unsigned/validate-only intent requests. */
|
|
194
|
+
export const EVM_ZERO_SIGNATURE = '0x0000000000000000000000000000000000000000000000000000000000000000';
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Output-token addresses must be normalized to the zero address before signing the EIP-712
|
|
198
|
+
* witness: the auctioneer collapses any native sentinel (`0xeee…`/`0x0…`) to `Address::default()`
|
|
199
|
+
* when it reconstructs `requestedOutput`/`extraTransfers` to recover the signer. Signing the raw
|
|
200
|
+
* sentinel instead would yield a different hash → signature recovery fails. (`tokenIn` is NOT
|
|
201
|
+
* normalized on either side, so leave it untouched.)
|
|
202
|
+
*/
|
|
203
|
+
export function normalizeNativeEvmTokenForSigning(tokenAddress: string): string {
|
|
204
|
+
return isNativeEvmToken(tokenAddress) ? EVM_ZERO_ADDRESS : tokenAddress;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export const TOKEN_SEARCH_API_BASE_URL = 'https://shogun-token-search-api-825534211396.europe-west4.run.app';
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import type { Address, Hex, TypedDataDefinition } from 'viem';
|
|
1
2
|
import type { SupportedEvmChain } from '../../chains.js';
|
|
2
3
|
import type {
|
|
3
4
|
SingleChainUserIntentRequest,
|
|
4
5
|
CrossChainUserIntentRequest,
|
|
5
6
|
DcaSingleChainUserIntentRequest,
|
|
7
|
+
DcaCrossChainUserIntentRequest,
|
|
6
8
|
} from '../../types/intent.js';
|
|
7
9
|
import type { SingleChainOrder } from '../orders/single-chain.js';
|
|
8
10
|
import type { CrossChainOrder } from '../orders/cross-chain.js';
|
|
@@ -13,6 +15,13 @@ import {
|
|
|
13
15
|
getEVMSingleChainOrderTypedData,
|
|
14
16
|
getEVMDcaSingleChainOrderTypedData,
|
|
15
17
|
} from './order-signature.js';
|
|
18
|
+
import {
|
|
19
|
+
CROSS_CHAIN_DCA_PERMIT2_TYPES,
|
|
20
|
+
PERMIT2_DOMAIN,
|
|
21
|
+
type CrossChainDcaPermitTransferFrom,
|
|
22
|
+
} from './permit2.js';
|
|
23
|
+
import { EVMIntentProvider } from './intent-provider.js';
|
|
24
|
+
import { DCA_CROSS_CHAIN_GUARD_ADDRESSES, EVM_ZERO_SIGNATURE } from '../../constants.js';
|
|
16
25
|
import { generateExecutionDetailsHash } from '../../utils/generate-execution-details-hash.js';
|
|
17
26
|
import { Parsers } from '../../utils/parsers.js';
|
|
18
27
|
|
|
@@ -97,7 +106,7 @@ export function createEvmSingleChainLimitOrderIntentRequest(
|
|
|
97
106
|
chainSpecificData: {
|
|
98
107
|
EVM: {
|
|
99
108
|
nonce: params.nonce || String(Math.floor(Math.random() * 10000000)),
|
|
100
|
-
signature: params.signature ||
|
|
109
|
+
signature: params.signature || EVM_ZERO_SIGNATURE,
|
|
101
110
|
},
|
|
102
111
|
},
|
|
103
112
|
};
|
|
@@ -131,7 +140,7 @@ export function createEvmSingleChainDcaOrderIntentRequest(
|
|
|
131
140
|
chainSpecificData: {
|
|
132
141
|
EVM: {
|
|
133
142
|
nonce: params.nonce || String(Math.floor(Math.random() * 10000000)),
|
|
134
|
-
signature: params.signature ||
|
|
143
|
+
signature: params.signature || EVM_ZERO_SIGNATURE,
|
|
135
144
|
},
|
|
136
145
|
},
|
|
137
146
|
};
|
|
@@ -157,8 +166,8 @@ export function createEvmCrossChainOrderIntentRequest(
|
|
|
157
166
|
};
|
|
158
167
|
const executionDetailsString = JSON.stringify(executionDetails, Parsers.bigIntReplacer);
|
|
159
168
|
|
|
160
|
-
//
|
|
161
|
-
const executionDetailsHash = generateExecutionDetailsHash(
|
|
169
|
+
// Hash the exact string we send; the auctioneer recomputes sha256 over it and rejects on mismatch
|
|
170
|
+
const executionDetailsHash = generateExecutionDetailsHash(executionDetailsString);
|
|
162
171
|
|
|
163
172
|
const genericData = {
|
|
164
173
|
user: params.user,
|
|
@@ -169,9 +178,6 @@ export function createEvmCrossChainOrderIntentRequest(
|
|
|
169
178
|
deadline: params.deadline,
|
|
170
179
|
executionDetailsHash: executionDetailsHash as `0x${string}`,
|
|
171
180
|
extraTransfers: params.extraTransfers,
|
|
172
|
-
takeProfitMinOut: params.takeProfitMinOut,
|
|
173
|
-
stopLossType: params.stopLossType,
|
|
174
|
-
stopLossTriggerPrice: params.stopLossTriggerPrice,
|
|
175
181
|
};
|
|
176
182
|
|
|
177
183
|
return {
|
|
@@ -180,7 +186,7 @@ export function createEvmCrossChainOrderIntentRequest(
|
|
|
180
186
|
chainSpecificData: {
|
|
181
187
|
EVM: {
|
|
182
188
|
nonce: params.nonce || String(Math.floor(Math.random() * 10000000)),
|
|
183
|
-
signature: params.signature ||
|
|
189
|
+
signature: params.signature || EVM_ZERO_SIGNATURE,
|
|
184
190
|
},
|
|
185
191
|
},
|
|
186
192
|
};
|
|
@@ -220,3 +226,120 @@ export async function generateEvmCrossChainOrderTypedData(order: CrossChainOrder
|
|
|
220
226
|
export function generateEvmRandomNonce(): string {
|
|
221
227
|
return String(Math.floor(Math.random() * 10000000));
|
|
222
228
|
}
|
|
229
|
+
|
|
230
|
+
export type CreateEvmCrossChainDcaOrderIntentParams = {
|
|
231
|
+
user: `0x${string}`;
|
|
232
|
+
sourceChainId: SupportedEvmChain;
|
|
233
|
+
sourceTokenAddress: string;
|
|
234
|
+
destinationChainId: number;
|
|
235
|
+
destinationTokenAddress: string;
|
|
236
|
+
destinationTokenMinAmount?: bigint;
|
|
237
|
+
destinationAddress: string;
|
|
238
|
+
minStablecoinAmount?: bigint;
|
|
239
|
+
deadline: number;
|
|
240
|
+
startTime: number;
|
|
241
|
+
amountInPerInterval: bigint;
|
|
242
|
+
totalIntervals: number;
|
|
243
|
+
intervalDuration: number;
|
|
244
|
+
extraTransfers?: ExtraTransfer[];
|
|
245
|
+
nonce?: string;
|
|
246
|
+
signature?: string;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Builds the cross-chain DCA execution-details string and its SHA-256 hash.
|
|
251
|
+
* Both the wire payload and the EIP-712 witness must reference the SAME string/hash, so this
|
|
252
|
+
* is the single source of truth — the auctioneer recomputes sha256 over the string it receives.
|
|
253
|
+
*/
|
|
254
|
+
function buildCrossChainDcaExecutionDetails(params: CreateEvmCrossChainDcaOrderIntentParams): {
|
|
255
|
+
executionDetailsString: string;
|
|
256
|
+
executionDetailsHash: string;
|
|
257
|
+
} {
|
|
258
|
+
const executionDetails = {
|
|
259
|
+
destChainId: params.destinationChainId,
|
|
260
|
+
tokenOut: params.destinationTokenAddress,
|
|
261
|
+
amountOutMin: params.destinationTokenMinAmount?.toString() || '1',
|
|
262
|
+
destinationAddress: params.destinationAddress,
|
|
263
|
+
extraTransfers: params.extraTransfers,
|
|
264
|
+
};
|
|
265
|
+
const executionDetailsString = JSON.stringify(executionDetails, Parsers.bigIntReplacer);
|
|
266
|
+
return { executionDetailsString, executionDetailsHash: generateExecutionDetailsHash(executionDetailsString) };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Creates an EVM cross-chain DCA order intent request, ready to POST to
|
|
271
|
+
* `/user_intent/cross_chain/dca_order`. Pass `nonce`/`signature` from
|
|
272
|
+
* {@link getEVMCrossChainDcaOrderTypedData} for an executable order.
|
|
273
|
+
*/
|
|
274
|
+
export function createEvmCrossChainDcaOrderIntentRequest(
|
|
275
|
+
params: CreateEvmCrossChainDcaOrderIntentParams,
|
|
276
|
+
): DcaCrossChainUserIntentRequest {
|
|
277
|
+
const { executionDetailsString, executionDetailsHash } = buildCrossChainDcaExecutionDetails(params);
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
genericData: {
|
|
281
|
+
user: params.user,
|
|
282
|
+
srcChainId: params.sourceChainId,
|
|
283
|
+
tokenIn: params.sourceTokenAddress,
|
|
284
|
+
minStablecoinsAmount: params.minStablecoinAmount || 1n,
|
|
285
|
+
deadline: params.deadline,
|
|
286
|
+
executionDetailsHash: executionDetailsHash as `0x${string}`,
|
|
287
|
+
startTime: params.startTime,
|
|
288
|
+
amountInPerInterval: params.amountInPerInterval,
|
|
289
|
+
totalIntervals: params.totalIntervals,
|
|
290
|
+
intervalDuration: params.intervalDuration,
|
|
291
|
+
},
|
|
292
|
+
executionDetails: executionDetailsString,
|
|
293
|
+
chainSpecificData: {
|
|
294
|
+
EVM: {
|
|
295
|
+
nonce: params.nonce || generateEvmRandomNonce(),
|
|
296
|
+
signature: params.signature || EVM_ZERO_SIGNATURE,
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Generates the EIP-712 typed data (and nonce) for a cross-chain DCA order's Permit2 witness.
|
|
304
|
+
* Sign `orderTypedData`, then pass the resulting `nonce`/`signature` into
|
|
305
|
+
* {@link createEvmCrossChainDcaOrderIntentRequest} — both share the same execution-details hash.
|
|
306
|
+
*/
|
|
307
|
+
export function getEVMCrossChainDcaOrderTypedData(
|
|
308
|
+
params: CreateEvmCrossChainDcaOrderIntentParams,
|
|
309
|
+
providedNonce?: bigint,
|
|
310
|
+
): { orderTypedData: TypedDataDefinition; nonce: bigint } {
|
|
311
|
+
const { executionDetailsHash } = buildCrossChainDcaExecutionDetails(params);
|
|
312
|
+
const nonce = providedNonce ?? EVMIntentProvider.getRandomNonce();
|
|
313
|
+
const totalAmountIn = params.amountInPerInterval * BigInt(params.totalIntervals);
|
|
314
|
+
const spender = DCA_CROSS_CHAIN_GUARD_ADDRESSES[params.sourceChainId] as Address;
|
|
315
|
+
|
|
316
|
+
const message: CrossChainDcaPermitTransferFrom = {
|
|
317
|
+
permitted: { token: params.sourceTokenAddress as Address, amount: totalAmountIn },
|
|
318
|
+
spender,
|
|
319
|
+
nonce,
|
|
320
|
+
deadline: BigInt(params.deadline),
|
|
321
|
+
witness: {
|
|
322
|
+
user: params.user as Address,
|
|
323
|
+
tokenIn: params.sourceTokenAddress as Address,
|
|
324
|
+
srcChainId: params.sourceChainId,
|
|
325
|
+
startTime: params.startTime,
|
|
326
|
+
deadline: params.deadline,
|
|
327
|
+
totalIntervals: params.totalIntervals,
|
|
328
|
+
intervalDuration: params.intervalDuration,
|
|
329
|
+
amountInPerInterval: params.amountInPerInterval,
|
|
330
|
+
minStablecoinsAmountPerInterval: params.minStablecoinAmount || 1n,
|
|
331
|
+
executionDetailsHash: executionDetailsHash as Hex,
|
|
332
|
+
nonce,
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
return {
|
|
337
|
+
orderTypedData: {
|
|
338
|
+
domain: PERMIT2_DOMAIN(params.sourceChainId),
|
|
339
|
+
types: CROSS_CHAIN_DCA_PERMIT2_TYPES,
|
|
340
|
+
primaryType: 'PermitWitnessTransferFrom',
|
|
341
|
+
message,
|
|
342
|
+
},
|
|
343
|
+
nonce,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
CROSS_CHAIN_GUARD_ADDRESSES,
|
|
7
7
|
MAX_UINT_32,
|
|
8
8
|
DCA_SINGLE_CHAIN_GUARD_ADDRESSES,
|
|
9
|
+
normalizeNativeEvmTokenForSigning,
|
|
9
10
|
} from '../../constants.js';
|
|
10
11
|
import {
|
|
11
12
|
getEVMCrossChainOrderTypedData,
|
|
@@ -103,14 +104,14 @@ export class EVMIntentProvider {
|
|
|
103
104
|
const spender = SINGLE_CHAIN_GUARD_ADDRESSES[order.chainId as SupportedEvmChain] as Address;
|
|
104
105
|
|
|
105
106
|
const requestedOutput: TransferData = {
|
|
106
|
-
token: order.tokenOut as Address,
|
|
107
|
+
token: normalizeNativeEvmTokenForSigning(order.tokenOut) as Address,
|
|
107
108
|
receiver: order.destinationAddress as Address,
|
|
108
109
|
amount: order.amountOutMin,
|
|
109
110
|
};
|
|
110
111
|
|
|
111
112
|
const extraTransfers: TransferData[] =
|
|
112
113
|
order.extraTransfers?.map((transfer) => ({
|
|
113
|
-
token: transfer.token as Address,
|
|
114
|
+
token: normalizeNativeEvmTokenForSigning(transfer.token) as Address,
|
|
114
115
|
receiver: transfer.receiver as Address,
|
|
115
116
|
amount: transfer.amount,
|
|
116
117
|
})) || [];
|
|
@@ -166,14 +167,14 @@ export class EVMIntentProvider {
|
|
|
166
167
|
const spender = DCA_SINGLE_CHAIN_GUARD_ADDRESSES[order.chainId as SupportedEvmChain] as Address;
|
|
167
168
|
|
|
168
169
|
const requestedOutput: TransferData = {
|
|
169
|
-
token: order.tokenOut as Address,
|
|
170
|
+
token: normalizeNativeEvmTokenForSigning(order.tokenOut) as Address,
|
|
170
171
|
receiver: order.destinationAddress as Address,
|
|
171
172
|
amount: BigInt(order.amountOutMin),
|
|
172
173
|
};
|
|
173
174
|
|
|
174
175
|
const extraTransfers: TransferData[] =
|
|
175
176
|
order.extraTransfers?.map((transfer) => ({
|
|
176
|
-
token: transfer.token as Address,
|
|
177
|
+
token: normalizeNativeEvmTokenForSigning(transfer.token) as Address,
|
|
177
178
|
receiver: transfer.receiver as Address,
|
|
178
179
|
amount: transfer.amount,
|
|
179
180
|
})) || [];
|
package/src/core/evm/permit2.ts
CHANGED
|
@@ -174,3 +174,39 @@ export const DCA_SINGLE_CHAIN_PERMIT2_TYPES = {
|
|
|
174
174
|
{ name: 'witness', type: 'SingleChainDcaOrder' },
|
|
175
175
|
],
|
|
176
176
|
} as const;
|
|
177
|
+
|
|
178
|
+
export type CrossChainDcaPermitTransferFrom = {
|
|
179
|
+
permitted: TokenPermissions;
|
|
180
|
+
spender: Address;
|
|
181
|
+
nonce: bigint;
|
|
182
|
+
deadline: bigint;
|
|
183
|
+
witness: DcaOrderInfo;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// Cross-chain DCA witness — mirrors the auctioneer's
|
|
187
|
+
// `DcaOrderInfo(address user,address tokenIn,uint32 srcChainId,uint32 startTime,uint32 deadline,
|
|
188
|
+
// uint24 totalIntervals,uint32 intervalDuration,uint128 amountInPerInterval,
|
|
189
|
+
// uint128 minStablecoinsAmountPerInterval,bytes32 executionDetailsHash,uint256 nonce)`.
|
|
190
|
+
export const CROSS_CHAIN_DCA_PERMIT2_TYPES = {
|
|
191
|
+
TokenPermissions: TOKEN_PERMISSIONS_TYPE,
|
|
192
|
+
DcaOrderInfo: [
|
|
193
|
+
{ name: 'user', type: 'address' },
|
|
194
|
+
{ name: 'tokenIn', type: 'address' },
|
|
195
|
+
{ name: 'srcChainId', type: 'uint32' },
|
|
196
|
+
{ name: 'startTime', type: 'uint32' },
|
|
197
|
+
{ name: 'deadline', type: 'uint32' },
|
|
198
|
+
{ name: 'totalIntervals', type: 'uint24' },
|
|
199
|
+
{ name: 'intervalDuration', type: 'uint32' },
|
|
200
|
+
{ name: 'amountInPerInterval', type: 'uint128' },
|
|
201
|
+
{ name: 'minStablecoinsAmountPerInterval', type: 'uint128' },
|
|
202
|
+
{ name: 'executionDetailsHash', type: 'bytes32' },
|
|
203
|
+
{ name: 'nonce', type: 'uint256' },
|
|
204
|
+
],
|
|
205
|
+
PermitWitnessTransferFrom: [
|
|
206
|
+
{ name: 'permitted', type: 'TokenPermissions' },
|
|
207
|
+
{ name: 'spender', type: 'address' },
|
|
208
|
+
{ name: 'nonce', type: 'uint256' },
|
|
209
|
+
{ name: 'deadline', type: 'uint256' },
|
|
210
|
+
{ name: 'witness', type: 'DcaOrderInfo' },
|
|
211
|
+
],
|
|
212
|
+
} as const;
|
|
@@ -1,45 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
1
|
+
import { AuctioneerAPI } from './index.js';
|
|
2
|
+
import type { ApiUserOrders } from '../../../types/api.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Fetches
|
|
6
|
-
*
|
|
5
|
+
* Fetches the orders owned by the wallets encoded in a SIWE-issued JWT.
|
|
6
|
+
*
|
|
7
|
+
* The auctioneer derives the queried wallets from the verified JWT claims, never from
|
|
8
|
+
* caller-supplied params — a caller can only read intents for wallets it proved control
|
|
9
|
+
* of via SIWE. Obtain the token with `EVMSDK.authenticate()` (or the `fetchSiweMessage` +
|
|
10
|
+
* `fetchJWTToken` pair) and pass it here.
|
|
7
11
|
*/
|
|
8
|
-
export async function fetchUserOrders(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
suiAddress?: string | null
|
|
12
|
-
): Promise<ApiUserOrders> {
|
|
13
|
-
const params = new URLSearchParams();
|
|
14
|
-
|
|
15
|
-
if (evmAddress) params.append('evmWallets', evmAddress);
|
|
16
|
-
if (solAddress) params.append('solanaWallets', solAddress);
|
|
17
|
-
if (suiAddress) params.append('suiWallets', suiAddress);
|
|
18
|
-
|
|
19
|
-
if ([evmAddress, solAddress, suiAddress].every((a) => !a)) {
|
|
20
|
-
throw new Error('At least one wallet address is required');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const url = `${AUCTIONEER_URL}/user_intent?${params.toString()}`;
|
|
24
|
-
|
|
25
|
-
const response = await fetch(url, {
|
|
26
|
-
method: 'GET',
|
|
27
|
-
headers: {
|
|
28
|
-
'Content-Type': 'application/json', ...(DEV_ACCESS_KEY
|
|
29
|
-
? { "x-dev-access": DEV_ACCESS_KEY }
|
|
30
|
-
: {})
|
|
31
|
-
},
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
if (!response.ok) {
|
|
35
|
-
throw new Error(`Failed to fetch user orders: ${response.status} ${response.statusText}`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const data: ApiResponse<ApiUserOrders> = await response.json();
|
|
39
|
-
|
|
40
|
-
if (!data.success) {
|
|
41
|
-
throw new Error(`Failed to fetch user orders: ${data.error}`);
|
|
12
|
+
export async function fetchUserOrders(aggregatedToken: string): Promise<ApiUserOrders> {
|
|
13
|
+
if (!aggregatedToken) {
|
|
14
|
+
throw new Error('A SIWE-issued JWT is required to fetch user orders');
|
|
42
15
|
}
|
|
43
16
|
|
|
44
|
-
return
|
|
17
|
+
return new AuctioneerAPI(aggregatedToken).getUserOrders();
|
|
45
18
|
}
|
|
@@ -200,6 +200,9 @@ export class CrossChainOrder {
|
|
|
200
200
|
destinationAddress: this.destinationAddress,
|
|
201
201
|
amountOutMin: this.destinationTokenMinAmount,
|
|
202
202
|
extraTransfers: this.extraTransfers,
|
|
203
|
+
stopLossType: this.stopLossType,
|
|
204
|
+
stopLossTriggerPrice: this.stopLossTriggerPrice,
|
|
205
|
+
takeProfitMinOut: this.takeProfitMinOut,
|
|
203
206
|
};
|
|
204
207
|
}
|
|
205
208
|
|
|
@@ -4,6 +4,9 @@ import type { ApiResponse } from '../../types/api.js';
|
|
|
4
4
|
import type { SingleChainPreparedData, SingleChainUserIntentRequest } from '../../types/intent.js';
|
|
5
5
|
import { SingleChainOrderValidator } from '../../utils/order-validator.js';
|
|
6
6
|
import { QuoteProvider } from '../../utils/quote/aggregator.js';
|
|
7
|
+
import { estimateAmountOut } from '../../utils/quote/pricing/estimate-amount-out.js';
|
|
8
|
+
import { estimateExpensesInTokenOut } from '../../utils/quote/pricing/expenses.js';
|
|
9
|
+
import { DEFAULT_COMMISSION_BPS, EVM_SINGLE_CHAIN_GAS_LIMIT, SINGLE_CHAIN_COMMISSION_DENOMINATOR } from '../../utils/quote/pricing/constants.js';
|
|
7
10
|
import { getEVMSingleChainOrderTypedData } from '../evm/order-signature.js';
|
|
8
11
|
import { BaseSDK } from '../sdk.js';
|
|
9
12
|
import { getSolanaSingleChainOrderInstructions } from '../solana/order-instructions.js';
|
|
@@ -116,14 +119,24 @@ export class SingleChainOrder {
|
|
|
116
119
|
|
|
117
120
|
switch (scenario) {
|
|
118
121
|
case 'QUOTE_REQUIRED': {
|
|
119
|
-
const quote = await
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
122
|
+
const [quote, expenses] = await Promise.all([
|
|
123
|
+
QuoteProvider.getSingleChainQuote({
|
|
124
|
+
tokenIn: input.tokenIn,
|
|
125
|
+
amount: input.amountIn,
|
|
126
|
+
chainId: input.chainId,
|
|
127
|
+
tokenOut: input.tokenOut,
|
|
128
|
+
}),
|
|
129
|
+
estimateExpensesInTokenOut({
|
|
130
|
+
chainId: input.chainId,
|
|
131
|
+
tokenOut: input.tokenOut,
|
|
132
|
+
gasLimit: EVM_SINGLE_CHAIN_GAS_LIMIT,
|
|
133
|
+
getQuote: (p) => QuoteProvider.getSingleChainQuote(p),
|
|
134
|
+
}),
|
|
135
|
+
]);
|
|
136
|
+
|
|
137
|
+
// Worst-case output already carries provider slippage; never use the nominal amountOut.
|
|
138
|
+
const swapOutput = quote.amountOutMin ?? quote.amountOut;
|
|
139
|
+
return estimateAmountOut(swapOutput, DEFAULT_COMMISSION_BPS, expenses, SINGLE_CHAIN_COMMISSION_DENOMINATOR);
|
|
127
140
|
}
|
|
128
141
|
|
|
129
142
|
case 'USE_PROVIDED_AMOUNT':
|
package/src/core/sdk.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { AUCTIONEER_URL
|
|
1
|
+
import { AUCTIONEER_URL } from '../constants.js';
|
|
2
2
|
import { NetworkError } from '../errors/index.js';
|
|
3
3
|
import type { ApiResponse } from '../types/api.js';
|
|
4
4
|
import type {
|
|
5
5
|
CrossChainOrderPrepared,
|
|
6
6
|
CrossChainUserIntentRequest,
|
|
7
|
+
DcaCrossChainUserIntentRequest,
|
|
7
8
|
DcaSingleChainOrderPrepared,
|
|
8
9
|
DcaSingleChainUserIntentRequest,
|
|
9
10
|
SingleChainOrderPrepared,
|
|
@@ -28,11 +29,7 @@ export abstract class BaseSDK {
|
|
|
28
29
|
private static async makeRequest(url: string, body: string): Promise<ApiResponse> {
|
|
29
30
|
const response = await fetch(url, {
|
|
30
31
|
method: 'POST',
|
|
31
|
-
headers: {
|
|
32
|
-
'Content-Type': 'application/json', ...(DEV_ACCESS_KEY
|
|
33
|
-
? { "x-dev-access": DEV_ACCESS_KEY }
|
|
34
|
-
: {}),
|
|
35
|
-
},
|
|
32
|
+
headers: { 'Content-Type': 'application/json' },
|
|
36
33
|
body,
|
|
37
34
|
});
|
|
38
35
|
|
|
@@ -126,4 +123,13 @@ export abstract class BaseSDK {
|
|
|
126
123
|
public static async validateDcaSingleChainOrder(intentRequest: DcaSingleChainUserIntentRequest): Promise<void> {
|
|
127
124
|
return BaseSDK.validateOrder(intentRequest, '/validate_intent/single_chain/dca_order');
|
|
128
125
|
}
|
|
126
|
+
|
|
127
|
+
public static async validateDcaCrossChainOrder(intentRequest: DcaCrossChainUserIntentRequest): Promise<void> {
|
|
128
|
+
return BaseSDK.validateOrder(intentRequest, '/validate_intent/cross_chain/dca_order');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public static async sendDcaCrossChainOrder(intentRequest: DcaCrossChainUserIntentRequest): Promise<ApiResponse> {
|
|
132
|
+
const body = JSON.stringify(intentRequest, Parsers.bigIntReplacer);
|
|
133
|
+
return BaseSDK.makeRequest(`${AUCTIONEER_URL}/user_intent/cross_chain/dca_order`, body);
|
|
134
|
+
}
|
|
129
135
|
}
|
|
@@ -60,7 +60,8 @@ export type CreateSolanaCrossChainOrderIntentParams = {
|
|
|
60
60
|
deadline: number;
|
|
61
61
|
extraTransfers?: ExtraTransfer[];
|
|
62
62
|
orderPubkey?: string;
|
|
63
|
-
|
|
63
|
+
stopLossType?: StopLossType;
|
|
64
|
+
stopLossTriggerPrice?: string;
|
|
64
65
|
takeProfitMinOut?: bigint;
|
|
65
66
|
};
|
|
66
67
|
|
|
@@ -150,13 +151,14 @@ export function createSolanaCrossChainOrderIntentRequest(
|
|
|
150
151
|
amountOutMin: params.destinationTokenMinAmount?.toString() || '1',
|
|
151
152
|
destinationAddress: params.destinationAddress,
|
|
152
153
|
extraTransfers: params.extraTransfers,
|
|
153
|
-
|
|
154
|
+
stopLossType: params.stopLossType,
|
|
155
|
+
stopLossTriggerPrice: params.stopLossTriggerPrice,
|
|
154
156
|
takeProfitMinOut: params.takeProfitMinOut?.toString(),
|
|
155
157
|
};
|
|
156
158
|
const executionDetailsString = JSON.stringify(executionDetails, Parsers.bigIntReplacer);
|
|
157
159
|
|
|
158
|
-
//
|
|
159
|
-
const executionDetailsHash = generateExecutionDetailsHash(
|
|
160
|
+
// Hash the exact string we send; the auctioneer recomputes sha256 over it and rejects on mismatch
|
|
161
|
+
const executionDetailsHash = generateExecutionDetailsHash(executionDetailsString);
|
|
160
162
|
|
|
161
163
|
const genericData = {
|
|
162
164
|
user: params.user,
|
|
@@ -84,6 +84,8 @@ export function createSuiSingleChainLimitOrderIntentRequest(
|
|
|
84
84
|
deadline: params.deadline,
|
|
85
85
|
|
|
86
86
|
takeProfitMinOut: params.takeProfitMinOut,
|
|
87
|
+
stopLossType: params.stopLossType,
|
|
88
|
+
stopLossTriggerPrice: params.stopLossTriggerPrice,
|
|
87
89
|
} as SingleChainOrder;
|
|
88
90
|
|
|
89
91
|
return {
|
|
@@ -153,7 +155,8 @@ export function createSuiCrossChainOrderIntentRequest(
|
|
|
153
155
|
};
|
|
154
156
|
const executionDetailsString = JSON.stringify(executionDetails, Parsers.bigIntReplacer);
|
|
155
157
|
|
|
156
|
-
|
|
158
|
+
// Hash the exact string we send; the auctioneer recomputes sha256 over it and rejects on mismatch
|
|
159
|
+
const executionDetailsHash = generateExecutionDetailsHash(executionDetailsString);
|
|
157
160
|
|
|
158
161
|
const genericData = {
|
|
159
162
|
user: params.user,
|
package/src/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ export { CrossChainOrder } from './core/orders/cross-chain.js';
|
|
|
11
11
|
export type { CreateCrossChainOrderParams } from './core/orders/cross-chain.js';
|
|
12
12
|
export { SingleChainOrder } from './core/orders/single-chain.js';
|
|
13
13
|
export type { CreateSingleChainOrderParams } from './core/orders/single-chain.js';
|
|
14
|
+
export type { StopLossType } from './core/orders/common.js';
|
|
14
15
|
|
|
15
16
|
export { fetchUserOrders } from './core/orders/api/fetch.js';
|
|
16
17
|
export { AuctioneerAPI } from './core/orders/api/index.js';
|
|
@@ -92,13 +93,16 @@ export {
|
|
|
92
93
|
createEvmSingleChainLimitOrderIntentRequest,
|
|
93
94
|
createEvmSingleChainDcaOrderIntentRequest,
|
|
94
95
|
createEvmCrossChainOrderIntentRequest,
|
|
96
|
+
createEvmCrossChainDcaOrderIntentRequest,
|
|
95
97
|
generateEvmSingleChainLimitOrderTypedData,
|
|
96
98
|
generateEvmSingleChainDcaOrderTypedData,
|
|
97
99
|
generateEvmCrossChainOrderTypedData,
|
|
100
|
+
getEVMCrossChainDcaOrderTypedData,
|
|
98
101
|
generateEvmRandomNonce,
|
|
99
102
|
type CreateEvmSingleChainLimitOrderIntentParams,
|
|
100
103
|
type CreateEvmSingleChainDcaOrderIntentParams,
|
|
101
104
|
type CreateEvmCrossChainOrderIntentParams,
|
|
105
|
+
type CreateEvmCrossChainDcaOrderIntentParams,
|
|
102
106
|
} from './core/evm/intent-helpers.js';
|
|
103
107
|
|
|
104
108
|
// Solana Intent Helpers
|
package/src/types/api.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { ExtraTransfer } from '../core/orders/common.js';
|
|
2
2
|
|
|
3
|
-
export type ChainOrderStatus =
|
|
3
|
+
export type ChainOrderStatus =
|
|
4
|
+
| 'Auction'
|
|
5
|
+
| 'NoBids'
|
|
6
|
+
| 'Executing'
|
|
7
|
+
| 'Fulfilled'
|
|
8
|
+
| 'Cancelled'
|
|
9
|
+
| 'Outdated'
|
|
10
|
+
| 'DcaIntervalFulfilled';
|
|
4
11
|
|
|
5
12
|
export type ApiCrossChainOrder = {
|
|
6
13
|
orderId: string;
|
|
@@ -61,8 +68,12 @@ export interface ApiIntentResponse {
|
|
|
61
68
|
/** Unique identifier for the created intent */
|
|
62
69
|
intentId: string;
|
|
63
70
|
|
|
64
|
-
/**
|
|
65
|
-
|
|
71
|
+
/**
|
|
72
|
+
* JWT token used for authorization or subsequent requests.
|
|
73
|
+
* Optional: order-creation responses only return `intentId`; obtain a JWT via the
|
|
74
|
+
* SIWE flow (`EVMSDK.authenticate()` / `fetchJWTToken`) instead.
|
|
75
|
+
*/
|
|
76
|
+
jwt?: string;
|
|
66
77
|
}
|
|
67
78
|
export type ValidResponseData = ApiUserOrders | string | ApiIntentResponse | null;
|
|
68
79
|
|