@luxfi/exchange 0.1.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/bridge/__tests__/use-private-teleport.test.d.ts +2 -0
- package/dist/bridge/__tests__/use-private-teleport.test.d.ts.map +1 -0
- package/dist/bridge/__tests__/use-private-teleport.test.js +272 -0
- package/dist/bridge/cross-chain-store.d.ts +57 -0
- package/dist/bridge/cross-chain-store.d.ts.map +1 -0
- package/dist/bridge/cross-chain-store.js +158 -0
- package/dist/bridge/index.d.ts +78 -0
- package/dist/bridge/index.d.ts.map +1 -0
- package/dist/bridge/index.js +79 -0
- package/dist/bridge/private-teleport-types.d.ts +634 -0
- package/dist/bridge/private-teleport-types.d.ts.map +1 -0
- package/dist/bridge/private-teleport-types.js +308 -0
- package/dist/bridge/types.d.ts +84 -0
- package/dist/bridge/types.d.ts.map +1 -0
- package/dist/bridge/types.js +37 -0
- package/dist/bridge/use-cross-chain-mint.d.ts +34 -0
- package/dist/bridge/use-cross-chain-mint.d.ts.map +1 -0
- package/dist/bridge/use-cross-chain-mint.js +228 -0
- package/dist/bridge/use-private-teleport.d.ts +69 -0
- package/dist/bridge/use-private-teleport.d.ts.map +1 -0
- package/dist/bridge/use-private-teleport.js +666 -0
- package/dist/chains/index.d.ts +6 -0
- package/dist/chains/index.d.ts.map +1 -0
- package/dist/chains/index.js +6 -0
- package/dist/chains/lux.d.ts +508 -0
- package/dist/chains/lux.d.ts.map +1 -0
- package/dist/chains/lux.js +131 -0
- package/dist/contracts/abis/dex-swap-router.d.ts +137 -0
- package/dist/contracts/abis/dex-swap-router.d.ts.map +1 -0
- package/dist/contracts/abis/dex-swap-router.js +95 -0
- package/dist/contracts/abis/erc20.d.ts +136 -0
- package/dist/contracts/abis/erc20.d.ts.map +1 -0
- package/dist/contracts/abis/erc20.js +96 -0
- package/dist/contracts/abis/index.d.ts +15 -0
- package/dist/contracts/abis/index.d.ts.map +1 -0
- package/dist/contracts/abis/index.js +15 -0
- package/dist/contracts/abis/nft-position-manager.d.ts +235 -0
- package/dist/contracts/abis/nft-position-manager.d.ts.map +1 -0
- package/dist/contracts/abis/nft-position-manager.js +146 -0
- package/dist/contracts/abis/pool-manager.d.ts +315 -0
- package/dist/contracts/abis/pool-manager.d.ts.map +1 -0
- package/dist/contracts/abis/pool-manager.js +191 -0
- package/dist/contracts/abis/quoter-v2.d.ts +103 -0
- package/dist/contracts/abis/quoter-v2.d.ts.map +1 -0
- package/dist/contracts/abis/quoter-v2.js +68 -0
- package/dist/contracts/abis/swap-router.d.ts +119 -0
- package/dist/contracts/abis/swap-router.d.ts.map +1 -0
- package/dist/contracts/abis/swap-router.js +75 -0
- package/dist/contracts/abis/uniswap-v2-factory.d.ts +75 -0
- package/dist/contracts/abis/uniswap-v2-factory.d.ts.map +1 -0
- package/dist/contracts/abis/uniswap-v2-factory.js +49 -0
- package/dist/contracts/abis/uniswap-v2-pair.d.ts +119 -0
- package/dist/contracts/abis/uniswap-v2-pair.d.ts.map +1 -0
- package/dist/contracts/abis/uniswap-v2-pair.js +85 -0
- package/dist/contracts/abis/uniswap-v2-router.d.ts +249 -0
- package/dist/contracts/abis/uniswap-v2-router.d.ts.map +1 -0
- package/dist/contracts/abis/uniswap-v2-router.js +146 -0
- package/dist/contracts/abis/uniswap-v3-factory.d.ts +77 -0
- package/dist/contracts/abis/uniswap-v3-factory.d.ts.map +1 -0
- package/dist/contracts/abis/uniswap-v3-factory.js +45 -0
- package/dist/contracts/abis/uniswap-v3-pool.d.ts +128 -0
- package/dist/contracts/abis/uniswap-v3-pool.d.ts.map +1 -0
- package/dist/contracts/abis/uniswap-v3-pool.js +81 -0
- package/dist/contracts/addresses.d.ts +141 -0
- package/dist/contracts/addresses.d.ts.map +1 -0
- package/dist/contracts/addresses.js +108 -0
- package/dist/contracts/index.d.ts +6 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +5 -0
- package/dist/dex/balance-delta.d.ts +27 -0
- package/dist/dex/balance-delta.d.ts.map +1 -0
- package/dist/dex/balance-delta.js +45 -0
- package/dist/dex/index.d.ts +7 -0
- package/dist/dex/index.d.ts.map +1 -0
- package/dist/dex/index.js +6 -0
- package/dist/dex/pool-key.d.ts +19 -0
- package/dist/dex/pool-key.d.ts.map +1 -0
- package/dist/dex/pool-key.js +44 -0
- package/dist/dex/types.d.ts +71 -0
- package/dist/dex/types.d.ts.map +1 -0
- package/dist/dex/types.js +28 -0
- package/dist/hooks/index.d.ts +10 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +9 -0
- package/dist/hooks/use-pools.d.ts +24 -0
- package/dist/hooks/use-pools.d.ts.map +1 -0
- package/dist/hooks/use-pools.js +85 -0
- package/dist/hooks/use-positions.d.ts +17 -0
- package/dist/hooks/use-positions.d.ts.map +1 -0
- package/dist/hooks/use-positions.js +65 -0
- package/dist/hooks/use-swap-quote.d.ts +19 -0
- package/dist/hooks/use-swap-quote.d.ts.map +1 -0
- package/dist/hooks/use-swap-quote.js +54 -0
- package/dist/hooks/use-swap.d.ts +22 -0
- package/dist/hooks/use-swap.d.ts.map +1 -0
- package/dist/hooks/use-swap.js +46 -0
- package/dist/hooks/use-token-allowance.d.ts +27 -0
- package/dist/hooks/use-token-allowance.d.ts.map +1 -0
- package/dist/hooks/use-token-allowance.js +59 -0
- package/dist/hooks/use-token-balance.d.ts +17 -0
- package/dist/hooks/use-token-balance.d.ts.map +1 -0
- package/dist/hooks/use-token-balance.js +58 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/stores/index.d.ts +7 -0
- package/dist/stores/index.d.ts.map +1 -0
- package/dist/stores/index.js +6 -0
- package/dist/stores/settings-store.d.ts +25 -0
- package/dist/stores/settings-store.d.ts.map +1 -0
- package/dist/stores/settings-store.js +16 -0
- package/dist/stores/swap-store.d.ts +38 -0
- package/dist/stores/swap-store.d.ts.map +1 -0
- package/dist/stores/swap-store.js +58 -0
- package/dist/stores/token-store.d.ts +21 -0
- package/dist/stores/token-store.d.ts.map +1 -0
- package/dist/stores/token-store.js +32 -0
- package/dist/tokens/index.d.ts +65 -0
- package/dist/tokens/index.d.ts.map +1 -0
- package/dist/tokens/index.js +185 -0
- package/package.json +78 -0
- package/src/chains/index.ts +21 -0
- package/src/chains/lux.ts +141 -0
- package/src/contracts/abis/dex-swap-router.ts +98 -0
- package/src/contracts/abis/erc20.ts +96 -0
- package/src/contracts/abis/index.ts +17 -0
- package/src/contracts/abis/nft-position-manager.ts +146 -0
- package/src/contracts/abis/pool-manager.ts +198 -0
- package/src/contracts/abis/quoter-v2.ts +68 -0
- package/src/contracts/abis/swap-router.ts +75 -0
- package/src/contracts/abis/uniswap-v2-factory.ts +49 -0
- package/src/contracts/abis/uniswap-v2-pair.ts +85 -0
- package/src/contracts/abis/uniswap-v2-router.ts +146 -0
- package/src/contracts/abis/uniswap-v3-factory.ts +45 -0
- package/src/contracts/abis/uniswap-v3-pool.ts +81 -0
- package/src/contracts/addresses.ts +128 -0
- package/src/contracts/index.ts +14 -0
- package/src/dex/balance-delta.ts +52 -0
- package/src/dex/index.ts +7 -0
- package/src/dex/pool-key.ts +62 -0
- package/src/dex/types.ts +87 -0
- package/src/hooks/index.ts +10 -0
- package/src/hooks/use-pools.ts +116 -0
- package/src/hooks/use-positions.ts +90 -0
- package/src/hooks/use-swap-quote.ts +81 -0
- package/src/hooks/use-swap.ts +64 -0
- package/src/hooks/use-token-allowance.ts +74 -0
- package/src/hooks/use-token-balance.ts +71 -0
- package/src/index.ts +31 -0
- package/src/stores/index.ts +7 -0
- package/src/stores/settings-store.ts +54 -0
- package/src/stores/swap-store.ts +112 -0
- package/src/stores/token-store.ts +62 -0
- package/src/tokens/index.ts +220 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useQuery } from '@tanstack/react-query'
|
|
4
|
+
import { usePublicClient } from 'wagmi'
|
|
5
|
+
import { getContracts } from '../contracts'
|
|
6
|
+
import { QUOTER_V2_ABI } from '../contracts/abis'
|
|
7
|
+
import type { Token } from '../tokens'
|
|
8
|
+
|
|
9
|
+
export interface SwapQuote {
|
|
10
|
+
amountIn: bigint
|
|
11
|
+
amountOut: bigint
|
|
12
|
+
priceImpact: number
|
|
13
|
+
route: Token[]
|
|
14
|
+
gasEstimate: bigint
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface UseSwapQuoteParams {
|
|
18
|
+
tokenIn: Token | null
|
|
19
|
+
tokenOut: Token | null
|
|
20
|
+
amountIn: bigint
|
|
21
|
+
chainId: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Hook to get swap quote from V3 Quoter
|
|
26
|
+
*/
|
|
27
|
+
export function useSwapQuote({
|
|
28
|
+
tokenIn,
|
|
29
|
+
tokenOut,
|
|
30
|
+
amountIn,
|
|
31
|
+
chainId,
|
|
32
|
+
}: UseSwapQuoteParams) {
|
|
33
|
+
const publicClient = usePublicClient({ chainId })
|
|
34
|
+
const contracts = getContracts(chainId)
|
|
35
|
+
|
|
36
|
+
return useQuery({
|
|
37
|
+
queryKey: ['swapQuote', tokenIn?.address, tokenOut?.address, amountIn.toString(), chainId],
|
|
38
|
+
queryFn: async (): Promise<SwapQuote | null> => {
|
|
39
|
+
if (!tokenIn || !tokenOut || amountIn === 0n || !publicClient) {
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
// Call QuoterV2 for quote
|
|
45
|
+
const result = await publicClient.simulateContract({
|
|
46
|
+
address: contracts.V3_QUOTER_V2,
|
|
47
|
+
abi: QUOTER_V2_ABI,
|
|
48
|
+
functionName: 'quoteExactInputSingle',
|
|
49
|
+
args: [
|
|
50
|
+
{
|
|
51
|
+
tokenIn: tokenIn.address,
|
|
52
|
+
tokenOut: tokenOut.address,
|
|
53
|
+
amountIn,
|
|
54
|
+
fee: 3000, // 0.30% fee tier
|
|
55
|
+
sqrtPriceLimitX96: 0n,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const [amountOut, , , gasEstimate] = result.result as [bigint, bigint, number, bigint]
|
|
61
|
+
|
|
62
|
+
// Calculate price impact (simplified)
|
|
63
|
+
const priceImpact = 0.003 // 0.3% placeholder
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
amountIn,
|
|
67
|
+
amountOut,
|
|
68
|
+
priceImpact,
|
|
69
|
+
route: [tokenIn, tokenOut],
|
|
70
|
+
gasEstimate,
|
|
71
|
+
}
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error('Failed to get swap quote:', error)
|
|
74
|
+
return null
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
enabled: !!tokenIn && !!tokenOut && amountIn > 0n && !!publicClient,
|
|
78
|
+
staleTime: 10_000, // 10 seconds
|
|
79
|
+
refetchInterval: 15_000, // Refetch every 15 seconds
|
|
80
|
+
})
|
|
81
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useMutation } from '@tanstack/react-query'
|
|
4
|
+
import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi'
|
|
5
|
+
import { parseUnits } from 'viem'
|
|
6
|
+
import { getContracts } from '../contracts'
|
|
7
|
+
import { SWAP_ROUTER_ABI } from '../contracts/abis'
|
|
8
|
+
import type { Token } from '../tokens'
|
|
9
|
+
import { isNativeToken } from '../tokens'
|
|
10
|
+
|
|
11
|
+
export interface SwapRequest {
|
|
12
|
+
tokenIn: Token
|
|
13
|
+
tokenOut: Token
|
|
14
|
+
amountIn: bigint
|
|
15
|
+
amountOutMinimum: bigint
|
|
16
|
+
recipient: `0x${string}`
|
|
17
|
+
deadline: bigint
|
|
18
|
+
chainId: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Hook to execute a swap transaction
|
|
23
|
+
*/
|
|
24
|
+
export function useSwap() {
|
|
25
|
+
const { writeContractAsync, data: hash, isPending } = useWriteContract()
|
|
26
|
+
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash })
|
|
27
|
+
|
|
28
|
+
const swap = useMutation({
|
|
29
|
+
mutationFn: async (params: SwapRequest) => {
|
|
30
|
+
const contracts = getContracts(params.chainId)
|
|
31
|
+
const isNativeIn = isNativeToken(params.tokenIn)
|
|
32
|
+
|
|
33
|
+
const txHash = await writeContractAsync({
|
|
34
|
+
address: contracts.V3_SWAP_ROUTER_02,
|
|
35
|
+
abi: SWAP_ROUTER_ABI,
|
|
36
|
+
functionName: 'exactInputSingle',
|
|
37
|
+
args: [
|
|
38
|
+
{
|
|
39
|
+
tokenIn: isNativeIn ? contracts.WLUX : params.tokenIn.address,
|
|
40
|
+
tokenOut: params.tokenOut.address,
|
|
41
|
+
fee: 3000, // 0.30% fee tier
|
|
42
|
+
recipient: params.recipient,
|
|
43
|
+
deadline: params.deadline,
|
|
44
|
+
amountIn: params.amountIn,
|
|
45
|
+
amountOutMinimum: params.amountOutMinimum,
|
|
46
|
+
sqrtPriceLimitX96: 0n,
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
value: isNativeIn ? params.amountIn : 0n,
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
return txHash
|
|
53
|
+
},
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
swap: swap.mutateAsync,
|
|
58
|
+
hash,
|
|
59
|
+
isPending: isPending || swap.isPending,
|
|
60
|
+
isConfirming,
|
|
61
|
+
isSuccess,
|
|
62
|
+
error: swap.error,
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useReadContract, useWriteContract, useWaitForTransactionReceipt } from 'wagmi'
|
|
4
|
+
import { maxUint256 } from 'viem'
|
|
5
|
+
import { ERC20_ABI } from '../contracts/abis'
|
|
6
|
+
import type { Token } from '../tokens'
|
|
7
|
+
import { isNativeToken } from '../tokens'
|
|
8
|
+
import type { Address } from 'viem'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Hook to check and manage token allowance
|
|
12
|
+
*/
|
|
13
|
+
export function useTokenAllowance(
|
|
14
|
+
token: Token | null,
|
|
15
|
+
owner: Address | undefined,
|
|
16
|
+
spender: Address | undefined,
|
|
17
|
+
chainId: number
|
|
18
|
+
) {
|
|
19
|
+
const allowance = useReadContract({
|
|
20
|
+
address: token?.address,
|
|
21
|
+
abi: ERC20_ABI,
|
|
22
|
+
functionName: 'allowance',
|
|
23
|
+
args: owner && spender ? [owner, spender] : undefined,
|
|
24
|
+
chainId,
|
|
25
|
+
query: {
|
|
26
|
+
enabled: !!token && !isNativeToken(token) && !!owner && !!spender,
|
|
27
|
+
},
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
const { writeContractAsync, data: hash, isPending } = useWriteContract()
|
|
31
|
+
const { isLoading: isConfirming, isSuccess } = useWaitForTransactionReceipt({ hash })
|
|
32
|
+
|
|
33
|
+
const approve = async (amount: bigint = maxUint256) => {
|
|
34
|
+
if (!token || !spender || isNativeToken(token)) return
|
|
35
|
+
|
|
36
|
+
return writeContractAsync({
|
|
37
|
+
address: token.address,
|
|
38
|
+
abi: ERC20_ABI,
|
|
39
|
+
functionName: 'approve',
|
|
40
|
+
args: [spender, amount],
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Native tokens don't need approval
|
|
45
|
+
if (token && isNativeToken(token)) {
|
|
46
|
+
return {
|
|
47
|
+
allowance: maxUint256,
|
|
48
|
+
isApproved: true,
|
|
49
|
+
approve,
|
|
50
|
+
isPending: false,
|
|
51
|
+
isConfirming: false,
|
|
52
|
+
isSuccess: true,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const currentAllowance = (allowance.data as bigint) ?? 0n
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
allowance: currentAllowance,
|
|
60
|
+
isApproved: currentAllowance > 0n,
|
|
61
|
+
approve,
|
|
62
|
+
isPending,
|
|
63
|
+
isConfirming,
|
|
64
|
+
isSuccess,
|
|
65
|
+
refetch: allowance.refetch,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Check if approval is needed for a specific amount
|
|
71
|
+
*/
|
|
72
|
+
export function needsApproval(allowance: bigint, amount: bigint): boolean {
|
|
73
|
+
return allowance < amount
|
|
74
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useBalance, useReadContract } from 'wagmi'
|
|
4
|
+
import { ERC20_ABI } from '../contracts/abis'
|
|
5
|
+
import type { Token } from '../tokens'
|
|
6
|
+
import { isNativeToken } from '../tokens'
|
|
7
|
+
import type { Address } from 'viem'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Hook to get token balance for an address
|
|
11
|
+
*/
|
|
12
|
+
export function useTokenBalance(
|
|
13
|
+
token: Token | null,
|
|
14
|
+
address: Address | undefined,
|
|
15
|
+
chainId: number
|
|
16
|
+
) {
|
|
17
|
+
// For native token
|
|
18
|
+
const nativeBalance = useBalance({
|
|
19
|
+
address,
|
|
20
|
+
chainId,
|
|
21
|
+
query: {
|
|
22
|
+
enabled: !!token && isNativeToken(token) && !!address,
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
// For ERC20 token
|
|
27
|
+
const erc20Balance = useReadContract({
|
|
28
|
+
address: token?.address,
|
|
29
|
+
abi: ERC20_ABI,
|
|
30
|
+
functionName: 'balanceOf',
|
|
31
|
+
args: address ? [address] : undefined,
|
|
32
|
+
chainId,
|
|
33
|
+
query: {
|
|
34
|
+
enabled: !!token && !isNativeToken(token) && !!address,
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
if (!token || !address) {
|
|
39
|
+
return {
|
|
40
|
+
balance: 0n,
|
|
41
|
+
formatted: '0',
|
|
42
|
+
isLoading: false,
|
|
43
|
+
error: null,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (isNativeToken(token)) {
|
|
48
|
+
const value = nativeBalance.data?.value ?? 0n
|
|
49
|
+
return {
|
|
50
|
+
balance: value,
|
|
51
|
+
formatted: formatBalance(value, token.decimals),
|
|
52
|
+
isLoading: nativeBalance.isLoading,
|
|
53
|
+
error: nativeBalance.error,
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
balance: (erc20Balance.data as bigint) ?? 0n,
|
|
59
|
+
formatted: formatBalance((erc20Balance.data as bigint) ?? 0n, token.decimals),
|
|
60
|
+
isLoading: erc20Balance.isLoading,
|
|
61
|
+
error: erc20Balance.error,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function formatBalance(value: bigint, decimals: number): string {
|
|
66
|
+
const divisor = 10n ** BigInt(decimals)
|
|
67
|
+
const whole = value / divisor
|
|
68
|
+
const fraction = value % divisor
|
|
69
|
+
const fractionStr = fraction.toString().padStart(decimals, '0').slice(0, 6)
|
|
70
|
+
return `${whole}.${fractionStr}`.replace(/\.?0+$/, '')
|
|
71
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @luxfi/exchange - Core DEX logic for Lux Exchange
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - Chain and token definitions
|
|
6
|
+
* - AMM V2/V3 contract interfaces
|
|
7
|
+
* - DEX precompile integration
|
|
8
|
+
* - Swap/liquidity hooks
|
|
9
|
+
* - Zustand stores for state management
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Chains
|
|
13
|
+
export * from './chains'
|
|
14
|
+
|
|
15
|
+
// Tokens
|
|
16
|
+
export * from './tokens'
|
|
17
|
+
|
|
18
|
+
// Contracts & ABIs
|
|
19
|
+
export * from './contracts'
|
|
20
|
+
|
|
21
|
+
// DEX types
|
|
22
|
+
export * from './dex'
|
|
23
|
+
|
|
24
|
+
// Hooks
|
|
25
|
+
export * from './hooks'
|
|
26
|
+
|
|
27
|
+
// Stores
|
|
28
|
+
export * from './stores'
|
|
29
|
+
|
|
30
|
+
// Note: Bridge module is experimental and will be added in a future release
|
|
31
|
+
// See bridge-wip/ for the work-in-progress implementation
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { create } from 'zustand'
|
|
2
|
+
import { persist } from 'zustand/middleware'
|
|
3
|
+
|
|
4
|
+
export type RouterPreference = 'auto' | 'v2' | 'v3' | 'precompile'
|
|
5
|
+
|
|
6
|
+
export interface SettingsStore {
|
|
7
|
+
// Router preference
|
|
8
|
+
routerPreference: RouterPreference
|
|
9
|
+
|
|
10
|
+
// Expert mode (skip confirmation modals)
|
|
11
|
+
expertMode: boolean
|
|
12
|
+
|
|
13
|
+
// Auto slippage
|
|
14
|
+
autoSlippage: boolean
|
|
15
|
+
|
|
16
|
+
// Hide small balances
|
|
17
|
+
hideSmallBalances: boolean
|
|
18
|
+
|
|
19
|
+
// Small balance threshold (in USD)
|
|
20
|
+
smallBalanceThreshold: number
|
|
21
|
+
|
|
22
|
+
// Actions
|
|
23
|
+
setRouterPreference: (preference: RouterPreference) => void
|
|
24
|
+
toggleExpertMode: () => void
|
|
25
|
+
toggleAutoSlippage: () => void
|
|
26
|
+
toggleHideSmallBalances: () => void
|
|
27
|
+
setSmallBalanceThreshold: (threshold: number) => void
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const useSettingsStore = create<SettingsStore>()(
|
|
31
|
+
persist(
|
|
32
|
+
(set) => ({
|
|
33
|
+
routerPreference: 'auto',
|
|
34
|
+
expertMode: false,
|
|
35
|
+
autoSlippage: true,
|
|
36
|
+
hideSmallBalances: false,
|
|
37
|
+
smallBalanceThreshold: 1,
|
|
38
|
+
|
|
39
|
+
setRouterPreference: (preference) => set({ routerPreference: preference }),
|
|
40
|
+
|
|
41
|
+
toggleExpertMode: () => set((state) => ({ expertMode: !state.expertMode })),
|
|
42
|
+
|
|
43
|
+
toggleAutoSlippage: () => set((state) => ({ autoSlippage: !state.autoSlippage })),
|
|
44
|
+
|
|
45
|
+
toggleHideSmallBalances: () =>
|
|
46
|
+
set((state) => ({ hideSmallBalances: !state.hideSmallBalances })),
|
|
47
|
+
|
|
48
|
+
setSmallBalanceThreshold: (threshold) => set({ smallBalanceThreshold: threshold }),
|
|
49
|
+
}),
|
|
50
|
+
{
|
|
51
|
+
name: 'settings-storage',
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { create } from 'zustand'
|
|
2
|
+
import { persist } from 'zustand/middleware'
|
|
3
|
+
import type { Token } from '../tokens'
|
|
4
|
+
|
|
5
|
+
export type SwapField = 'input' | 'output'
|
|
6
|
+
|
|
7
|
+
export interface SwapState {
|
|
8
|
+
// Tokens
|
|
9
|
+
tokenIn: Token | null
|
|
10
|
+
tokenOut: Token | null
|
|
11
|
+
|
|
12
|
+
// Amounts (as strings for input fields)
|
|
13
|
+
amountIn: string
|
|
14
|
+
amountOut: string
|
|
15
|
+
|
|
16
|
+
// Which field is being typed in
|
|
17
|
+
independentField: SwapField
|
|
18
|
+
|
|
19
|
+
// Slippage tolerance (in basis points, e.g., 50 = 0.5%)
|
|
20
|
+
slippageTolerance: number
|
|
21
|
+
|
|
22
|
+
// Transaction deadline (in minutes)
|
|
23
|
+
deadline: number
|
|
24
|
+
|
|
25
|
+
// Actions
|
|
26
|
+
setTokenIn: (token: Token | null) => void
|
|
27
|
+
setTokenOut: (token: Token | null) => void
|
|
28
|
+
setAmountIn: (amount: string) => void
|
|
29
|
+
setAmountOut: (amount: string) => void
|
|
30
|
+
setIndependentField: (field: SwapField) => void
|
|
31
|
+
setSlippageTolerance: (tolerance: number) => void
|
|
32
|
+
setDeadline: (deadline: number) => void
|
|
33
|
+
switchTokens: () => void
|
|
34
|
+
reset: () => void
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const initialState = {
|
|
38
|
+
tokenIn: null,
|
|
39
|
+
tokenOut: null,
|
|
40
|
+
amountIn: '',
|
|
41
|
+
amountOut: '',
|
|
42
|
+
independentField: 'input' as SwapField,
|
|
43
|
+
slippageTolerance: 50, // 0.5%
|
|
44
|
+
deadline: 30, // 30 minutes
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const useSwapStore = create<SwapState>()(
|
|
48
|
+
persist(
|
|
49
|
+
(set) => ({
|
|
50
|
+
...initialState,
|
|
51
|
+
|
|
52
|
+
setTokenIn: (token) =>
|
|
53
|
+
set((state) => {
|
|
54
|
+
// If selecting same token as output, switch them
|
|
55
|
+
if (token && state.tokenOut && token.address === state.tokenOut.address) {
|
|
56
|
+
return {
|
|
57
|
+
tokenIn: token,
|
|
58
|
+
tokenOut: state.tokenIn,
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { tokenIn: token }
|
|
62
|
+
}),
|
|
63
|
+
|
|
64
|
+
setTokenOut: (token) =>
|
|
65
|
+
set((state) => {
|
|
66
|
+
// If selecting same token as input, switch them
|
|
67
|
+
if (token && state.tokenIn && token.address === state.tokenIn.address) {
|
|
68
|
+
return {
|
|
69
|
+
tokenOut: token,
|
|
70
|
+
tokenIn: state.tokenOut,
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { tokenOut: token }
|
|
74
|
+
}),
|
|
75
|
+
|
|
76
|
+
setAmountIn: (amount) =>
|
|
77
|
+
set({
|
|
78
|
+
amountIn: amount,
|
|
79
|
+
independentField: 'input',
|
|
80
|
+
}),
|
|
81
|
+
|
|
82
|
+
setAmountOut: (amount) =>
|
|
83
|
+
set({
|
|
84
|
+
amountOut: amount,
|
|
85
|
+
independentField: 'output',
|
|
86
|
+
}),
|
|
87
|
+
|
|
88
|
+
setIndependentField: (field) => set({ independentField: field }),
|
|
89
|
+
|
|
90
|
+
setSlippageTolerance: (tolerance) => set({ slippageTolerance: tolerance }),
|
|
91
|
+
|
|
92
|
+
setDeadline: (deadline) => set({ deadline: deadline }),
|
|
93
|
+
|
|
94
|
+
switchTokens: () =>
|
|
95
|
+
set((state) => ({
|
|
96
|
+
tokenIn: state.tokenOut,
|
|
97
|
+
tokenOut: state.tokenIn,
|
|
98
|
+
amountIn: state.amountOut,
|
|
99
|
+
amountOut: state.amountIn,
|
|
100
|
+
})),
|
|
101
|
+
|
|
102
|
+
reset: () => set(initialState),
|
|
103
|
+
}),
|
|
104
|
+
{
|
|
105
|
+
name: 'swap-storage',
|
|
106
|
+
partialize: (state) => ({
|
|
107
|
+
slippageTolerance: state.slippageTolerance,
|
|
108
|
+
deadline: state.deadline,
|
|
109
|
+
}),
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { create } from 'zustand'
|
|
2
|
+
import { persist } from 'zustand/middleware'
|
|
3
|
+
import type { Token } from '../tokens'
|
|
4
|
+
|
|
5
|
+
export interface TokenStore {
|
|
6
|
+
// Custom tokens added by user
|
|
7
|
+
customTokens: Token[]
|
|
8
|
+
|
|
9
|
+
// Favorite tokens
|
|
10
|
+
favoriteTokens: string[] // addresses
|
|
11
|
+
|
|
12
|
+
// Actions
|
|
13
|
+
addCustomToken: (token: Token) => void
|
|
14
|
+
removeCustomToken: (address: string) => void
|
|
15
|
+
toggleFavorite: (address: string) => void
|
|
16
|
+
isFavorite: (address: string) => boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const useTokenStore = create<TokenStore>()(
|
|
20
|
+
persist(
|
|
21
|
+
(set, get) => ({
|
|
22
|
+
customTokens: [],
|
|
23
|
+
favoriteTokens: [],
|
|
24
|
+
|
|
25
|
+
addCustomToken: (token) =>
|
|
26
|
+
set((state) => {
|
|
27
|
+
// Check if token already exists
|
|
28
|
+
if (state.customTokens.some((t) => t.address.toLowerCase() === token.address.toLowerCase())) {
|
|
29
|
+
return state
|
|
30
|
+
}
|
|
31
|
+
return { customTokens: [...state.customTokens, token] }
|
|
32
|
+
}),
|
|
33
|
+
|
|
34
|
+
removeCustomToken: (address) =>
|
|
35
|
+
set((state) => ({
|
|
36
|
+
customTokens: state.customTokens.filter(
|
|
37
|
+
(t) => t.address.toLowerCase() !== address.toLowerCase()
|
|
38
|
+
),
|
|
39
|
+
})),
|
|
40
|
+
|
|
41
|
+
toggleFavorite: (address) =>
|
|
42
|
+
set((state) => {
|
|
43
|
+
const normalized = address.toLowerCase()
|
|
44
|
+
if (state.favoriteTokens.includes(normalized)) {
|
|
45
|
+
return {
|
|
46
|
+
favoriteTokens: state.favoriteTokens.filter((a) => a !== normalized),
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
favoriteTokens: [...state.favoriteTokens, normalized],
|
|
51
|
+
}
|
|
52
|
+
}),
|
|
53
|
+
|
|
54
|
+
isFavorite: (address) => {
|
|
55
|
+
return get().favoriteTokens.includes(address.toLowerCase())
|
|
56
|
+
},
|
|
57
|
+
}),
|
|
58
|
+
{
|
|
59
|
+
name: 'token-storage',
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
)
|