@luxfi/dex 1.2.1 → 2.0.0
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/client/clob.d.ts +52 -0
- package/dist/client/clob.d.ts.map +1 -0
- package/dist/client/clob.js +196 -0
- package/dist/client/index.d.ts +7 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +6 -0
- package/dist/client/types.d.ts +126 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +5 -0
- package/dist/hooks/index.d.ts +22 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +25 -0
- package/dist/hooks/use-lxbook.d.ts +95 -0
- package/dist/hooks/use-lxbook.d.ts.map +1 -0
- package/dist/hooks/use-lxbook.js +213 -0
- package/dist/hooks/use-lxfeed.d.ts +111 -0
- package/dist/hooks/use-lxfeed.d.ts.map +1 -0
- package/dist/hooks/use-lxfeed.js +152 -0
- package/dist/hooks/use-lxvault.d.ts +137 -0
- package/dist/hooks/use-lxvault.d.ts.map +1 -0
- package/dist/hooks/use-lxvault.js +227 -0
- package/dist/hooks/use-quote.d.ts +18 -0
- package/dist/hooks/use-quote.d.ts.map +1 -0
- package/dist/hooks/use-quote.js +65 -0
- package/dist/hooks/use-swap.d.ts +17 -0
- package/dist/hooks/use-swap.d.ts.map +1 -0
- package/dist/hooks/use-swap.js +75 -0
- package/dist/index.d.ts +50 -115
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +72 -225
- package/dist/precompile/abis.d.ts +991 -0
- package/dist/precompile/abis.d.ts.map +1 -0
- package/dist/precompile/abis.js +743 -0
- package/dist/precompile/addresses.d.ts +129 -0
- package/dist/precompile/addresses.d.ts.map +1 -0
- package/dist/precompile/addresses.js +117 -0
- package/dist/precompile/index.d.ts +19 -0
- package/dist/precompile/index.d.ts.map +1 -0
- package/dist/precompile/index.js +18 -0
- package/dist/precompile/types.d.ts +246 -0
- package/dist/precompile/types.d.ts.map +1 -0
- package/dist/precompile/types.js +84 -0
- package/dist/router/index.d.ts +7 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +6 -0
- package/dist/router/router.d.ts +58 -0
- package/dist/router/router.d.ts.map +1 -0
- package/dist/router/router.js +272 -0
- package/dist/router/types.d.ts +76 -0
- package/dist/router/types.d.ts.map +1 -0
- package/dist/router/types.js +1 -0
- package/package.json +55 -29
- package/src/client/clob.ts +256 -0
- package/src/client/index.ts +6 -0
- package/src/client/types.ts +148 -0
- package/src/hooks/index.ts +29 -0
- package/src/hooks/use-lxbook.ts +343 -0
- package/src/hooks/use-lxfeed.ts +179 -0
- package/src/hooks/use-lxvault.ts +318 -0
- package/src/hooks/use-quote.ts +92 -0
- package/src/hooks/use-swap.ts +103 -0
- package/src/index.ts +142 -309
- package/src/precompile/abis.ts +755 -0
- package/src/precompile/addresses.ts +153 -0
- package/src/precompile/index.ts +18 -0
- package/src/precompile/types.ts +295 -0
- package/src/router/index.ts +6 -0
- package/src/router/router.ts +338 -0
- package/src/router/types.ts +87 -0
- package/dist/marketData.d.ts +0 -152
- package/dist/marketData.d.ts.map +0 -1
- package/dist/marketData.js +0 -253
- package/src/marketData.ts +0 -351
- package/tsconfig.json +0 -19
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LXVault Hooks (LP-9030)
|
|
3
|
+
* React hooks for custody, margin, and positions via LXVault precompile
|
|
4
|
+
*/
|
|
5
|
+
import { useCallback } from 'react'
|
|
6
|
+
import { useReadContract, useWriteContract, useWaitForTransactionReceipt, useAccount } from 'wagmi'
|
|
7
|
+
import type { Address } from 'viem'
|
|
8
|
+
import { LX } from '../precompile/addresses'
|
|
9
|
+
import { LX_VAULT_ABI } from '../precompile/abis'
|
|
10
|
+
import type { LXAccount, LXPosition, LXMarginInfo } from '../precompile/types'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Build account tuple from address and subaccount
|
|
14
|
+
*/
|
|
15
|
+
function buildAccount(main: Address, subaccountId: number = 0): LXAccount {
|
|
16
|
+
return { main, subaccountId }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Hook to get token balance in vault
|
|
21
|
+
*/
|
|
22
|
+
export function useLXVaultBalance(token: Address, subaccountId: number = 0): any {
|
|
23
|
+
const { address } = useAccount()
|
|
24
|
+
|
|
25
|
+
const { data, isLoading, error, refetch } = useReadContract({
|
|
26
|
+
address: LX.LX_VAULT,
|
|
27
|
+
abi: LX_VAULT_ABI,
|
|
28
|
+
functionName: 'getBalance',
|
|
29
|
+
args: address ? [buildAccount(address, subaccountId), token] : undefined,
|
|
30
|
+
query: { enabled: !!address },
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
balance: data as bigint | undefined,
|
|
35
|
+
isLoading,
|
|
36
|
+
error,
|
|
37
|
+
refetch,
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Hook to get position for a market
|
|
43
|
+
*/
|
|
44
|
+
export function useLXVaultPosition(marketId: number, subaccountId: number = 0): any {
|
|
45
|
+
const { address } = useAccount()
|
|
46
|
+
|
|
47
|
+
const { data, isLoading, error, refetch } = useReadContract({
|
|
48
|
+
address: LX.LX_VAULT,
|
|
49
|
+
abi: LX_VAULT_ABI,
|
|
50
|
+
functionName: 'getPosition',
|
|
51
|
+
args: address ? [buildAccount(address, subaccountId), marketId] : undefined,
|
|
52
|
+
query: { enabled: !!address },
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
position: data as LXPosition | undefined,
|
|
57
|
+
isLoading,
|
|
58
|
+
error,
|
|
59
|
+
refetch,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Hook to get margin info (free margin, used margin, etc.)
|
|
65
|
+
*/
|
|
66
|
+
export function useLXVaultMargin(subaccountId: number = 0): any {
|
|
67
|
+
const { address } = useAccount()
|
|
68
|
+
|
|
69
|
+
const { data, isLoading, error, refetch } = useReadContract({
|
|
70
|
+
address: LX.LX_VAULT,
|
|
71
|
+
abi: LX_VAULT_ABI,
|
|
72
|
+
functionName: 'getMargin',
|
|
73
|
+
args: address ? [buildAccount(address, subaccountId)] : undefined,
|
|
74
|
+
query: { enabled: !!address },
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
margin: data as LXMarginInfo | undefined,
|
|
79
|
+
isLoading,
|
|
80
|
+
error,
|
|
81
|
+
refetch,
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Hook to check if account is liquidatable
|
|
87
|
+
*/
|
|
88
|
+
export function useLXVaultLiquidatable(account?: Address, subaccountId: number = 0): any {
|
|
89
|
+
const { address: connectedAddress } = useAccount()
|
|
90
|
+
const targetAddress = account ?? connectedAddress
|
|
91
|
+
|
|
92
|
+
const { data, isLoading, error, refetch } = useReadContract({
|
|
93
|
+
address: LX.LX_VAULT,
|
|
94
|
+
abi: LX_VAULT_ABI,
|
|
95
|
+
functionName: 'isLiquidatable',
|
|
96
|
+
args: targetAddress ? [buildAccount(targetAddress, subaccountId)] : undefined,
|
|
97
|
+
query: { enabled: !!targetAddress },
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
const result = data as [boolean, bigint] | undefined
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
liquidatable: result?.[0],
|
|
104
|
+
shortfall: result?.[1],
|
|
105
|
+
isLoading,
|
|
106
|
+
error,
|
|
107
|
+
refetch,
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Hook to get funding rate for a market
|
|
113
|
+
*/
|
|
114
|
+
export function useLXVaultFundingRate(marketId: number): any {
|
|
115
|
+
const { data, isLoading, error, refetch } = useReadContract({
|
|
116
|
+
address: LX.LX_VAULT,
|
|
117
|
+
abi: LX_VAULT_ABI,
|
|
118
|
+
functionName: 'getFundingRate',
|
|
119
|
+
args: [marketId],
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
const result = data as [bigint, bigint] | undefined
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
rateX18: result?.[0],
|
|
126
|
+
nextFundingTime: result?.[1],
|
|
127
|
+
isLoading,
|
|
128
|
+
error,
|
|
129
|
+
refetch,
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
interface UseLXVaultDepositResult {
|
|
134
|
+
deposit: (token: Address, amount: bigint, subaccountId?: number) => void
|
|
135
|
+
hash: `0x${string}` | undefined
|
|
136
|
+
isPending: boolean
|
|
137
|
+
isConfirming: boolean
|
|
138
|
+
isSuccess: boolean
|
|
139
|
+
error: Error | null
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Hook for depositing tokens into vault
|
|
144
|
+
*/
|
|
145
|
+
export function useLXVaultDeposit(): UseLXVaultDepositResult {
|
|
146
|
+
const { writeContract, data: hash, isPending, error } = useWriteContract()
|
|
147
|
+
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash })
|
|
148
|
+
|
|
149
|
+
const deposit = useCallback(
|
|
150
|
+
(token: Address, amount: bigint, subaccountId: number = 0) => {
|
|
151
|
+
writeContract({
|
|
152
|
+
address: LX.LX_VAULT,
|
|
153
|
+
abi: LX_VAULT_ABI,
|
|
154
|
+
functionName: 'deposit',
|
|
155
|
+
args: [token, amount, subaccountId],
|
|
156
|
+
})
|
|
157
|
+
},
|
|
158
|
+
[writeContract]
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
deposit,
|
|
163
|
+
hash,
|
|
164
|
+
isPending,
|
|
165
|
+
isConfirming,
|
|
166
|
+
isSuccess,
|
|
167
|
+
error,
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
interface UseLXVaultWithdrawResult {
|
|
172
|
+
withdraw: (token: Address, amount: bigint, subaccountId?: number) => void
|
|
173
|
+
hash: `0x${string}` | undefined
|
|
174
|
+
isPending: boolean
|
|
175
|
+
isConfirming: boolean
|
|
176
|
+
isSuccess: boolean
|
|
177
|
+
error: Error | null
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Hook for withdrawing tokens from vault
|
|
182
|
+
*/
|
|
183
|
+
export function useLXVaultWithdraw(): UseLXVaultWithdrawResult {
|
|
184
|
+
const { writeContract, data: hash, isPending, error } = useWriteContract()
|
|
185
|
+
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash })
|
|
186
|
+
|
|
187
|
+
const withdraw = useCallback(
|
|
188
|
+
(token: Address, amount: bigint, subaccountId: number = 0) => {
|
|
189
|
+
writeContract({
|
|
190
|
+
address: LX.LX_VAULT,
|
|
191
|
+
abi: LX_VAULT_ABI,
|
|
192
|
+
functionName: 'withdraw',
|
|
193
|
+
args: [token, amount, subaccountId],
|
|
194
|
+
})
|
|
195
|
+
},
|
|
196
|
+
[writeContract]
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
withdraw,
|
|
201
|
+
hash,
|
|
202
|
+
isPending,
|
|
203
|
+
isConfirming,
|
|
204
|
+
isSuccess,
|
|
205
|
+
error,
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
interface UseLXVaultTransferResult {
|
|
210
|
+
transfer: (token: Address, amount: bigint, fromSubaccount: number, toSubaccount: number) => void
|
|
211
|
+
hash: `0x${string}` | undefined
|
|
212
|
+
isPending: boolean
|
|
213
|
+
isConfirming: boolean
|
|
214
|
+
isSuccess: boolean
|
|
215
|
+
error: Error | null
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Hook for transferring between subaccounts
|
|
220
|
+
*/
|
|
221
|
+
export function useLXVaultTransfer(): UseLXVaultTransferResult {
|
|
222
|
+
const { writeContract, data: hash, isPending, error } = useWriteContract()
|
|
223
|
+
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash })
|
|
224
|
+
|
|
225
|
+
const transfer = useCallback(
|
|
226
|
+
(
|
|
227
|
+
token: Address,
|
|
228
|
+
amount: bigint,
|
|
229
|
+
fromSubaccount: number,
|
|
230
|
+
toSubaccount: number
|
|
231
|
+
) => {
|
|
232
|
+
writeContract({
|
|
233
|
+
address: LX.LX_VAULT,
|
|
234
|
+
abi: LX_VAULT_ABI,
|
|
235
|
+
functionName: 'transfer',
|
|
236
|
+
args: [token, amount, fromSubaccount, toSubaccount],
|
|
237
|
+
})
|
|
238
|
+
},
|
|
239
|
+
[writeContract]
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
transfer,
|
|
244
|
+
hash,
|
|
245
|
+
isPending,
|
|
246
|
+
isConfirming,
|
|
247
|
+
isSuccess,
|
|
248
|
+
error,
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
interface UseLXVaultLiquidateResult {
|
|
253
|
+
liquidate: (targetAccount: Address, targetSubaccount: number, marketId: number, sizeX18: bigint) => void
|
|
254
|
+
hash: `0x${string}` | undefined
|
|
255
|
+
isPending: boolean
|
|
256
|
+
isConfirming: boolean
|
|
257
|
+
isSuccess: boolean
|
|
258
|
+
error: Error | null
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Hook for liquidating underwater positions
|
|
263
|
+
*/
|
|
264
|
+
export function useLXVaultLiquidate(): UseLXVaultLiquidateResult {
|
|
265
|
+
const { writeContract, data: hash, isPending, error } = useWriteContract()
|
|
266
|
+
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash })
|
|
267
|
+
|
|
268
|
+
const liquidate = useCallback(
|
|
269
|
+
(
|
|
270
|
+
targetAccount: Address,
|
|
271
|
+
targetSubaccount: number,
|
|
272
|
+
marketId: number,
|
|
273
|
+
sizeX18: bigint
|
|
274
|
+
) => {
|
|
275
|
+
writeContract({
|
|
276
|
+
address: LX.LX_VAULT,
|
|
277
|
+
abi: LX_VAULT_ABI,
|
|
278
|
+
functionName: 'liquidate',
|
|
279
|
+
args: [buildAccount(targetAccount, targetSubaccount), marketId, sizeX18],
|
|
280
|
+
})
|
|
281
|
+
},
|
|
282
|
+
[writeContract]
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
liquidate,
|
|
287
|
+
hash,
|
|
288
|
+
isPending,
|
|
289
|
+
isConfirming,
|
|
290
|
+
isSuccess,
|
|
291
|
+
error,
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Combined hook for common vault operations
|
|
297
|
+
*/
|
|
298
|
+
export function useLXVault(subaccountId: number = 0): any {
|
|
299
|
+
const { address } = useAccount()
|
|
300
|
+
const margin = useLXVaultMargin(subaccountId)
|
|
301
|
+
const { deposit, isPending: isDepositing } = useLXVaultDeposit()
|
|
302
|
+
const { withdraw, isPending: isWithdrawing } = useLXVaultWithdraw()
|
|
303
|
+
const { transfer, isPending: isTransferring } = useLXVaultTransfer()
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
address,
|
|
307
|
+
subaccountId,
|
|
308
|
+
margin: margin.margin,
|
|
309
|
+
isLoadingMargin: margin.isLoading,
|
|
310
|
+
deposit,
|
|
311
|
+
withdraw,
|
|
312
|
+
transfer,
|
|
313
|
+
isDepositing,
|
|
314
|
+
isWithdrawing,
|
|
315
|
+
isTransferring,
|
|
316
|
+
refetchMargin: margin.refetch,
|
|
317
|
+
}
|
|
318
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback, useEffect, useRef } from 'react'
|
|
4
|
+
import type { Address } from 'viem'
|
|
5
|
+
import { usePublicClient } from 'wagmi'
|
|
6
|
+
import { OmnichainRouter, type Quote, type QuoteRequest } from '../router'
|
|
7
|
+
|
|
8
|
+
interface UseQuoteOptions {
|
|
9
|
+
refreshInterval?: number // ms
|
|
10
|
+
enabled?: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface UseQuoteResult {
|
|
14
|
+
quote: Quote | null
|
|
15
|
+
isLoading: boolean
|
|
16
|
+
error: Error | null
|
|
17
|
+
refetch: () => Promise<void>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Hook to get swap quotes from the omnichain router
|
|
22
|
+
*/
|
|
23
|
+
export function useQuote(
|
|
24
|
+
tokenIn: Address | undefined,
|
|
25
|
+
tokenOut: Address | undefined,
|
|
26
|
+
amountIn: bigint | undefined,
|
|
27
|
+
options: UseQuoteOptions = {}
|
|
28
|
+
): UseQuoteResult {
|
|
29
|
+
const { refreshInterval = 10000, enabled = true } = options
|
|
30
|
+
const publicClient = usePublicClient()
|
|
31
|
+
const routerRef = useRef<OmnichainRouter | null>(null)
|
|
32
|
+
|
|
33
|
+
const [quote, setQuote] = useState<Quote | null>(null)
|
|
34
|
+
const [isLoading, setIsLoading] = useState(false)
|
|
35
|
+
const [error, setError] = useState<Error | null>(null)
|
|
36
|
+
|
|
37
|
+
// Initialize router
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!routerRef.current) {
|
|
40
|
+
routerRef.current = new OmnichainRouter()
|
|
41
|
+
}
|
|
42
|
+
if (publicClient) {
|
|
43
|
+
routerRef.current.setPublicClient(publicClient)
|
|
44
|
+
}
|
|
45
|
+
}, [publicClient])
|
|
46
|
+
|
|
47
|
+
const fetchQuote = useCallback(async () => {
|
|
48
|
+
if (!tokenIn || !tokenOut || !amountIn || amountIn === 0n || !enabled) {
|
|
49
|
+
setQuote(null)
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setIsLoading(true)
|
|
54
|
+
setError(null)
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const request: QuoteRequest = {
|
|
58
|
+
tokenIn,
|
|
59
|
+
tokenOut,
|
|
60
|
+
amountIn,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const newQuote = await routerRef.current!.getQuote(request)
|
|
64
|
+
setQuote(newQuote)
|
|
65
|
+
} catch (err) {
|
|
66
|
+
setError(err instanceof Error ? err : new Error('Failed to get quote'))
|
|
67
|
+
setQuote(null)
|
|
68
|
+
} finally {
|
|
69
|
+
setIsLoading(false)
|
|
70
|
+
}
|
|
71
|
+
}, [tokenIn, tokenOut, amountIn, enabled])
|
|
72
|
+
|
|
73
|
+
// Fetch on mount and when inputs change
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
fetchQuote()
|
|
76
|
+
}, [fetchQuote])
|
|
77
|
+
|
|
78
|
+
// Auto-refresh
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (!enabled || refreshInterval <= 0) return
|
|
81
|
+
|
|
82
|
+
const interval = setInterval(fetchQuote, refreshInterval)
|
|
83
|
+
return () => clearInterval(interval)
|
|
84
|
+
}, [fetchQuote, enabled, refreshInterval])
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
quote,
|
|
88
|
+
isLoading,
|
|
89
|
+
error,
|
|
90
|
+
refetch: fetchQuote,
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback } from 'react'
|
|
4
|
+
import type { Address } from 'viem'
|
|
5
|
+
import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi'
|
|
6
|
+
import { DEX_PRECOMPILES } from '../precompile/addresses'
|
|
7
|
+
import { SWAP_ROUTER_ABI } from '../precompile/abis'
|
|
8
|
+
import { createPoolKey } from '../precompile/types'
|
|
9
|
+
import type { Quote } from '../router'
|
|
10
|
+
|
|
11
|
+
interface UseSwapResult {
|
|
12
|
+
swap: (quote: Quote, recipient: Address) => Promise<void>
|
|
13
|
+
isPending: boolean
|
|
14
|
+
isConfirming: boolean
|
|
15
|
+
isSuccess: boolean
|
|
16
|
+
error: Error | null
|
|
17
|
+
txHash: `0x${string}` | undefined
|
|
18
|
+
reset: () => void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Hook to execute swaps via the DEX precompiles
|
|
23
|
+
*/
|
|
24
|
+
export function useSwap(): UseSwapResult {
|
|
25
|
+
const [error, setError] = useState<Error | null>(null)
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
data: txHash,
|
|
29
|
+
writeContractAsync,
|
|
30
|
+
isPending,
|
|
31
|
+
reset: resetWrite,
|
|
32
|
+
} = useWriteContract()
|
|
33
|
+
|
|
34
|
+
const {
|
|
35
|
+
isLoading: isConfirming,
|
|
36
|
+
isSuccess,
|
|
37
|
+
} = useWaitForTransactionReceipt({
|
|
38
|
+
hash: txHash,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const swap = useCallback(async (quote: Quote, recipient: Address) => {
|
|
42
|
+
setError(null)
|
|
43
|
+
|
|
44
|
+
if (!quote || quote.route.length === 0) {
|
|
45
|
+
setError(new Error('Invalid quote'))
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const step = quote.route[0]
|
|
50
|
+
|
|
51
|
+
if (step.source === 'amm') {
|
|
52
|
+
try {
|
|
53
|
+
// Execute AMM swap via precompile
|
|
54
|
+
const poolKey = createPoolKey(quote.tokenIn, quote.tokenOut)
|
|
55
|
+
const zeroForOne = quote.tokenIn.toLowerCase() < quote.tokenOut.toLowerCase()
|
|
56
|
+
|
|
57
|
+
await writeContractAsync({
|
|
58
|
+
address: DEX_PRECOMPILES.SWAP_ROUTER,
|
|
59
|
+
abi: SWAP_ROUTER_ABI,
|
|
60
|
+
functionName: 'exactInputSingle',
|
|
61
|
+
args: [{
|
|
62
|
+
poolKey,
|
|
63
|
+
zeroForOne,
|
|
64
|
+
amountIn: quote.amountIn,
|
|
65
|
+
amountOutMinimum: quote.minimumAmountOut,
|
|
66
|
+
sqrtPriceLimitX96: 0n,
|
|
67
|
+
hookData: '0x',
|
|
68
|
+
}],
|
|
69
|
+
// Include native value if swapping from native LUX
|
|
70
|
+
value: quote.tokenIn === '0x0000000000000000000000000000000000000000'
|
|
71
|
+
? quote.amountIn
|
|
72
|
+
: 0n,
|
|
73
|
+
})
|
|
74
|
+
} catch (err) {
|
|
75
|
+
setError(err instanceof Error ? err : new Error('Swap failed'))
|
|
76
|
+
throw err
|
|
77
|
+
}
|
|
78
|
+
} else if (step.source === 'clob') {
|
|
79
|
+
// CLOB swaps are handled off-chain
|
|
80
|
+
// The frontend should use the CLOB client directly
|
|
81
|
+
setError(new Error('CLOB swaps should be executed via CLOB client'))
|
|
82
|
+
throw new Error('CLOB swaps not supported in this hook')
|
|
83
|
+
} else {
|
|
84
|
+
setError(new Error(`Unknown route source: ${step.source}`))
|
|
85
|
+
throw new Error(`Unknown route source: ${step.source}`)
|
|
86
|
+
}
|
|
87
|
+
}, [writeContractAsync])
|
|
88
|
+
|
|
89
|
+
const reset = useCallback(() => {
|
|
90
|
+
setError(null)
|
|
91
|
+
resetWrite()
|
|
92
|
+
}, [resetWrite])
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
swap,
|
|
96
|
+
isPending,
|
|
97
|
+
isConfirming,
|
|
98
|
+
isSuccess,
|
|
99
|
+
error,
|
|
100
|
+
txHash,
|
|
101
|
+
reset,
|
|
102
|
+
}
|
|
103
|
+
}
|