@shogun-sdk/intents-sdk 1.2.6-test.15 → 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 +535 -484
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +121 -43
- package/dist/index.d.ts +121 -43
- package/dist/index.js +369 -318
- package/dist/index.js.map +1 -1
- package/package.json +10 -7
- package/src/auth/siwe.ts +9 -7
- package/src/constants.ts +32 -17
- 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 -36
- package/src/core/orders/api/index.ts +2 -3
- package/src/core/orders/cross-chain.ts +3 -0
- package/src/core/orders/single-chain.ts +21 -8
- package/src/core/sdk.ts +11 -2
- package/src/core/solana/dca/create-order.ts +6 -19
- package/src/core/solana/intent-helpers.ts +12 -20
- package/src/core/solana/order-instructions.ts +12 -37
- package/src/core/sui/intent-helpers.ts +4 -1
- package/src/index.ts +4 -7
- 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/src/core/solana/transaction-options.ts +0 -83
- package/src/utils/auctioneer-headers.ts +0 -32
|
@@ -35,16 +35,14 @@ import {
|
|
|
35
35
|
import { ChainID } from '../../../chains.js';
|
|
36
36
|
import { getTransferSolInstruction } from '@solana-program/system';
|
|
37
37
|
import { getCreateDcaOrderInstructionAsync } from '../generated/single-chain/index.js';
|
|
38
|
-
import { resolveSolanaFeePayer, resolveSolanaTip, type SolanaTransactionOptions } from '../transaction-options.js';
|
|
39
38
|
|
|
40
39
|
export async function getSolanaDcaSingleChainOrderInstructions(
|
|
41
40
|
order: DcaSingleChainOrder,
|
|
42
|
-
options?:
|
|
41
|
+
options?: { rpcUrl?: string },
|
|
43
42
|
): Promise<SolanaOrderInstructionResult & { secretNumber: string }> {
|
|
44
43
|
const rpc = options?.rpcUrl ? createSolanaRpc(options.rpcUrl) : getDefaultSolanaRPC();
|
|
45
44
|
const orderSigner = await generateKeyPairSigner();
|
|
46
|
-
const
|
|
47
|
-
const feePayerSigner = resolveSolanaFeePayer(userSigner, options?.feePayer);
|
|
45
|
+
const signer = createNoopSigner(order.user as Address);
|
|
48
46
|
|
|
49
47
|
let tokenInMint = address(order.tokenIn);
|
|
50
48
|
|
|
@@ -76,7 +74,7 @@ export async function getSolanaDcaSingleChainOrderInstructions(
|
|
|
76
74
|
|
|
77
75
|
if (!userTokenInAccount.exists) {
|
|
78
76
|
const createAccountIx = await getCreateAssociatedTokenInstructionAsync({
|
|
79
|
-
payer:
|
|
77
|
+
payer: signer,
|
|
80
78
|
ata: tokenInProgramAccount,
|
|
81
79
|
owner: orderUserAddress,
|
|
82
80
|
mint: tokenMintProgram.address,
|
|
@@ -92,7 +90,7 @@ export async function getSolanaDcaSingleChainOrderInstructions(
|
|
|
92
90
|
const transferIx = getTransferSolInstruction({
|
|
93
91
|
amount: totalAmountIn,
|
|
94
92
|
destination: userTokenInAccount.address,
|
|
95
|
-
source:
|
|
93
|
+
source: signer,
|
|
96
94
|
});
|
|
97
95
|
|
|
98
96
|
instructions.push(transferIx);
|
|
@@ -105,7 +103,7 @@ export async function getSolanaDcaSingleChainOrderInstructions(
|
|
|
105
103
|
}
|
|
106
104
|
|
|
107
105
|
const createDcaOrderIx = await getCreateDcaOrderInstructionAsync({
|
|
108
|
-
user:
|
|
106
|
+
user: signer,
|
|
109
107
|
order: orderSigner,
|
|
110
108
|
guard: guardAddress,
|
|
111
109
|
tokenInMint: tokenInMint,
|
|
@@ -121,23 +119,12 @@ export async function getSolanaDcaSingleChainOrderInstructions(
|
|
|
121
119
|
});
|
|
122
120
|
|
|
123
121
|
instructions.push(createDcaOrderIx);
|
|
124
|
-
const tip = resolveSolanaTip(options);
|
|
125
|
-
|
|
126
|
-
if (tip) {
|
|
127
|
-
instructions.push(
|
|
128
|
-
getTransferSolInstruction({
|
|
129
|
-
source: feePayerSigner,
|
|
130
|
-
destination: tip.account,
|
|
131
|
-
amount: tip.lamports,
|
|
132
|
-
}),
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
122
|
|
|
136
123
|
const { value: latestBlockhash } = await rpc.getLatestBlockhash({ commitment: 'confirmed' }).send();
|
|
137
124
|
|
|
138
125
|
const txMessage = pipe(
|
|
139
126
|
createTransactionMessage({ version: 0 }),
|
|
140
|
-
(tx) => setTransactionMessageFeePayerSigner(
|
|
127
|
+
(tx) => setTransactionMessageFeePayerSigner(signer, tx),
|
|
141
128
|
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
|
|
142
129
|
(tx) => appendTransactionMessageInstructions(instructions, tx),
|
|
143
130
|
(tx) => addSignersToTransactionMessage([orderSigner], tx),
|
|
@@ -10,7 +10,6 @@ import type { DcaSingleChainOrder } from '../orders/dca-single-chain.js';
|
|
|
10
10
|
import type { ExtraTransfer, StopLossType } from '../orders/common.js';
|
|
11
11
|
import { getSolanaCrossChainOrderInstructions, getSolanaSingleChainOrderInstructions } from './order-instructions.js';
|
|
12
12
|
import { getSolanaDcaSingleChainOrderInstructions } from './dca/create-order.js';
|
|
13
|
-
import type { SolanaTransactionOptions } from './transaction-options.js';
|
|
14
13
|
import { prepareDcaSecretData } from './dca/single-chain-dca-order.js';
|
|
15
14
|
import { prepareSecretData } from './dca/single-chain-limit-order.js';
|
|
16
15
|
import { generateExecutionDetailsHash } from '../../utils/generate-execution-details-hash.js';
|
|
@@ -61,7 +60,8 @@ export type CreateSolanaCrossChainOrderIntentParams = {
|
|
|
61
60
|
deadline: number;
|
|
62
61
|
extraTransfers?: ExtraTransfer[];
|
|
63
62
|
orderPubkey?: string;
|
|
64
|
-
|
|
63
|
+
stopLossType?: StopLossType;
|
|
64
|
+
stopLossTriggerPrice?: string;
|
|
65
65
|
takeProfitMinOut?: bigint;
|
|
66
66
|
};
|
|
67
67
|
|
|
@@ -151,13 +151,14 @@ export function createSolanaCrossChainOrderIntentRequest(
|
|
|
151
151
|
amountOutMin: params.destinationTokenMinAmount?.toString() || '1',
|
|
152
152
|
destinationAddress: params.destinationAddress,
|
|
153
153
|
extraTransfers: params.extraTransfers,
|
|
154
|
-
|
|
154
|
+
stopLossType: params.stopLossType,
|
|
155
|
+
stopLossTriggerPrice: params.stopLossTriggerPrice,
|
|
155
156
|
takeProfitMinOut: params.takeProfitMinOut?.toString(),
|
|
156
157
|
};
|
|
157
158
|
const executionDetailsString = JSON.stringify(executionDetails, Parsers.bigIntReplacer);
|
|
158
159
|
|
|
159
|
-
//
|
|
160
|
-
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);
|
|
161
162
|
|
|
162
163
|
const genericData = {
|
|
163
164
|
user: params.user,
|
|
@@ -186,11 +187,8 @@ export function createSolanaCrossChainOrderIntentRequest(
|
|
|
186
187
|
* @param order The single-chain order
|
|
187
188
|
* @returns Promise resolving to instruction bytes
|
|
188
189
|
*/
|
|
189
|
-
export async function generateSolanaSingleChainLimitOrderInstructions(
|
|
190
|
-
order
|
|
191
|
-
options?: SolanaTransactionOptions,
|
|
192
|
-
) {
|
|
193
|
-
return getSolanaSingleChainOrderInstructions(order, options);
|
|
190
|
+
export async function generateSolanaSingleChainLimitOrderInstructions(order: SingleChainOrder) {
|
|
191
|
+
return getSolanaSingleChainOrderInstructions(order);
|
|
194
192
|
}
|
|
195
193
|
|
|
196
194
|
/**
|
|
@@ -198,11 +196,8 @@ export async function generateSolanaSingleChainLimitOrderInstructions(
|
|
|
198
196
|
* @param order The DCA order
|
|
199
197
|
* @returns Promise resolving to instruction bytes
|
|
200
198
|
*/
|
|
201
|
-
export async function generateSolanaSingleChainDcaOrderInstructions(
|
|
202
|
-
order
|
|
203
|
-
options?: SolanaTransactionOptions,
|
|
204
|
-
) {
|
|
205
|
-
return getSolanaDcaSingleChainOrderInstructions(order, options);
|
|
199
|
+
export async function generateSolanaSingleChainDcaOrderInstructions(order: DcaSingleChainOrder) {
|
|
200
|
+
return getSolanaDcaSingleChainOrderInstructions(order);
|
|
206
201
|
}
|
|
207
202
|
|
|
208
203
|
/**
|
|
@@ -210,11 +205,8 @@ export async function generateSolanaSingleChainDcaOrderInstructions(
|
|
|
210
205
|
* @param order The cross-chain order
|
|
211
206
|
* @returns Promise resolving to instruction bytes
|
|
212
207
|
*/
|
|
213
|
-
export async function generateSolanaCrossChainOrderInstructions(
|
|
214
|
-
order
|
|
215
|
-
options?: SolanaTransactionOptions,
|
|
216
|
-
) {
|
|
217
|
-
return getSolanaCrossChainOrderInstructions(order, options);
|
|
208
|
+
export async function generateSolanaCrossChainOrderInstructions(order: CrossChainOrder) {
|
|
209
|
+
return getSolanaCrossChainOrderInstructions(order);
|
|
218
210
|
}
|
|
219
211
|
|
|
220
212
|
/**
|
|
@@ -38,7 +38,6 @@ import { getDefaultSolanaRPC } from './client.js';
|
|
|
38
38
|
import { getCreateOrderInstruction } from './generated/cross-chain/index.js';
|
|
39
39
|
import { getCreateLimitOrderInstructionAsync } from './generated/single-chain/index.js';
|
|
40
40
|
import { genSecretHashAndNumber } from './utils.js';
|
|
41
|
-
import { resolveSolanaFeePayer, resolveSolanaTip, type SolanaTransactionOptions } from './transaction-options.js';
|
|
42
41
|
|
|
43
42
|
export type SolanaOrderInstructionResult = {
|
|
44
43
|
// Order address
|
|
@@ -49,12 +48,11 @@ export type SolanaOrderInstructionResult = {
|
|
|
49
48
|
|
|
50
49
|
export async function getSolanaSingleChainOrderInstructions(
|
|
51
50
|
order: SingleChainOrder,
|
|
52
|
-
options?:
|
|
51
|
+
options?: { rpcUrl?: string },
|
|
53
52
|
): Promise<SolanaOrderInstructionResult & { secretNumber: string }> {
|
|
54
53
|
const rpc = options?.rpcUrl ? createSolanaRpc(options.rpcUrl) : getDefaultSolanaRPC();
|
|
55
54
|
const orderSigner = await generateKeyPairSigner();
|
|
56
|
-
const
|
|
57
|
-
const feePayerSigner = resolveSolanaFeePayer(userSigner, options?.feePayer);
|
|
55
|
+
const signer = createNoopSigner(order.user as Address);
|
|
58
56
|
|
|
59
57
|
let tokenInMint = address(order.tokenIn);
|
|
60
58
|
|
|
@@ -86,7 +84,7 @@ export async function getSolanaSingleChainOrderInstructions(
|
|
|
86
84
|
|
|
87
85
|
if (!userTokenInAccount.exists) {
|
|
88
86
|
const createAccountIx = await getCreateAssociatedTokenInstructionAsync({
|
|
89
|
-
payer:
|
|
87
|
+
payer: signer,
|
|
90
88
|
ata: tokenInProgramAccount,
|
|
91
89
|
owner: orderUserAddress,
|
|
92
90
|
mint: tokenMintProgram.address,
|
|
@@ -100,7 +98,7 @@ export async function getSolanaSingleChainOrderInstructions(
|
|
|
100
98
|
const transferIx = getTransferSolInstruction({
|
|
101
99
|
amount: order.amountIn,
|
|
102
100
|
destination: userTokenInAccount.address,
|
|
103
|
-
source:
|
|
101
|
+
source: signer,
|
|
104
102
|
});
|
|
105
103
|
|
|
106
104
|
instructions.push(transferIx);
|
|
@@ -113,7 +111,7 @@ export async function getSolanaSingleChainOrderInstructions(
|
|
|
113
111
|
}
|
|
114
112
|
|
|
115
113
|
const createSingleChainLimitOrderIx = await getCreateLimitOrderInstructionAsync({
|
|
116
|
-
user:
|
|
114
|
+
user: signer,
|
|
117
115
|
order: orderSigner,
|
|
118
116
|
guard: guardAddress,
|
|
119
117
|
tokenInMint: tokenInMint,
|
|
@@ -127,23 +125,12 @@ export async function getSolanaSingleChainOrderInstructions(
|
|
|
127
125
|
});
|
|
128
126
|
|
|
129
127
|
instructions.push(createSingleChainLimitOrderIx);
|
|
130
|
-
const tip = resolveSolanaTip(options);
|
|
131
|
-
|
|
132
|
-
if (tip) {
|
|
133
|
-
instructions.push(
|
|
134
|
-
getTransferSolInstruction({
|
|
135
|
-
source: feePayerSigner,
|
|
136
|
-
destination: tip.account,
|
|
137
|
-
amount: tip.lamports,
|
|
138
|
-
}),
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
128
|
|
|
142
129
|
const { value: latestBlockhash } = await rpc.getLatestBlockhash({ commitment: 'confirmed' }).send();
|
|
143
130
|
|
|
144
131
|
const txMessage = pipe(
|
|
145
132
|
createTransactionMessage({ version: 0 }),
|
|
146
|
-
(tx) => setTransactionMessageFeePayerSigner(
|
|
133
|
+
(tx) => setTransactionMessageFeePayerSigner(signer, tx),
|
|
147
134
|
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
|
|
148
135
|
(tx) => appendTransactionMessageInstructions(instructions, tx),
|
|
149
136
|
(tx) => addSignersToTransactionMessage([orderSigner], tx),
|
|
@@ -162,12 +149,11 @@ export async function getSolanaSingleChainOrderInstructions(
|
|
|
162
149
|
|
|
163
150
|
export async function getSolanaCrossChainOrderInstructions(
|
|
164
151
|
order: CrossChainOrder,
|
|
165
|
-
options?:
|
|
152
|
+
options?: { rpcUrl?: string },
|
|
166
153
|
): Promise<SolanaOrderInstructionResult> {
|
|
167
154
|
const rpc = options?.rpcUrl ? createSolanaRpc(options.rpcUrl) : getDefaultSolanaRPC();
|
|
168
155
|
const orderSigner = await generateKeyPairSigner();
|
|
169
|
-
const
|
|
170
|
-
const feePayerSigner = resolveSolanaFeePayer(userSigner, options?.feePayer);
|
|
156
|
+
const signer = createNoopSigner(order.user as Address);
|
|
171
157
|
|
|
172
158
|
let tokenInMint = address(order.sourceTokenAddress);
|
|
173
159
|
|
|
@@ -214,7 +200,7 @@ export async function getSolanaCrossChainOrderInstructions(
|
|
|
214
200
|
|
|
215
201
|
if (!userTokenInAccount.exists) {
|
|
216
202
|
const createAccountIx = await getCreateAssociatedTokenInstructionAsync({
|
|
217
|
-
payer:
|
|
203
|
+
payer: signer,
|
|
218
204
|
ata: tokenInProgramAccount,
|
|
219
205
|
owner: orderUserAddress,
|
|
220
206
|
mint: tokenMintProgram.address,
|
|
@@ -225,7 +211,7 @@ export async function getSolanaCrossChainOrderInstructions(
|
|
|
225
211
|
}
|
|
226
212
|
|
|
227
213
|
const transferIx = getTransferSolInstruction({
|
|
228
|
-
source:
|
|
214
|
+
source: signer,
|
|
229
215
|
destination: userTokenInAccount.address,
|
|
230
216
|
amount: order.sourceTokenAmount,
|
|
231
217
|
});
|
|
@@ -241,7 +227,7 @@ export async function getSolanaCrossChainOrderInstructions(
|
|
|
241
227
|
const executionHashUint8Array = order.executionDetailsHashToBytes();
|
|
242
228
|
|
|
243
229
|
const createOrderIx = getCreateOrderInstruction({
|
|
244
|
-
user:
|
|
230
|
+
user: signer,
|
|
245
231
|
order: orderSigner,
|
|
246
232
|
guard: address(CROSS_CHAIN_GUARD_ADDRESSES[ChainID.Solana]),
|
|
247
233
|
systemProgram: SYSTEM_PROGRAM_ADDRESS,
|
|
@@ -257,23 +243,12 @@ export async function getSolanaCrossChainOrderInstructions(
|
|
|
257
243
|
});
|
|
258
244
|
|
|
259
245
|
instructions.push(createOrderIx);
|
|
260
|
-
const tip = resolveSolanaTip(options);
|
|
261
|
-
|
|
262
|
-
if (tip) {
|
|
263
|
-
instructions.push(
|
|
264
|
-
getTransferSolInstruction({
|
|
265
|
-
source: feePayerSigner,
|
|
266
|
-
destination: tip.account,
|
|
267
|
-
amount: tip.lamports,
|
|
268
|
-
}),
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
246
|
|
|
272
247
|
const { value: latestBlockhash } = await rpc.getLatestBlockhash({ commitment: 'confirmed' }).send();
|
|
273
248
|
|
|
274
249
|
const txMessage = pipe(
|
|
275
250
|
createTransactionMessage({ version: 0 }),
|
|
276
|
-
(tx) => setTransactionMessageFeePayerSigner(
|
|
251
|
+
(tx) => setTransactionMessageFeePayerSigner(signer, tx),
|
|
277
252
|
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
|
|
278
253
|
(tx) => appendTransactionMessageInstructions(instructions, tx),
|
|
279
254
|
(tx) => addSignersToTransactionMessage([orderSigner], tx),
|
|
@@ -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';
|
|
@@ -28,12 +29,6 @@ export {
|
|
|
28
29
|
} from './types/api.js';
|
|
29
30
|
export { calculateAmounts, getCoinFromResponse, getTokensData } from './utils/defillama.js';
|
|
30
31
|
export type { DefiLlamaCoinData, DefiLlamaTokensResponse } from './utils/defillama.js';
|
|
31
|
-
export {
|
|
32
|
-
DEV_ACCESS_HEADER,
|
|
33
|
-
DEV_ACCESS_STORAGE_KEY,
|
|
34
|
-
getDevAccessHeaderValue,
|
|
35
|
-
withAuctioneerHeaders,
|
|
36
|
-
} from './utils/auctioneer-headers.js';
|
|
37
32
|
export {
|
|
38
33
|
getCancelCrossChainOrderRawData,
|
|
39
34
|
getCancelSingleChainOrderRawData,
|
|
@@ -51,7 +46,6 @@ export {
|
|
|
51
46
|
getSolanaSingleChainOrderInstructions,
|
|
52
47
|
type SolanaOrderInstructionResult,
|
|
53
48
|
} from './core/solana/order-instructions.js';
|
|
54
|
-
export { type SolanaTransactionOptions } from './core/solana/transaction-options.js';
|
|
55
49
|
export {
|
|
56
50
|
cancelCrossChainOrderInstructionsAsBytes,
|
|
57
51
|
cancelSingleChainOrderInstructionsAsBytes,
|
|
@@ -99,13 +93,16 @@ export {
|
|
|
99
93
|
createEvmSingleChainLimitOrderIntentRequest,
|
|
100
94
|
createEvmSingleChainDcaOrderIntentRequest,
|
|
101
95
|
createEvmCrossChainOrderIntentRequest,
|
|
96
|
+
createEvmCrossChainDcaOrderIntentRequest,
|
|
102
97
|
generateEvmSingleChainLimitOrderTypedData,
|
|
103
98
|
generateEvmSingleChainDcaOrderTypedData,
|
|
104
99
|
generateEvmCrossChainOrderTypedData,
|
|
100
|
+
getEVMCrossChainDcaOrderTypedData,
|
|
105
101
|
generateEvmRandomNonce,
|
|
106
102
|
type CreateEvmSingleChainLimitOrderIntentParams,
|
|
107
103
|
type CreateEvmSingleChainDcaOrderIntentParams,
|
|
108
104
|
type CreateEvmCrossChainOrderIntentParams,
|
|
105
|
+
type CreateEvmCrossChainDcaOrderIntentParams,
|
|
109
106
|
} from './core/evm/intent-helpers.js';
|
|
110
107
|
|
|
111
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
|
|
package/src/types/intent.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SupportedChain } from '../chains.js';
|
|
2
2
|
import type { CrossChainOrder } from '../core/orders/cross-chain.js';
|
|
3
|
-
import type { ExtraTransfer } from '../core/orders/common.js';
|
|
3
|
+
import type { ExtraTransfer, StopLossType } from '../core/orders/common.js';
|
|
4
4
|
import type { SingleChainOrder } from '../core/orders/single-chain.js';
|
|
5
5
|
import type { DcaSingleChainOrder } from '../core/orders/dca-single-chain.js';
|
|
6
6
|
|
|
@@ -20,7 +20,11 @@ export type ExecutionDetails = {
|
|
|
20
20
|
/** Extra transfers to be made */
|
|
21
21
|
extraTransfers?: ExtraTransfer[];
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
/** Stop loss type, must match the auctioneer's `stopLossType` field */
|
|
24
|
+
stopLossType?: StopLossType;
|
|
25
|
+
/** Initial trigger price (tokenIn/tokenOut) that arms the stop loss */
|
|
26
|
+
stopLossTriggerPrice?: string;
|
|
27
|
+
/** Take profit minimum amount out */
|
|
24
28
|
takeProfitMinOut?: bigint;
|
|
25
29
|
};
|
|
26
30
|
|
|
@@ -96,6 +100,36 @@ export type SingleChainUserIntentRequest = {
|
|
|
96
100
|
};
|
|
97
101
|
};
|
|
98
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Source chain data for a cross-chain DCA order. Mirrors the auctioneer's
|
|
105
|
+
* `CrossChainDcaOrderGenericRequestData` (common cross-chain fields + flattened
|
|
106
|
+
* `CommonDcaOrderData`). The destination fields live in `executionDetails`.
|
|
107
|
+
*/
|
|
108
|
+
export type DcaCrossChainSourceChainData = {
|
|
109
|
+
user: string;
|
|
110
|
+
srcChainId: SupportedChain;
|
|
111
|
+
tokenIn: string;
|
|
112
|
+
minStablecoinsAmount: bigint;
|
|
113
|
+
deadline: number;
|
|
114
|
+
executionDetailsHash: ExecutionDetailsHash;
|
|
115
|
+
/** Flattened CommonDcaOrderData */
|
|
116
|
+
startTime: number;
|
|
117
|
+
amountInPerInterval: bigint;
|
|
118
|
+
totalIntervals: number;
|
|
119
|
+
intervalDuration: number;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export type DcaCrossChainUserIntentRequest = {
|
|
123
|
+
genericData: DcaCrossChainSourceChainData;
|
|
124
|
+
/** JSON-stringified execution details for the destination chain */
|
|
125
|
+
executionDetails: string;
|
|
126
|
+
chainSpecificData: {
|
|
127
|
+
EVM?: DcaSingleChainEVMPreparedData;
|
|
128
|
+
Solana?: DcaSingleChainSolanaPreparedData;
|
|
129
|
+
Sui?: DcaSingleChainSuiPreparedData;
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
|
|
99
133
|
export type ChainPreparedData = EvmPreparedData | SuiPreparedData | SolanaPreparedData;
|
|
100
134
|
|
|
101
135
|
export type CrossChainOrderPrepared = {
|
|
@@ -32,10 +32,6 @@ export abstract class BaseValidator implements IChainValidator {
|
|
|
32
32
|
this.validateAmount(order.amountIn);
|
|
33
33
|
this.validateAmount(order.amountOutMin ?? 1n);
|
|
34
34
|
|
|
35
|
-
if (order.stopLossMaxOut) {
|
|
36
|
-
this.validateAmount(order.stopLossMaxOut);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
35
|
if (order.takeProfitMinOut) {
|
|
40
36
|
this.validateAmount(order.takeProfitMinOut);
|
|
41
37
|
}
|
|
@@ -1,36 +1,13 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
|
-
import type { ExtraTransfer } from '../core/orders/common.js';
|
|
3
|
-
import { Parsers } from './parsers.js';
|
|
4
|
-
|
|
5
|
-
export function generateExecutionDetailsHash({
|
|
6
|
-
destChainId,
|
|
7
|
-
destinationAddress,
|
|
8
|
-
tokenOut,
|
|
9
|
-
amountOutMin,
|
|
10
|
-
stopLossMaxOut,
|
|
11
|
-
takeProfitMinOut,
|
|
12
|
-
extraTransfers,
|
|
13
|
-
}: {
|
|
14
|
-
destChainId: number;
|
|
15
|
-
destinationAddress: string;
|
|
16
|
-
tokenOut: string;
|
|
17
|
-
amountOutMin: string;
|
|
18
|
-
stopLossMaxOut?: string;
|
|
19
|
-
takeProfitMinOut?: string;
|
|
20
|
-
extraTransfers?: ExtraTransfer[];
|
|
21
|
-
}): string {
|
|
22
|
-
const executionDetails = JSON.stringify(
|
|
23
|
-
{
|
|
24
|
-
destChainId,
|
|
25
|
-
destinationAddress,
|
|
26
|
-
tokenOut,
|
|
27
|
-
amountOutMin,
|
|
28
|
-
stopLossMaxOut,
|
|
29
|
-
takeProfitMinOut,
|
|
30
|
-
extraTransfers,
|
|
31
|
-
},
|
|
32
|
-
Parsers.bigIntReplacer,
|
|
33
|
-
);
|
|
34
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Computes the SHA-256 hash the auctioneer expects for cross-chain execution details.
|
|
5
|
+
*
|
|
6
|
+
* The auctioneer recomputes sha256 over the exact `executionDetails` JSON string it
|
|
7
|
+
* receives and rejects the order when it differs (hash mismatch). Callers therefore MUST
|
|
8
|
+
* hash the same serialized string they send on the wire — pass that string here directly
|
|
9
|
+
* instead of re-serializing a parallel object, which is how key-order/field drift creeps in.
|
|
10
|
+
*/
|
|
11
|
+
export function generateExecutionDetailsHash(executionDetails: string): string {
|
|
35
12
|
return '0x' + createHash('sha256').update(executionDetails).digest('hex');
|
|
36
13
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Case-insensitive address equality; false if either address is missing. */
|
|
2
|
+
export function compareAddresses(firstAddress?: string, secondAddress?: string): boolean {
|
|
3
|
+
return !!firstAddress && !!secondAddress && firstAddress.toLowerCase() === secondAddress.toLowerCase();
|
|
4
|
+
}
|
|
@@ -9,10 +9,11 @@ import { LiquidSwapQuoteProvider, type LiquidSwapQuoteResponse } from './liquids
|
|
|
9
9
|
import { RaydiumQuoteProvider, type RaydiumPumpQuoteParams, type RaydiumQuoteResponse } from './raydium.js';
|
|
10
10
|
import { PumpFunQuoteProvider } from './pumpfun/index.js';
|
|
11
11
|
import { RelayQuoteProvider } from './relay.js';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
};
|
|
12
|
+
import { convertDecimals } from './pricing/decimals.js';
|
|
13
|
+
import { estimateAmountOut, IntentNotViableError } from './pricing/estimate-amount-out.js';
|
|
14
|
+
import { estimateExpensesInTokenOut } from './pricing/expenses.js';
|
|
15
|
+
import { DEFAULT_COMMISSION_BPS, CROSS_CHAIN_COMMISSION_DENOMINATOR, EVM_CROSS_CHAIN_GAS_LIMIT } from './pricing/constants.js';
|
|
16
|
+
import { compareAddresses } from './address.js';
|
|
16
17
|
|
|
17
18
|
type SingleChainQuoteParams = {
|
|
18
19
|
chainId: ChainID;
|
|
@@ -95,6 +96,7 @@ export class QuoteProvider {
|
|
|
95
96
|
},
|
|
96
97
|
};
|
|
97
98
|
} catch (e) {
|
|
99
|
+
if (e instanceof IntentNotViableError) throw e;
|
|
98
100
|
console.error('Error getting quote from routers:', e);
|
|
99
101
|
throw new Error('Failed to get quote from routers');
|
|
100
102
|
}
|
|
@@ -136,12 +138,22 @@ export class QuoteProvider {
|
|
|
136
138
|
tokenOut: sourceStable.address,
|
|
137
139
|
};
|
|
138
140
|
|
|
139
|
-
const sourceQuote = await
|
|
141
|
+
const [sourceQuote, expenses] = await Promise.all([
|
|
142
|
+
this.getSingleChainQuote(sourceSingleChainQuoteParams),
|
|
143
|
+
estimateExpensesInTokenOut({
|
|
144
|
+
chainId: params.destChainId,
|
|
145
|
+
tokenOut: params.tokenOut,
|
|
146
|
+
gasLimit: EVM_CROSS_CHAIN_GAS_LIMIT,
|
|
147
|
+
getQuote: (p) => this.getSingleChainQuote(p),
|
|
148
|
+
}),
|
|
149
|
+
]);
|
|
140
150
|
|
|
141
151
|
// Step 2: Normalize stable amount between source and destination ---
|
|
142
152
|
// Adjust for decimal difference between stables (e.g. 6 → 18)
|
|
143
|
-
const normalizedBridgeAmount =
|
|
144
|
-
|
|
153
|
+
const normalizedBridgeAmount = convertDecimals(
|
|
154
|
+
sourceQuote.amountOut,
|
|
155
|
+
sourceStable.decimals,
|
|
156
|
+
destStable.decimals,
|
|
145
157
|
);
|
|
146
158
|
|
|
147
159
|
// Step 3: Get quote from dest bridge stable → target token ---
|
|
@@ -154,17 +166,25 @@ export class QuoteProvider {
|
|
|
154
166
|
|
|
155
167
|
const destQuote = await this.getSingleChainQuote(destSingleChainQuoteParams);
|
|
156
168
|
|
|
157
|
-
//
|
|
169
|
+
// Step 4: Compute stable min-amount with correct decimals ---
|
|
158
170
|
const estimatedAmountInAsMinStablecoinAmount = BigInt(
|
|
159
171
|
Math.floor(sourceQuote.amountInUsd * 10 ** sourceStable.decimals),
|
|
160
172
|
);
|
|
161
173
|
|
|
174
|
+
const swapOutput = destQuote.amountOutMin ?? destQuote.amountOut;
|
|
175
|
+
const estimatedAmountOutReduced = estimateAmountOut(
|
|
176
|
+
swapOutput,
|
|
177
|
+
DEFAULT_COMMISSION_BPS,
|
|
178
|
+
expenses,
|
|
179
|
+
CROSS_CHAIN_COMMISSION_DENOMINATOR,
|
|
180
|
+
);
|
|
181
|
+
|
|
162
182
|
return {
|
|
163
183
|
estimatedAmountOut: destQuote.amountOut,
|
|
164
184
|
estimatedAmountOutUsd: destQuote.amountOutUsd,
|
|
165
185
|
amountInUsd: sourceQuote.amountInUsd,
|
|
166
186
|
estimatedAmountInAsMinStablecoinAmount,
|
|
167
|
-
estimatedAmountOutReduced
|
|
187
|
+
estimatedAmountOutReduced,
|
|
168
188
|
estimatedAmountOutUsdReduced: destQuote.amountOutUsd,
|
|
169
189
|
};
|
|
170
190
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ChainID } from '../../../chains.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Solver commission in basis points. MUST equal the solver's COMMISSION env value
|
|
5
|
+
* (solver/src/config.rs). Update both together to avoid quote divergence.
|
|
6
|
+
*/
|
|
7
|
+
export const DEFAULT_COMMISSION_BPS = 50n; // 0.5% — placeholder, confirm against solver COMMISSION
|
|
8
|
+
|
|
9
|
+
/** Single-chain divisor — mirrors evaluate.rs:130-131 (`commission / 10_000`). */
|
|
10
|
+
export const SINGLE_CHAIN_COMMISSION_DENOMINATOR = 10_000n;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Cross-chain divisor — mirrors cross_chain_estimation.rs:213-214 (`commission / 100_000`).
|
|
14
|
+
* The asymmetry with the single-chain divisor is intentional (verbatim solver mirror);
|
|
15
|
+
* confirm with the solver team whether this is a unit choice or a bug before relying on it.
|
|
16
|
+
*/
|
|
17
|
+
export const CROSS_CHAIN_COMMISSION_DENOMINATOR = 100_000n;
|
|
18
|
+
|
|
19
|
+
/** EVM fulfillment gas units — mirrors solver/src/constants/chains.rs (EVM single-chain). */
|
|
20
|
+
export const EVM_SINGLE_CHAIN_GAS_LIMIT = 3_000_000n;
|
|
21
|
+
|
|
22
|
+
/** EVM cross-chain fulfillment gas units — mirrors solver/src/constants/chains.rs (EVM cross-chain). */
|
|
23
|
+
export const EVM_CROSS_CHAIN_GAS_LIMIT = 2_000_000n;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Fixed native-token gas cost for non-EVM chains, in the chain's smallest unit.
|
|
27
|
+
* Mirrors solver/src/constants/chains.rs: Sui = 20_000_000 MIST; Solana base = 200_000 lamports
|
|
28
|
+
* (single-receiver default; per-receiver ATA cost is not modeled at quote time).
|
|
29
|
+
*/
|
|
30
|
+
export const NON_EVM_FIXED_NATIVE_GAS_COST: Partial<Record<ChainID, bigint>> = {
|
|
31
|
+
[ChainID.Sui]: 20_000_000n,
|
|
32
|
+
[ChainID.Solana]: 200_000n,
|
|
33
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integer decimal conversion between two tokens, mirroring solver cross_chain_estimation.rs:205-211.
|
|
3
|
+
* Uses BigInt powers of ten to avoid the float precision loss of `Math.floor(Number(...))`.
|
|
4
|
+
*/
|
|
5
|
+
export function convertDecimals(amount: bigint, fromDecimals: number, toDecimals: number): bigint {
|
|
6
|
+
const diff = toDecimals - fromDecimals;
|
|
7
|
+
if (diff >= 0) {
|
|
8
|
+
return amount * 10n ** BigInt(diff);
|
|
9
|
+
}
|
|
10
|
+
return amount / 10n ** BigInt(-diff);
|
|
11
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Expense components, all expressed in the output token's smallest units. */
|
|
2
|
+
export type Expenses = {
|
|
3
|
+
gasInTokenOut: bigint;
|
|
4
|
+
protocolFeeInTokenOut: bigint;
|
|
5
|
+
extraTransfersInTokenOut: bigint;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/** Thrown when total expenses meet or exceed the swap output, mirroring the solver's rejection. */
|
|
9
|
+
export class IntentNotViableError extends Error {
|
|
10
|
+
constructor(message: string) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'IntentNotViableError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Ports solver_rust evaluate.rs: final amount = swapOutput − expenses − commission.
|
|
18
|
+
* `commissionBps` is basis points; `commissionDenominator` selects single-chain (10_000n)
|
|
19
|
+
* vs cross-chain (100_000n) semantics. Throws IntentNotViableError when nothing is left.
|
|
20
|
+
*/
|
|
21
|
+
export function estimateAmountOut(
|
|
22
|
+
swapOutput: bigint,
|
|
23
|
+
commissionBps: bigint,
|
|
24
|
+
expenses: Expenses,
|
|
25
|
+
commissionDenominator = 10_000n,
|
|
26
|
+
): bigint {
|
|
27
|
+
const commissionFee = (swapOutput * commissionBps) / commissionDenominator;
|
|
28
|
+
const total =
|
|
29
|
+
expenses.gasInTokenOut + expenses.protocolFeeInTokenOut + expenses.extraTransfersInTokenOut + commissionFee;
|
|
30
|
+
|
|
31
|
+
if (total >= swapOutput) {
|
|
32
|
+
throw new IntentNotViableError(`Total expenses (${total}) meet or exceed swap output (${swapOutput})`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return swapOutput - total;
|
|
36
|
+
}
|