tempo.ts 0.1.5 → 0.2.1
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/README.md +33 -2
- package/dist/chains.d.ts +509 -115
- package/dist/chains.d.ts.map +1 -1
- package/dist/chains.js +20 -9
- package/dist/chains.js.map +1 -1
- package/dist/ox/Order.d.ts +92 -0
- package/dist/ox/Order.d.ts.map +1 -0
- package/dist/ox/Order.js +88 -0
- package/dist/ox/Order.js.map +1 -0
- package/dist/ox/OrdersFilters.d.ts +72 -0
- package/dist/ox/OrdersFilters.d.ts.map +1 -0
- package/dist/ox/OrdersFilters.js +100 -0
- package/dist/ox/OrdersFilters.js.map +1 -0
- package/dist/ox/Pagination.d.ts +128 -0
- package/dist/ox/Pagination.d.ts.map +1 -0
- package/dist/ox/Pagination.js +78 -0
- package/dist/ox/Pagination.js.map +1 -0
- package/dist/ox/PoolId.d.ts +18 -0
- package/dist/ox/PoolId.d.ts.map +1 -0
- package/dist/ox/PoolId.js +13 -0
- package/dist/ox/PoolId.js.map +1 -0
- package/dist/ox/RpcSchema.d.ts +32 -0
- package/dist/ox/RpcSchema.d.ts.map +1 -0
- package/dist/ox/RpcSchema.js +2 -0
- package/dist/ox/RpcSchema.js.map +1 -0
- package/dist/ox/SignatureEnvelope.d.ts +1 -1
- package/dist/ox/SignatureEnvelope.d.ts.map +1 -1
- package/dist/ox/SignatureEnvelope.js.map +1 -1
- package/dist/{viem → ox}/Tick.d.ts +4 -0
- package/dist/ox/Tick.d.ts.map +1 -0
- package/dist/ox/Tick.js.map +1 -0
- package/dist/ox/Transaction.d.ts.map +1 -1
- package/dist/ox/Transaction.js +2 -1
- package/dist/ox/Transaction.js.map +1 -1
- package/dist/ox/TransactionEnvelopeAA.d.ts +6 -6
- package/dist/ox/TransactionEnvelopeAA.d.ts.map +1 -1
- package/dist/ox/TransactionEnvelopeAA.js +4 -2
- package/dist/ox/TransactionEnvelopeAA.js.map +1 -1
- package/dist/ox/TransactionRequest.d.ts +4 -0
- package/dist/ox/TransactionRequest.d.ts.map +1 -1
- package/dist/ox/TransactionRequest.js.map +1 -1
- package/dist/ox/index.d.ts +6 -0
- package/dist/ox/index.d.ts.map +1 -1
- package/dist/ox/index.js +6 -0
- package/dist/ox/index.js.map +1 -1
- package/dist/prool/Instance.d.ts.map +1 -1
- package/dist/prool/Instance.js +20 -4
- package/dist/prool/Instance.js.map +1 -1
- package/dist/viem/Abis.d.ts +1469 -1082
- package/dist/viem/Abis.d.ts.map +1 -1
- package/dist/viem/Abis.js +932 -671
- package/dist/viem/Abis.js.map +1 -1
- package/dist/viem/Account.d.ts +150 -0
- package/dist/viem/Account.d.ts.map +1 -0
- package/dist/viem/Account.js +221 -0
- package/dist/viem/Account.js.map +1 -0
- package/dist/viem/Actions/amm.d.ts +144 -161
- package/dist/viem/Actions/amm.d.ts.map +1 -1
- package/dist/viem/Actions/amm.js +109 -163
- package/dist/viem/Actions/amm.js.map +1 -1
- package/dist/viem/Actions/dex.d.ts +920 -664
- package/dist/viem/Actions/dex.d.ts.map +1 -1
- package/dist/viem/Actions/dex.js +129 -30
- package/dist/viem/Actions/dex.js.map +1 -1
- package/dist/viem/Actions/faucet.d.ts +34 -0
- package/dist/viem/Actions/faucet.d.ts.map +1 -0
- package/dist/viem/Actions/faucet.js +33 -0
- package/dist/viem/Actions/faucet.js.map +1 -0
- package/dist/viem/Actions/fee.d.ts +16 -30
- package/dist/viem/Actions/fee.d.ts.map +1 -1
- package/dist/viem/Actions/fee.js +13 -13
- package/dist/viem/Actions/fee.js.map +1 -1
- package/dist/viem/Actions/index.d.ts +2 -0
- package/dist/viem/Actions/index.d.ts.map +1 -1
- package/dist/viem/Actions/index.js +2 -0
- package/dist/viem/Actions/index.js.map +1 -1
- package/dist/viem/Actions/policy.d.ts +46 -46
- package/dist/viem/Actions/policy.js +46 -46
- package/dist/viem/Actions/reward.d.ts +3236 -0
- package/dist/viem/Actions/reward.d.ts.map +1 -0
- package/dist/viem/Actions/reward.js +725 -0
- package/dist/viem/Actions/reward.js.map +1 -0
- package/dist/viem/Actions/token.d.ts +4399 -2750
- package/dist/viem/Actions/token.d.ts.map +1 -1
- package/dist/viem/Actions/token.js +361 -482
- package/dist/viem/Actions/token.js.map +1 -1
- package/dist/viem/Addresses.d.ts +1 -2
- package/dist/viem/Addresses.d.ts.map +1 -1
- package/dist/viem/Addresses.js +1 -2
- package/dist/viem/Addresses.js.map +1 -1
- package/dist/viem/Chain.d.ts +38 -12
- package/dist/viem/Chain.d.ts.map +1 -1
- package/dist/viem/Chain.js +27 -18
- package/dist/viem/Chain.js.map +1 -1
- package/dist/viem/Decorator.d.ts +1009 -428
- package/dist/viem/Decorator.d.ts.map +1 -1
- package/dist/viem/Decorator.js +17 -5
- package/dist/viem/Decorator.js.map +1 -1
- package/dist/viem/Formatters.d.ts +8 -1
- package/dist/viem/Formatters.d.ts.map +1 -1
- package/dist/viem/Formatters.js +17 -0
- package/dist/viem/Formatters.js.map +1 -1
- package/dist/viem/P256.d.ts +2 -0
- package/dist/viem/P256.d.ts.map +1 -0
- package/dist/viem/P256.js +2 -0
- package/dist/viem/P256.js.map +1 -0
- package/dist/viem/Secp256k1.d.ts +2 -0
- package/dist/viem/Secp256k1.d.ts.map +1 -0
- package/dist/viem/Secp256k1.js +2 -0
- package/dist/viem/Secp256k1.js.map +1 -0
- package/dist/viem/TokenIds.d.ts +1 -2
- package/dist/viem/TokenIds.d.ts.map +1 -1
- package/dist/viem/TokenIds.js +1 -2
- package/dist/viem/TokenIds.js.map +1 -1
- package/dist/viem/Transaction.d.ts +1 -1
- package/dist/viem/Transaction.d.ts.map +1 -1
- package/dist/viem/Transaction.js +46 -5
- package/dist/viem/Transaction.js.map +1 -1
- package/dist/viem/WebAuthnP256.d.ts +79 -0
- package/dist/viem/WebAuthnP256.d.ts.map +1 -0
- package/dist/viem/WebAuthnP256.js +95 -0
- package/dist/viem/WebAuthnP256.js.map +1 -0
- package/dist/viem/WebCryptoP256.d.ts +2 -0
- package/dist/viem/WebCryptoP256.d.ts.map +1 -0
- package/dist/viem/WebCryptoP256.js +2 -0
- package/dist/viem/WebCryptoP256.js.map +1 -0
- package/dist/viem/index.d.ts +6 -3
- package/dist/viem/index.d.ts.map +1 -1
- package/dist/viem/index.js +6 -3
- package/dist/viem/index.js.map +1 -1
- package/dist/viem/internal/account.d.ts +24 -0
- package/dist/viem/internal/account.d.ts.map +1 -0
- package/dist/viem/internal/account.js +68 -0
- package/dist/viem/internal/account.js.map +1 -0
- package/dist/viem/internal/types.d.ts +10 -0
- package/dist/viem/internal/types.d.ts.map +1 -1
- package/dist/wagmi/Actions/amm.d.ts +428 -0
- package/dist/wagmi/Actions/amm.d.ts.map +1 -0
- package/dist/wagmi/Actions/amm.js +472 -0
- package/dist/wagmi/Actions/amm.js.map +1 -0
- package/dist/wagmi/Actions/dex.d.ts +908 -0
- package/dist/wagmi/Actions/dex.d.ts.map +1 -0
- package/dist/wagmi/Actions/dex.js +1023 -0
- package/dist/wagmi/Actions/dex.js.map +1 -0
- package/dist/wagmi/Actions/faucet.d.ts +35 -0
- package/dist/wagmi/Actions/faucet.d.ts.map +1 -0
- package/dist/wagmi/Actions/faucet.js +33 -0
- package/dist/wagmi/Actions/faucet.js.map +1 -0
- package/dist/wagmi/Actions/fee.d.ts +111 -0
- package/dist/wagmi/Actions/fee.d.ts.map +1 -0
- package/dist/wagmi/Actions/fee.js +126 -0
- package/dist/wagmi/Actions/fee.js.map +1 -0
- package/dist/wagmi/Actions/index.d.ts +7 -0
- package/dist/wagmi/Actions/index.d.ts.map +1 -0
- package/dist/wagmi/Actions/index.js +7 -0
- package/dist/wagmi/Actions/index.js.map +1 -0
- package/dist/wagmi/Actions/reward.d.ts +348 -0
- package/dist/wagmi/Actions/reward.d.ts.map +1 -0
- package/dist/wagmi/Actions/reward.js +388 -0
- package/dist/wagmi/Actions/reward.js.map +1 -0
- package/dist/wagmi/Actions/token.d.ts +1546 -0
- package/dist/wagmi/Actions/token.d.ts.map +1 -0
- package/dist/wagmi/Actions/token.js +1712 -0
- package/dist/wagmi/Actions/token.js.map +1 -0
- package/dist/wagmi/Connector.d.ts +81 -0
- package/dist/wagmi/Connector.d.ts.map +1 -0
- package/dist/wagmi/Connector.js +261 -0
- package/dist/wagmi/Connector.js.map +1 -0
- package/dist/wagmi/Hooks/amm.d.ts +421 -0
- package/dist/wagmi/Hooks/amm.d.ts.map +1 -0
- package/dist/wagmi/Hooks/amm.js +504 -0
- package/dist/wagmi/Hooks/amm.js.map +1 -0
- package/dist/wagmi/Hooks/dex.d.ts +816 -0
- package/dist/wagmi/Hooks/dex.d.ts.map +1 -0
- package/dist/wagmi/Hooks/dex.js +973 -0
- package/dist/wagmi/Hooks/dex.js.map +1 -0
- package/dist/wagmi/Hooks/faucet.d.ts +39 -0
- package/dist/wagmi/Hooks/faucet.d.ts.map +1 -0
- package/dist/wagmi/Hooks/faucet.js +40 -0
- package/dist/wagmi/Hooks/faucet.js.map +1 -0
- package/dist/wagmi/Hooks/fee.d.ts +97 -0
- package/dist/wagmi/Hooks/fee.d.ts.map +1 -0
- package/dist/wagmi/Hooks/fee.js +109 -0
- package/dist/wagmi/Hooks/fee.js.map +1 -0
- package/dist/wagmi/Hooks/index.d.ts +7 -0
- package/dist/wagmi/Hooks/index.d.ts.map +1 -0
- package/dist/wagmi/Hooks/index.js +7 -0
- package/dist/wagmi/Hooks/index.js.map +1 -0
- package/dist/wagmi/Hooks/reward.d.ts +307 -0
- package/dist/wagmi/Hooks/reward.d.ts.map +1 -0
- package/dist/wagmi/Hooks/reward.js +349 -0
- package/dist/wagmi/Hooks/reward.js.map +1 -0
- package/dist/wagmi/Hooks/token.d.ts +1388 -0
- package/dist/wagmi/Hooks/token.d.ts.map +1 -0
- package/dist/wagmi/Hooks/token.js +1657 -0
- package/dist/wagmi/Hooks/token.js.map +1 -0
- package/dist/wagmi/index.d.ts +4 -0
- package/dist/wagmi/index.d.ts.map +1 -0
- package/dist/wagmi/index.js +4 -0
- package/dist/wagmi/index.js.map +1 -0
- package/package.json +54 -10
- package/src/chains.ts +21 -9
- package/src/ox/Order.test.ts +78 -0
- package/src/ox/Order.ts +125 -0
- package/src/ox/OrdersFilters.test.ts +182 -0
- package/src/ox/OrdersFilters.ts +125 -0
- package/src/ox/Pagination.test.ts +162 -0
- package/src/ox/Pagination.ts +164 -0
- package/src/ox/PoolId.test.ts +33 -0
- package/src/ox/PoolId.ts +27 -0
- package/src/ox/RpcSchema.ts +35 -0
- package/src/ox/SignatureEnvelope.ts +3 -1
- package/src/{viem → ox}/Tick.test.ts +1 -1
- package/src/{viem → ox}/Tick.ts +5 -0
- package/src/ox/Transaction.test.ts +1 -1
- package/src/ox/Transaction.ts +2 -1
- package/src/ox/TransactionEnvelopeAA.test.ts +239 -96
- package/src/ox/TransactionEnvelopeAA.ts +9 -7
- package/src/ox/TransactionRequest.ts +4 -0
- package/src/ox/index.ts +6 -0
- package/src/prool/Instance.ts +51 -37
- package/src/prool/internal/chain.json +104 -52
- package/src/tsconfig.json +9 -0
- package/src/viem/Abis.ts +972 -710
- package/src/viem/Account.ts +279 -0
- package/src/viem/Actions/__snapshots__/dex.test.ts.snap +850 -0
- package/src/viem/Actions/amm.test.ts +173 -169
- package/src/viem/Actions/amm.ts +131 -203
- package/src/viem/Actions/dex.test.ts +563 -484
- package/src/viem/Actions/dex.ts +203 -30
- package/src/viem/Actions/faucet.ts +50 -0
- package/src/viem/Actions/fee.test.ts +23 -34
- package/src/viem/Actions/fee.ts +20 -13
- package/src/viem/Actions/index.ts +2 -0
- package/src/viem/Actions/policy.test.ts +19 -33
- package/src/viem/Actions/policy.ts +46 -46
- package/src/viem/Actions/reward.test.ts +457 -0
- package/src/viem/Actions/reward.ts +999 -0
- package/src/viem/Actions/token.test.ts +453 -287
- package/src/viem/Actions/token.ts +605 -693
- package/src/viem/Addresses.ts +1 -2
- package/src/viem/Chain.bench-d.ts +12 -0
- package/src/viem/Chain.ts +70 -20
- package/src/viem/Decorator.bench-d.ts +1 -1
- package/src/viem/Decorator.test.ts +3 -1
- package/src/viem/Decorator.ts +1049 -442
- package/src/viem/Formatters.ts +31 -5
- package/src/viem/P256.ts +1 -0
- package/src/viem/Secp256k1.ts +1 -0
- package/src/viem/TokenIds.ts +1 -2
- package/src/viem/Transaction.ts +53 -7
- package/src/viem/WebAuthnP256.ts +140 -0
- package/src/viem/WebCryptoP256.ts +1 -0
- package/src/viem/e2e.test.ts +1126 -297
- package/src/viem/index.ts +6 -3
- package/src/viem/internal/account.ts +107 -0
- package/src/viem/internal/types.ts +9 -0
- package/src/wagmi/Actions/__snapshots__/dex.test.ts.snap +310 -0
- package/src/wagmi/Actions/amm.test.ts +198 -0
- package/src/wagmi/Actions/amm.ts +691 -0
- package/src/wagmi/Actions/dex.test.ts +1507 -0
- package/src/wagmi/Actions/dex.ts +1640 -0
- package/src/wagmi/Actions/faucet.ts +46 -0
- package/src/wagmi/Actions/fee.test.ts +63 -0
- package/src/wagmi/Actions/fee.ts +208 -0
- package/src/wagmi/Actions/index.ts +6 -0
- package/src/wagmi/Actions/reward.test.ts +210 -0
- package/src/wagmi/Actions/reward.ts +632 -0
- package/src/wagmi/Actions/token.test.ts +1308 -0
- package/src/wagmi/Actions/token.ts +2613 -0
- package/src/wagmi/Connector.test.ts +53 -0
- package/src/wagmi/Connector.ts +390 -0
- package/src/wagmi/Hooks/__snapshots__/dex.test.ts.snap +457 -0
- package/src/wagmi/Hooks/amm.test.ts +424 -0
- package/src/wagmi/Hooks/amm.ts +806 -0
- package/src/wagmi/Hooks/dex.test.ts +1017 -0
- package/src/wagmi/Hooks/dex.ts +1685 -0
- package/src/wagmi/Hooks/faucet.ts +76 -0
- package/src/wagmi/Hooks/fee.test.ts +166 -0
- package/src/wagmi/Hooks/fee.ts +206 -0
- package/src/wagmi/Hooks/index.ts +6 -0
- package/src/wagmi/Hooks/reward.test.ts +219 -0
- package/src/wagmi/Hooks/reward.ts +672 -0
- package/src/wagmi/Hooks/token.test.ts +1670 -0
- package/src/wagmi/Hooks/token.ts +2906 -0
- package/src/wagmi/index.ts +3 -0
- package/src/wagmi/internal/types.ts +16 -0
- package/dist/viem/Client.d.ts +0 -27
- package/dist/viem/Client.d.ts.map +0 -1
- package/dist/viem/Client.js +0 -28
- package/dist/viem/Client.js.map +0 -1
- package/dist/viem/Tick.d.ts.map +0 -1
- package/dist/viem/Tick.js.map +0 -1
- package/src/viem/Client.bench-d.ts +0 -8
- package/src/viem/Client.test.ts +0 -178
- package/src/viem/Client.ts +0 -91
- /package/dist/{viem → ox}/Tick.js +0 -0
|
@@ -0,0 +1,1017 @@
|
|
|
1
|
+
import { Tick } from 'tempo.ts/viem'
|
|
2
|
+
import { Actions, Hooks } from 'tempo.ts/wagmi'
|
|
3
|
+
import { type Address, isAddress, parseUnits } from 'viem'
|
|
4
|
+
import { describe, expect, test, vi } from 'vitest'
|
|
5
|
+
import { useConnect } from 'wagmi'
|
|
6
|
+
import { accounts, addresses } from '../../../test/viem/config.js'
|
|
7
|
+
import {
|
|
8
|
+
config,
|
|
9
|
+
renderHook,
|
|
10
|
+
setupOrders,
|
|
11
|
+
setupTokenPair,
|
|
12
|
+
} from '../../../test/wagmi/config.js'
|
|
13
|
+
|
|
14
|
+
const account = accounts[0]
|
|
15
|
+
const account2 = accounts[1]
|
|
16
|
+
|
|
17
|
+
describe('useBuy', () => {
|
|
18
|
+
test('default', async () => {
|
|
19
|
+
const { base, quote } = await setupTokenPair()
|
|
20
|
+
|
|
21
|
+
const { result } = await renderHook(() => Hooks.dex.useBuySync())
|
|
22
|
+
|
|
23
|
+
// Place ask order to create liquidity
|
|
24
|
+
await Actions.dex.placeSync(config, {
|
|
25
|
+
token: base,
|
|
26
|
+
amount: parseUnits('500', 6),
|
|
27
|
+
type: 'sell',
|
|
28
|
+
tick: Tick.fromPrice('1.001'),
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// Buy base tokens with quote tokens
|
|
32
|
+
const { receipt } = await result.current.mutateAsync({
|
|
33
|
+
tokenIn: quote,
|
|
34
|
+
tokenOut: base,
|
|
35
|
+
amountOut: parseUnits('100', 6),
|
|
36
|
+
maxAmountIn: parseUnits('150', 6),
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
expect(receipt).toBeDefined()
|
|
40
|
+
expect(receipt.status).toBe('success')
|
|
41
|
+
|
|
42
|
+
await vi.waitFor(() => expect(result.current.isSuccess).toBeTruthy())
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('behavior: respects maxAmountIn', async () => {
|
|
46
|
+
const { base, quote } = await setupTokenPair()
|
|
47
|
+
|
|
48
|
+
const { result } = await renderHook(() => Hooks.dex.useBuySync())
|
|
49
|
+
|
|
50
|
+
// Place ask order at high price
|
|
51
|
+
await Actions.dex.placeSync(config, {
|
|
52
|
+
token: base,
|
|
53
|
+
amount: parseUnits('500', 6),
|
|
54
|
+
type: 'sell',
|
|
55
|
+
tick: Tick.fromPrice('1.01'), // 1% above peg
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
// Try to buy with insufficient maxAmountIn - should fail
|
|
59
|
+
await expect(
|
|
60
|
+
result.current.mutateAsync({
|
|
61
|
+
tokenIn: quote,
|
|
62
|
+
tokenOut: base,
|
|
63
|
+
amountOut: parseUnits('100', 6),
|
|
64
|
+
maxAmountIn: parseUnits('50', 6), // Way too low for 1% premium
|
|
65
|
+
}),
|
|
66
|
+
).rejects.toThrow('The contract function "swapExactAmountOut" reverted')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('behavior: fails with insufficient liquidity', async () => {
|
|
70
|
+
const { base, quote } = await setupTokenPair()
|
|
71
|
+
|
|
72
|
+
const { result } = await renderHook(() => Hooks.dex.useBuySync())
|
|
73
|
+
|
|
74
|
+
// Don't place any orders - no liquidity
|
|
75
|
+
|
|
76
|
+
// Try to buy - should fail due to no liquidity
|
|
77
|
+
await expect(
|
|
78
|
+
result.current.mutateAsync({
|
|
79
|
+
tokenIn: quote,
|
|
80
|
+
tokenOut: base,
|
|
81
|
+
amountOut: parseUnits('100', 6),
|
|
82
|
+
maxAmountIn: parseUnits('150', 6),
|
|
83
|
+
}),
|
|
84
|
+
).rejects.toThrow('The contract function "swapExactAmountOut" reverted')
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
describe('useCancel', () => {
|
|
89
|
+
test('default', async () => {
|
|
90
|
+
const { base, quote } = await setupTokenPair()
|
|
91
|
+
|
|
92
|
+
const { result } = await renderHook(() => ({
|
|
93
|
+
place: Hooks.dex.usePlaceSync(),
|
|
94
|
+
cancel: Hooks.dex.useCancelSync(),
|
|
95
|
+
}))
|
|
96
|
+
|
|
97
|
+
// Place a bid order
|
|
98
|
+
const { orderId } = await result.current.place.mutateAsync({
|
|
99
|
+
token: base,
|
|
100
|
+
amount: parseUnits('100', 6),
|
|
101
|
+
type: 'buy',
|
|
102
|
+
tick: Tick.fromPrice('1.001'),
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
// Check initial DEX balance (should be 0)
|
|
106
|
+
const dexBalanceBefore = await Actions.dex.getBalance(config, {
|
|
107
|
+
account: account.address,
|
|
108
|
+
token: quote,
|
|
109
|
+
})
|
|
110
|
+
expect(dexBalanceBefore).toBe(0n)
|
|
111
|
+
|
|
112
|
+
// Cancel the order
|
|
113
|
+
const { receipt, orderId: returnedOrderId } =
|
|
114
|
+
await result.current.cancel.mutateAsync({
|
|
115
|
+
orderId,
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
expect(receipt).toBeDefined()
|
|
119
|
+
expect(receipt.status).toBe('success')
|
|
120
|
+
expect(returnedOrderId).toBe(orderId)
|
|
121
|
+
|
|
122
|
+
await vi.waitFor(() => expect(result.current.cancel.isSuccess).toBeTruthy())
|
|
123
|
+
|
|
124
|
+
// Check DEX balance after cancel - tokens should be refunded to internal balance
|
|
125
|
+
const dexBalanceAfter = await Actions.dex.getBalance(config, {
|
|
126
|
+
account: account.address,
|
|
127
|
+
token: quote,
|
|
128
|
+
})
|
|
129
|
+
expect(dexBalanceAfter).toBeGreaterThan(0n)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test('behavior: only maker can cancel', async () => {
|
|
133
|
+
const { base } = await setupTokenPair()
|
|
134
|
+
|
|
135
|
+
const { result } = await renderHook(() => ({
|
|
136
|
+
connect: useConnect(),
|
|
137
|
+
place: Hooks.dex.usePlaceSync(),
|
|
138
|
+
cancel: Hooks.dex.useCancelSync(),
|
|
139
|
+
}))
|
|
140
|
+
|
|
141
|
+
// Account places order
|
|
142
|
+
const { orderId } = await result.current.place.mutateAsync({
|
|
143
|
+
token: base,
|
|
144
|
+
amount: parseUnits('100', 6),
|
|
145
|
+
type: 'buy',
|
|
146
|
+
tick: Tick.fromPrice('1.001'),
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
// Transfer gas to account2
|
|
150
|
+
await Actions.token.transferSync(config, {
|
|
151
|
+
to: account2.address,
|
|
152
|
+
amount: parseUnits('1', 6),
|
|
153
|
+
token: addresses.alphaUsd,
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
// Use a different account via the connector
|
|
157
|
+
await result.current.connect.connectAsync({
|
|
158
|
+
connector: config.connectors[1]!,
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
// Account2 tries to cancel - should fail
|
|
162
|
+
await expect(
|
|
163
|
+
result.current.cancel.mutateAsync({
|
|
164
|
+
orderId,
|
|
165
|
+
}),
|
|
166
|
+
).rejects.toThrow('The contract function "cancel" reverted')
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
test('behavior: cannot cancel non-existent order', async () => {
|
|
170
|
+
await setupTokenPair()
|
|
171
|
+
|
|
172
|
+
const { result } = await renderHook(() => Hooks.dex.useCancelSync())
|
|
173
|
+
|
|
174
|
+
// Try to cancel an order that doesn't exist
|
|
175
|
+
await expect(
|
|
176
|
+
result.current.mutateAsync({
|
|
177
|
+
orderId: 999n,
|
|
178
|
+
}),
|
|
179
|
+
).rejects.toThrow('The contract function "cancel" reverted')
|
|
180
|
+
})
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
describe('useCreatePair', () => {
|
|
184
|
+
test('default', async () => {
|
|
185
|
+
await setupTokenPair() // This ensures connection
|
|
186
|
+
|
|
187
|
+
const { result } = await renderHook(() => Hooks.dex.useCreatePairSync())
|
|
188
|
+
|
|
189
|
+
const { token: baseToken } = await Actions.token.createSync(config, {
|
|
190
|
+
name: 'Test Base Token',
|
|
191
|
+
symbol: 'BASE',
|
|
192
|
+
currency: 'USD',
|
|
193
|
+
admin: account,
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
const { receipt, ...resultData } = await result.current.mutateAsync({
|
|
197
|
+
base: baseToken,
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
expect(receipt).toBeDefined()
|
|
201
|
+
expect(receipt.status).toBe('success')
|
|
202
|
+
|
|
203
|
+
const { key, ...rest } = resultData
|
|
204
|
+
expect(key).toBeDefined()
|
|
205
|
+
expect(rest).toEqual(
|
|
206
|
+
expect.objectContaining({
|
|
207
|
+
base: expect.toSatisfy(isAddress),
|
|
208
|
+
quote: expect.toSatisfy(isAddress),
|
|
209
|
+
}),
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
await vi.waitFor(() => expect(result.current.isSuccess).toBeTruthy())
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
describe('useBalance', () => {
|
|
217
|
+
test('default', async () => {
|
|
218
|
+
const { base, quote } = await setupTokenPair()
|
|
219
|
+
|
|
220
|
+
const { result, rerender } = await renderHook(
|
|
221
|
+
(props) =>
|
|
222
|
+
Hooks.dex.useBalance({ account: props?.account, token: quote }),
|
|
223
|
+
{ initialProps: { account: undefined as Address | undefined } },
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
await vi.waitFor(() => result.current.fetchStatus === 'fetching')
|
|
227
|
+
|
|
228
|
+
// Verify initial state (disabled/pending when account missing)
|
|
229
|
+
expect(result.current.status).toBe('pending')
|
|
230
|
+
|
|
231
|
+
// Set account and rerender
|
|
232
|
+
rerender({ account: accounts[0].address })
|
|
233
|
+
|
|
234
|
+
await vi.waitFor(() => expect(result.current.isSuccess).toBeTruthy())
|
|
235
|
+
|
|
236
|
+
// Initial balance should be 0
|
|
237
|
+
expect(result.current.data).toBe(0n)
|
|
238
|
+
|
|
239
|
+
// Place and cancel order to create internal balance
|
|
240
|
+
const { orderId } = await Actions.dex.placeSync(config, {
|
|
241
|
+
token: base,
|
|
242
|
+
amount: parseUnits('50', 6),
|
|
243
|
+
type: 'buy',
|
|
244
|
+
tick: Tick.fromPrice('1.0005'),
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
await Actions.dex.cancelSync(config, {
|
|
248
|
+
orderId,
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
// Trigger refetch and verify updated balance
|
|
252
|
+
const { data } = await result.current.refetch()
|
|
253
|
+
expect(data).toBeGreaterThan(0n)
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
test('behavior: check different account', async () => {
|
|
257
|
+
const { quote } = await setupTokenPair()
|
|
258
|
+
|
|
259
|
+
const { result } = await renderHook(() =>
|
|
260
|
+
Hooks.dex.useBalance({ account: account2.address, token: quote }),
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
await vi.waitFor(() => expect(result.current.isSuccess).toBeTruthy())
|
|
264
|
+
|
|
265
|
+
// Check account2's balance (should be 0)
|
|
266
|
+
expect(result.current.data).toBe(0n)
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
test('behavior: balances are per-token', async () => {
|
|
270
|
+
const { base, quote } = await setupTokenPair()
|
|
271
|
+
|
|
272
|
+
// Create balance in quote token
|
|
273
|
+
const { orderId } = await Actions.dex.placeSync(config, {
|
|
274
|
+
token: base,
|
|
275
|
+
amount: parseUnits('100', 6),
|
|
276
|
+
type: 'buy',
|
|
277
|
+
tick: Tick.fromPrice('1.001'),
|
|
278
|
+
})
|
|
279
|
+
await Actions.dex.cancelSync(config, { orderId })
|
|
280
|
+
|
|
281
|
+
const { result } = await renderHook(() => ({
|
|
282
|
+
quote: Hooks.dex.useBalance({ account: account.address, token: quote }),
|
|
283
|
+
base: Hooks.dex.useBalance({ account: account.address, token: base }),
|
|
284
|
+
}))
|
|
285
|
+
|
|
286
|
+
await vi.waitUntil(
|
|
287
|
+
() => result.current.base.isSuccess && result.current.quote.isSuccess,
|
|
288
|
+
{
|
|
289
|
+
timeout: 50_000,
|
|
290
|
+
},
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
// Check quote balance (should have refunded tokens)
|
|
294
|
+
expect(result.current.quote.data).toBeGreaterThan(0n)
|
|
295
|
+
// Check base balance (should still be 0)
|
|
296
|
+
expect(result.current.base.data).toBe(0n)
|
|
297
|
+
})
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
describe('useBuyQuote', () => {
|
|
301
|
+
test('default', async () => {
|
|
302
|
+
const { base, quote } = await setupTokenPair()
|
|
303
|
+
|
|
304
|
+
// Place ask orders to create liquidity
|
|
305
|
+
await Actions.dex.placeSync(config, {
|
|
306
|
+
token: base,
|
|
307
|
+
amount: parseUnits('500', 6),
|
|
308
|
+
type: 'sell',
|
|
309
|
+
tick: Tick.fromPrice('1.001'),
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
const { result } = await renderHook(() =>
|
|
313
|
+
Hooks.dex.useBuyQuote({
|
|
314
|
+
tokenIn: quote,
|
|
315
|
+
tokenOut: base,
|
|
316
|
+
amountOut: parseUnits('100', 6),
|
|
317
|
+
}),
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
await vi.waitFor(() => expect(result.current.isSuccess).toBeTruthy())
|
|
321
|
+
|
|
322
|
+
expect(result.current.data).toBeGreaterThan(0n)
|
|
323
|
+
// Should be approximately 100 * 1.001 = 100.1
|
|
324
|
+
expect(result.current.data).toBeGreaterThan(parseUnits('100', 6))
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
test('behavior: fails with no liquidity', async () => {
|
|
328
|
+
const { base, quote } = await setupTokenPair()
|
|
329
|
+
|
|
330
|
+
// No orders placed - no liquidity
|
|
331
|
+
|
|
332
|
+
const { result } = await renderHook(() =>
|
|
333
|
+
Hooks.dex.useBuyQuote({
|
|
334
|
+
tokenIn: quote,
|
|
335
|
+
tokenOut: base,
|
|
336
|
+
amountOut: parseUnits('100', 6),
|
|
337
|
+
}),
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
await vi.waitUntil(() => result.current.isError, {
|
|
341
|
+
timeout: 50_000,
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
expect(result.current.error?.message).toContain('InsufficientLiquidity')
|
|
345
|
+
})
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
describe('useOrder', () => {
|
|
349
|
+
test('default', async () => {
|
|
350
|
+
const { base } = await setupTokenPair()
|
|
351
|
+
|
|
352
|
+
// Place an order to get an order ID
|
|
353
|
+
const { orderId } = await Actions.dex.placeSync(config, {
|
|
354
|
+
token: base,
|
|
355
|
+
amount: parseUnits('100', 6),
|
|
356
|
+
type: 'buy',
|
|
357
|
+
tick: Tick.fromPrice('1.001'),
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
const { result } = await renderHook(() =>
|
|
361
|
+
Hooks.dex.useOrder({
|
|
362
|
+
orderId,
|
|
363
|
+
}),
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
await vi.waitFor(() => expect(result.current.isSuccess).toBeTruthy())
|
|
367
|
+
|
|
368
|
+
const order = result.current.data!
|
|
369
|
+
expect(order.maker).toBe(account.address)
|
|
370
|
+
expect(order.isBid).toBe(true)
|
|
371
|
+
expect(order.tick).toBe(Tick.fromPrice('1.001'))
|
|
372
|
+
expect(order.amount).toBe(parseUnits('100', 6))
|
|
373
|
+
expect(order.remaining).toBe(parseUnits('100', 6))
|
|
374
|
+
expect(order.isFlip).toBe(false)
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
test('behavior: returns flip order details', async () => {
|
|
378
|
+
const { base } = await setupTokenPair()
|
|
379
|
+
|
|
380
|
+
// Place a flip order
|
|
381
|
+
const { orderId } = await Actions.dex.placeFlipSync(config, {
|
|
382
|
+
token: base,
|
|
383
|
+
amount: parseUnits('50', 6),
|
|
384
|
+
type: 'buy',
|
|
385
|
+
tick: Tick.fromPrice('1.001'),
|
|
386
|
+
flipTick: Tick.fromPrice('1.002'),
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
const { result } = await renderHook(() =>
|
|
390
|
+
Hooks.dex.useOrder({
|
|
391
|
+
orderId,
|
|
392
|
+
}),
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
await vi.waitFor(() => expect(result.current.isSuccess).toBeTruthy())
|
|
396
|
+
|
|
397
|
+
const order = result.current.data!
|
|
398
|
+
expect(order.maker).toBe(account.address)
|
|
399
|
+
expect(order.isBid).toBe(true)
|
|
400
|
+
expect(order.tick).toBe(Tick.fromPrice('1.001'))
|
|
401
|
+
expect(order.amount).toBe(parseUnits('50', 6))
|
|
402
|
+
expect(order.isFlip).toBe(true)
|
|
403
|
+
expect(order.flipTick).toBe(Tick.fromPrice('1.002'))
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
test('behavior: fails for non-existent order', async () => {
|
|
407
|
+
await setupTokenPair()
|
|
408
|
+
|
|
409
|
+
const { result } = await renderHook(() =>
|
|
410
|
+
Hooks.dex.useOrder({
|
|
411
|
+
orderId: 999n,
|
|
412
|
+
}),
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
await vi.waitUntil(() => result.current.isError, {
|
|
416
|
+
timeout: 50_000,
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
expect(result.current.error?.message).toContain('OrderDoesNotExist')
|
|
420
|
+
})
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
describe('useGetOrders', () => {
|
|
424
|
+
test('default', async () => {
|
|
425
|
+
await setupOrders()
|
|
426
|
+
|
|
427
|
+
const { result } = await renderHook(() =>
|
|
428
|
+
Hooks.dex.useGetOrders({
|
|
429
|
+
limit: 10,
|
|
430
|
+
}),
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
await vi.waitFor(() => expect(result.current.isSuccess).toBeTruthy())
|
|
434
|
+
|
|
435
|
+
expect(result.current.data).matchSnapshot()
|
|
436
|
+
|
|
437
|
+
// fetch next page
|
|
438
|
+
result.current.fetchNextPage()
|
|
439
|
+
|
|
440
|
+
await vi.waitFor(() => {
|
|
441
|
+
expect(result.current.data?.pages.length).toBe(2)
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
expect(result.current.data).matchSnapshot()
|
|
445
|
+
})
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
describe('useOrderbook', () => {
|
|
449
|
+
test('default', async () => {
|
|
450
|
+
const { base, quote } = await setupTokenPair()
|
|
451
|
+
|
|
452
|
+
const { result } = await renderHook(() =>
|
|
453
|
+
Hooks.dex.useOrderbook({
|
|
454
|
+
base,
|
|
455
|
+
quote,
|
|
456
|
+
}),
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
await vi.waitFor(() => expect(result.current.isSuccess).toBeTruthy())
|
|
460
|
+
|
|
461
|
+
const book = result.current.data!
|
|
462
|
+
expect(book.base).toBe(base)
|
|
463
|
+
expect(book.quote).toBe(quote)
|
|
464
|
+
expect(book.bestBidTick).toBeDefined()
|
|
465
|
+
expect(book.bestAskTick).toBeDefined()
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
test('behavior: shows best bid and ask after orders placed', async () => {
|
|
469
|
+
const { base, quote } = await setupTokenPair()
|
|
470
|
+
|
|
471
|
+
const bidTick = Tick.fromPrice('0.999')
|
|
472
|
+
const askTick = Tick.fromPrice('1.001')
|
|
473
|
+
|
|
474
|
+
// Place a bid order
|
|
475
|
+
await Actions.dex.placeSync(config, {
|
|
476
|
+
token: base,
|
|
477
|
+
amount: parseUnits('100', 6),
|
|
478
|
+
type: 'buy',
|
|
479
|
+
tick: bidTick,
|
|
480
|
+
})
|
|
481
|
+
|
|
482
|
+
// Place an ask order
|
|
483
|
+
await Actions.dex.placeSync(config, {
|
|
484
|
+
token: base,
|
|
485
|
+
amount: parseUnits('100', 6),
|
|
486
|
+
type: 'sell',
|
|
487
|
+
tick: askTick,
|
|
488
|
+
})
|
|
489
|
+
|
|
490
|
+
const { result } = await renderHook(() =>
|
|
491
|
+
Hooks.dex.useOrderbook({
|
|
492
|
+
base,
|
|
493
|
+
quote,
|
|
494
|
+
}),
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
await vi.waitFor(() => expect(result.current.isSuccess).toBeTruthy())
|
|
498
|
+
|
|
499
|
+
const book = result.current.data!
|
|
500
|
+
expect(book.bestBidTick).toBe(bidTick)
|
|
501
|
+
expect(book.bestAskTick).toBe(askTick)
|
|
502
|
+
})
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
describe('usePriceLevel', () => {
|
|
506
|
+
test('default', async () => {
|
|
507
|
+
const { base } = await setupTokenPair()
|
|
508
|
+
|
|
509
|
+
const tick = Tick.fromPrice('1.001')
|
|
510
|
+
|
|
511
|
+
// Place an order to create liquidity at this tick
|
|
512
|
+
const { orderId } = await Actions.dex.placeSync(config, {
|
|
513
|
+
token: base,
|
|
514
|
+
amount: parseUnits('100', 6),
|
|
515
|
+
type: 'buy',
|
|
516
|
+
tick,
|
|
517
|
+
})
|
|
518
|
+
|
|
519
|
+
const { result } = await renderHook(() =>
|
|
520
|
+
Hooks.dex.usePriceLevel({
|
|
521
|
+
base,
|
|
522
|
+
tick,
|
|
523
|
+
isBid: true,
|
|
524
|
+
}),
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
await vi.waitFor(() => expect(result.current.isSuccess).toBeTruthy())
|
|
528
|
+
|
|
529
|
+
const level = result.current.data!
|
|
530
|
+
expect(level.head).toBe(orderId) // head should be our order
|
|
531
|
+
expect(level.tail).toBe(orderId) // tail should also be our order (only one)
|
|
532
|
+
expect(level.totalLiquidity).toBeGreaterThan(0n)
|
|
533
|
+
})
|
|
534
|
+
|
|
535
|
+
test('behavior: empty price level', async () => {
|
|
536
|
+
const { base } = await setupTokenPair()
|
|
537
|
+
|
|
538
|
+
const tick = Tick.fromPrice('1.001')
|
|
539
|
+
|
|
540
|
+
// Query a tick with no orders
|
|
541
|
+
const { result } = await renderHook(() =>
|
|
542
|
+
Hooks.dex.usePriceLevel({
|
|
543
|
+
base,
|
|
544
|
+
tick,
|
|
545
|
+
isBid: true,
|
|
546
|
+
}),
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
await vi.waitFor(() => expect(result.current.isSuccess).toBeTruthy())
|
|
550
|
+
|
|
551
|
+
const level = result.current.data!
|
|
552
|
+
expect(level.head).toBe(0n)
|
|
553
|
+
expect(level.tail).toBe(0n)
|
|
554
|
+
expect(level.totalLiquidity).toBe(0n)
|
|
555
|
+
})
|
|
556
|
+
})
|
|
557
|
+
|
|
558
|
+
describe('useSellQuote', () => {
|
|
559
|
+
test('default', async () => {
|
|
560
|
+
const { base, quote } = await setupTokenPair()
|
|
561
|
+
|
|
562
|
+
// Place bid orders to create liquidity
|
|
563
|
+
await Actions.dex.placeSync(config, {
|
|
564
|
+
token: base,
|
|
565
|
+
amount: parseUnits('500', 6),
|
|
566
|
+
type: 'buy',
|
|
567
|
+
tick: Tick.fromPrice('0.999'),
|
|
568
|
+
})
|
|
569
|
+
|
|
570
|
+
const { result } = await renderHook(() =>
|
|
571
|
+
Hooks.dex.useSellQuote({
|
|
572
|
+
tokenIn: base,
|
|
573
|
+
tokenOut: quote,
|
|
574
|
+
amountIn: parseUnits('100', 6),
|
|
575
|
+
}),
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
await vi.waitFor(() => expect(result.current.isSuccess).toBeTruthy())
|
|
579
|
+
|
|
580
|
+
expect(result.current.data).toBeGreaterThan(0n)
|
|
581
|
+
// Should be approximately 100 * 0.999 = 99.9
|
|
582
|
+
expect(result.current.data).toBeLessThan(parseUnits('100', 6))
|
|
583
|
+
})
|
|
584
|
+
|
|
585
|
+
test('behavior: fails with no liquidity', async () => {
|
|
586
|
+
const { base, quote } = await setupTokenPair()
|
|
587
|
+
|
|
588
|
+
// Quote should fail with no liquidity
|
|
589
|
+
const { result } = await renderHook(() =>
|
|
590
|
+
Hooks.dex.useSellQuote({
|
|
591
|
+
tokenIn: base,
|
|
592
|
+
tokenOut: quote,
|
|
593
|
+
amountIn: parseUnits('100', 6),
|
|
594
|
+
}),
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
await vi.waitUntil(() => result.current.isError, {
|
|
598
|
+
timeout: 50_000,
|
|
599
|
+
})
|
|
600
|
+
|
|
601
|
+
expect(result.current.error?.message).toContain('InsufficientLiquidity')
|
|
602
|
+
})
|
|
603
|
+
})
|
|
604
|
+
|
|
605
|
+
describe('usePlace', () => {
|
|
606
|
+
test('default', async () => {
|
|
607
|
+
const { base } = await setupTokenPair()
|
|
608
|
+
|
|
609
|
+
const { result } = await renderHook(() => Hooks.dex.usePlaceSync())
|
|
610
|
+
|
|
611
|
+
// Place a sell order
|
|
612
|
+
const { receipt, orderId, token, ...resultData } =
|
|
613
|
+
await result.current.mutateAsync({
|
|
614
|
+
token: base,
|
|
615
|
+
amount: parseUnits('100', 6),
|
|
616
|
+
type: 'sell',
|
|
617
|
+
tick: Tick.fromPrice('1.001'),
|
|
618
|
+
})
|
|
619
|
+
|
|
620
|
+
expect(receipt).toBeDefined()
|
|
621
|
+
expect(receipt.status).toBe('success')
|
|
622
|
+
expect(orderId).toBeGreaterThan(0n)
|
|
623
|
+
expect(token).toBe(base)
|
|
624
|
+
expect(resultData).toMatchInlineSnapshot(`
|
|
625
|
+
{
|
|
626
|
+
"amount": 100000000n,
|
|
627
|
+
"isBid": false,
|
|
628
|
+
"maker": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
|
|
629
|
+
"tick": 100,
|
|
630
|
+
}
|
|
631
|
+
`)
|
|
632
|
+
|
|
633
|
+
await vi.waitFor(() => expect(result.current.isSuccess).toBeTruthy())
|
|
634
|
+
|
|
635
|
+
// Place a buy order
|
|
636
|
+
const {
|
|
637
|
+
receipt: receipt2,
|
|
638
|
+
orderId: orderId2,
|
|
639
|
+
token: token2,
|
|
640
|
+
...result2
|
|
641
|
+
} = await result.current.mutateAsync({
|
|
642
|
+
token: base,
|
|
643
|
+
amount: parseUnits('100', 6),
|
|
644
|
+
type: 'buy',
|
|
645
|
+
tick: Tick.fromPrice('1.001'),
|
|
646
|
+
})
|
|
647
|
+
expect(receipt2.status).toBe('success')
|
|
648
|
+
expect(orderId2).toBeGreaterThan(0n)
|
|
649
|
+
expect(token2).toBe(base)
|
|
650
|
+
expect(result2).toMatchInlineSnapshot(`
|
|
651
|
+
{
|
|
652
|
+
"amount": 100000000n,
|
|
653
|
+
"isBid": true,
|
|
654
|
+
"maker": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
|
|
655
|
+
"tick": 100,
|
|
656
|
+
}
|
|
657
|
+
`)
|
|
658
|
+
})
|
|
659
|
+
})
|
|
660
|
+
|
|
661
|
+
describe('usePlaceFlip', () => {
|
|
662
|
+
test('default', async () => {
|
|
663
|
+
const { base } = await setupTokenPair()
|
|
664
|
+
|
|
665
|
+
const { result } = await renderHook(() => Hooks.dex.usePlaceFlipSync())
|
|
666
|
+
|
|
667
|
+
// Place a flip bid order
|
|
668
|
+
const { receipt, orderId, token, ...resultData } =
|
|
669
|
+
await result.current.mutateAsync({
|
|
670
|
+
token: base,
|
|
671
|
+
amount: parseUnits('100', 6),
|
|
672
|
+
type: 'buy',
|
|
673
|
+
tick: Tick.fromPrice('1.001'),
|
|
674
|
+
flipTick: Tick.fromPrice('1.002'),
|
|
675
|
+
})
|
|
676
|
+
|
|
677
|
+
expect(receipt).toBeDefined()
|
|
678
|
+
expect(receipt.status).toBe('success')
|
|
679
|
+
expect(orderId).toBeGreaterThan(0n)
|
|
680
|
+
expect(token).toBe(base)
|
|
681
|
+
expect(resultData.flipTick).toBe(Tick.fromPrice('1.002'))
|
|
682
|
+
expect(resultData).toMatchInlineSnapshot(`
|
|
683
|
+
{
|
|
684
|
+
"amount": 100000000n,
|
|
685
|
+
"flipTick": 200,
|
|
686
|
+
"isBid": true,
|
|
687
|
+
"maker": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
|
|
688
|
+
"tick": 100,
|
|
689
|
+
}
|
|
690
|
+
`)
|
|
691
|
+
|
|
692
|
+
await vi.waitFor(() => expect(result.current.isSuccess).toBeTruthy())
|
|
693
|
+
})
|
|
694
|
+
})
|
|
695
|
+
|
|
696
|
+
describe('useSell', () => {
|
|
697
|
+
test('default', async () => {
|
|
698
|
+
const { base, quote } = await setupTokenPair()
|
|
699
|
+
|
|
700
|
+
const { result } = await renderHook(() => Hooks.dex.useSellSync())
|
|
701
|
+
|
|
702
|
+
// Place bid order to create liquidity
|
|
703
|
+
await Actions.dex.placeSync(config, {
|
|
704
|
+
token: base,
|
|
705
|
+
amount: parseUnits('500', 6),
|
|
706
|
+
type: 'buy',
|
|
707
|
+
tick: Tick.fromPrice('0.999'),
|
|
708
|
+
})
|
|
709
|
+
|
|
710
|
+
// Sell base tokens
|
|
711
|
+
const { receipt } = await result.current.mutateAsync({
|
|
712
|
+
tokenIn: base,
|
|
713
|
+
tokenOut: quote,
|
|
714
|
+
amountIn: parseUnits('100', 6),
|
|
715
|
+
minAmountOut: parseUnits('50', 6),
|
|
716
|
+
})
|
|
717
|
+
|
|
718
|
+
expect(receipt).toBeDefined()
|
|
719
|
+
expect(receipt.status).toBe('success')
|
|
720
|
+
|
|
721
|
+
await vi.waitFor(() => expect(result.current.isSuccess).toBeTruthy())
|
|
722
|
+
})
|
|
723
|
+
|
|
724
|
+
test('behavior: respects minAmountOut', async () => {
|
|
725
|
+
const { base, quote } = await setupTokenPair()
|
|
726
|
+
|
|
727
|
+
const { result } = await renderHook(() => Hooks.dex.useSellSync())
|
|
728
|
+
|
|
729
|
+
// Place bid order at low price
|
|
730
|
+
await Actions.dex.placeSync(config, {
|
|
731
|
+
token: base,
|
|
732
|
+
amount: parseUnits('500', 6),
|
|
733
|
+
type: 'buy',
|
|
734
|
+
tick: Tick.fromPrice('0.99'), // 1% below peg
|
|
735
|
+
})
|
|
736
|
+
|
|
737
|
+
// Try to sell with too high minAmountOut - should fail
|
|
738
|
+
await expect(
|
|
739
|
+
result.current.mutateAsync({
|
|
740
|
+
tokenIn: base,
|
|
741
|
+
tokenOut: quote,
|
|
742
|
+
amountIn: parseUnits('100', 6),
|
|
743
|
+
minAmountOut: parseUnits('150', 6), // Way too high
|
|
744
|
+
}),
|
|
745
|
+
).rejects.toThrow('The contract function "swapExactAmountIn" reverted')
|
|
746
|
+
})
|
|
747
|
+
|
|
748
|
+
test('behavior: fails with insufficient liquidity', async () => {
|
|
749
|
+
const { base, quote } = await setupTokenPair()
|
|
750
|
+
|
|
751
|
+
const { result } = await renderHook(() => Hooks.dex.useSellSync())
|
|
752
|
+
|
|
753
|
+
// No orders - no liquidity
|
|
754
|
+
|
|
755
|
+
// Try to sell - should fail
|
|
756
|
+
await expect(
|
|
757
|
+
result.current.mutateAsync({
|
|
758
|
+
tokenIn: base,
|
|
759
|
+
tokenOut: quote,
|
|
760
|
+
amountIn: parseUnits('100', 6),
|
|
761
|
+
minAmountOut: parseUnits('50', 6),
|
|
762
|
+
}),
|
|
763
|
+
).rejects.toThrow('The contract function "swapExactAmountIn" reverted')
|
|
764
|
+
})
|
|
765
|
+
})
|
|
766
|
+
|
|
767
|
+
describe('useWatchFlipOrderPlaced', () => {
|
|
768
|
+
test('default', async () => {
|
|
769
|
+
const { base } = await setupTokenPair()
|
|
770
|
+
|
|
771
|
+
const { result } = await renderHook(() => Hooks.dex.usePlaceFlipSync())
|
|
772
|
+
|
|
773
|
+
const events: any[] = []
|
|
774
|
+
await renderHook(() =>
|
|
775
|
+
Hooks.dex.useWatchFlipOrderPlaced({
|
|
776
|
+
onFlipOrderPlaced(args) {
|
|
777
|
+
events.push(args)
|
|
778
|
+
},
|
|
779
|
+
}),
|
|
780
|
+
)
|
|
781
|
+
|
|
782
|
+
// Place flip order to trigger event
|
|
783
|
+
await result.current.mutateAsync({
|
|
784
|
+
token: base,
|
|
785
|
+
amount: parseUnits('100', 6),
|
|
786
|
+
type: 'buy',
|
|
787
|
+
tick: Tick.fromPrice('1.001'),
|
|
788
|
+
flipTick: Tick.fromPrice('1.002'),
|
|
789
|
+
})
|
|
790
|
+
|
|
791
|
+
await vi.waitUntil(() => events.length >= 1)
|
|
792
|
+
|
|
793
|
+
expect(events.length).toBeGreaterThanOrEqual(1)
|
|
794
|
+
expect(events[0]?.flipTick).toBe(Tick.fromPrice('1.002'))
|
|
795
|
+
expect(events[0]?.tick).toBe(Tick.fromPrice('1.001'))
|
|
796
|
+
expect(events[0]?.isBid).toBe(true)
|
|
797
|
+
expect(events[0]?.amount).toBe(parseUnits('100', 6))
|
|
798
|
+
})
|
|
799
|
+
})
|
|
800
|
+
|
|
801
|
+
describe('useWatchOrderCancelled', () => {
|
|
802
|
+
test('default', async () => {
|
|
803
|
+
const { base } = await setupTokenPair()
|
|
804
|
+
|
|
805
|
+
// Place order first
|
|
806
|
+
const { orderId } = await Actions.dex.placeSync(config, {
|
|
807
|
+
token: base,
|
|
808
|
+
amount: parseUnits('100', 6),
|
|
809
|
+
type: 'buy',
|
|
810
|
+
tick: Tick.fromPrice('1.001'),
|
|
811
|
+
})
|
|
812
|
+
|
|
813
|
+
const events: any[] = []
|
|
814
|
+
await renderHook(() =>
|
|
815
|
+
Hooks.dex.useWatchOrderCancelled({
|
|
816
|
+
onOrderCancelled(args) {
|
|
817
|
+
events.push(args)
|
|
818
|
+
},
|
|
819
|
+
}),
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
// Cancel order to trigger event
|
|
823
|
+
await Actions.dex.cancelSync(config, {
|
|
824
|
+
orderId,
|
|
825
|
+
})
|
|
826
|
+
|
|
827
|
+
await vi.waitUntil(() => events.length >= 1)
|
|
828
|
+
|
|
829
|
+
expect(events.length).toBeGreaterThanOrEqual(1)
|
|
830
|
+
expect(events[0]?.orderId).toBe(orderId)
|
|
831
|
+
})
|
|
832
|
+
|
|
833
|
+
test('behavior: filter by orderId', async () => {
|
|
834
|
+
const { base } = await setupTokenPair()
|
|
835
|
+
|
|
836
|
+
// Place two orders
|
|
837
|
+
const { orderId: orderId1 } = await Actions.dex.placeSync(config, {
|
|
838
|
+
token: base,
|
|
839
|
+
amount: parseUnits('100', 6),
|
|
840
|
+
type: 'buy',
|
|
841
|
+
tick: Tick.fromPrice('1.001'),
|
|
842
|
+
})
|
|
843
|
+
|
|
844
|
+
const { orderId: orderId2 } = await Actions.dex.placeSync(config, {
|
|
845
|
+
token: base,
|
|
846
|
+
amount: parseUnits('50', 6),
|
|
847
|
+
type: 'buy',
|
|
848
|
+
tick: Tick.fromPrice('1.001'),
|
|
849
|
+
})
|
|
850
|
+
|
|
851
|
+
const events: any[] = []
|
|
852
|
+
await renderHook(() =>
|
|
853
|
+
Hooks.dex.useWatchOrderCancelled({
|
|
854
|
+
orderId: orderId1, // Filter for only orderId1
|
|
855
|
+
onOrderCancelled(args) {
|
|
856
|
+
events.push(args)
|
|
857
|
+
},
|
|
858
|
+
}),
|
|
859
|
+
)
|
|
860
|
+
|
|
861
|
+
// Cancel orderId1 (should be captured)
|
|
862
|
+
await Actions.dex.cancelSync(config, {
|
|
863
|
+
orderId: orderId1,
|
|
864
|
+
})
|
|
865
|
+
|
|
866
|
+
// Cancel orderId2 (should NOT be captured due to filter)
|
|
867
|
+
await Actions.dex.cancelSync(config, {
|
|
868
|
+
orderId: orderId2,
|
|
869
|
+
})
|
|
870
|
+
|
|
871
|
+
await vi.waitUntil(() => events.length >= 1, { timeout: 2000 })
|
|
872
|
+
|
|
873
|
+
// Should only receive 1 event (for orderId1)
|
|
874
|
+
expect(events.length).toBe(1)
|
|
875
|
+
expect(events[0]?.orderId).toBe(orderId1)
|
|
876
|
+
})
|
|
877
|
+
})
|
|
878
|
+
|
|
879
|
+
describe.todo('useWatchOrderFilled')
|
|
880
|
+
|
|
881
|
+
describe('useWatchOrderPlaced', () => {
|
|
882
|
+
test('default', async () => {
|
|
883
|
+
const { base } = await setupTokenPair()
|
|
884
|
+
|
|
885
|
+
const events: any[] = []
|
|
886
|
+
await renderHook(() =>
|
|
887
|
+
Hooks.dex.useWatchOrderPlaced({
|
|
888
|
+
onOrderPlaced(args) {
|
|
889
|
+
events.push(args)
|
|
890
|
+
},
|
|
891
|
+
}),
|
|
892
|
+
)
|
|
893
|
+
|
|
894
|
+
// Place first order
|
|
895
|
+
await Actions.dex.placeSync(config, {
|
|
896
|
+
token: base,
|
|
897
|
+
amount: parseUnits('100', 6),
|
|
898
|
+
type: 'buy',
|
|
899
|
+
tick: Tick.fromPrice('1.001'),
|
|
900
|
+
})
|
|
901
|
+
|
|
902
|
+
// Place second order
|
|
903
|
+
await Actions.dex.placeSync(config, {
|
|
904
|
+
token: base,
|
|
905
|
+
amount: parseUnits('50', 6),
|
|
906
|
+
type: 'sell',
|
|
907
|
+
tick: Tick.fromPrice('0.999'),
|
|
908
|
+
})
|
|
909
|
+
|
|
910
|
+
await vi.waitUntil(() => events.length >= 2)
|
|
911
|
+
|
|
912
|
+
expect(events.length).toBeGreaterThanOrEqual(2)
|
|
913
|
+
expect(events[0]?.isBid).toBe(true)
|
|
914
|
+
expect(events[0]?.amount).toBe(parseUnits('100', 6))
|
|
915
|
+
expect(events[1]?.isBid).toBe(false)
|
|
916
|
+
expect(events[1]?.amount).toBe(parseUnits('50', 6))
|
|
917
|
+
})
|
|
918
|
+
|
|
919
|
+
test('behavior: filter by token', async () => {
|
|
920
|
+
const { base } = await setupTokenPair()
|
|
921
|
+
const { base: base2 } = await setupTokenPair()
|
|
922
|
+
|
|
923
|
+
const events: any[] = []
|
|
924
|
+
await renderHook(() =>
|
|
925
|
+
Hooks.dex.useWatchOrderPlaced({
|
|
926
|
+
token: base, // Filter for only base token
|
|
927
|
+
onOrderPlaced(args) {
|
|
928
|
+
events.push(args)
|
|
929
|
+
},
|
|
930
|
+
}),
|
|
931
|
+
)
|
|
932
|
+
|
|
933
|
+
// Place order on base (should be captured)
|
|
934
|
+
await Actions.dex.placeSync(config, {
|
|
935
|
+
token: base,
|
|
936
|
+
amount: parseUnits('100', 6),
|
|
937
|
+
type: 'buy',
|
|
938
|
+
tick: Tick.fromPrice('1.001'),
|
|
939
|
+
})
|
|
940
|
+
|
|
941
|
+
// Place order on base2 (should NOT be captured due to filter)
|
|
942
|
+
await Actions.dex.placeSync(config, {
|
|
943
|
+
token: base2,
|
|
944
|
+
amount: parseUnits('50', 6),
|
|
945
|
+
type: 'buy',
|
|
946
|
+
tick: Tick.fromPrice('1.001'),
|
|
947
|
+
})
|
|
948
|
+
|
|
949
|
+
await vi.waitUntil(() => events.length >= 1, { timeout: 2000 })
|
|
950
|
+
|
|
951
|
+
// Should only receive 1 event (for base token)
|
|
952
|
+
expect(events.length).toBe(1)
|
|
953
|
+
expect(events[0]?.token.toLowerCase()).toBe(base.toLowerCase())
|
|
954
|
+
})
|
|
955
|
+
})
|
|
956
|
+
|
|
957
|
+
describe('useWithdraw', () => {
|
|
958
|
+
test('default', async () => {
|
|
959
|
+
const { base, quote } = await setupTokenPair()
|
|
960
|
+
|
|
961
|
+
const { result } = await renderHook(() => ({
|
|
962
|
+
place: Hooks.dex.usePlaceSync(),
|
|
963
|
+
cancel: Hooks.dex.useCancelSync(),
|
|
964
|
+
withdraw: Hooks.dex.useWithdrawSync(),
|
|
965
|
+
}))
|
|
966
|
+
|
|
967
|
+
// Create internal balance
|
|
968
|
+
const { orderId } = await result.current.place.mutateAsync({
|
|
969
|
+
token: base,
|
|
970
|
+
amount: parseUnits('100', 6),
|
|
971
|
+
type: 'buy',
|
|
972
|
+
tick: Tick.fromPrice('1.001'),
|
|
973
|
+
})
|
|
974
|
+
|
|
975
|
+
await result.current.cancel.mutateAsync({ orderId })
|
|
976
|
+
|
|
977
|
+
// Get DEX balance
|
|
978
|
+
const dexBalance = await Actions.dex.getBalance(config, {
|
|
979
|
+
account: account.address,
|
|
980
|
+
token: quote,
|
|
981
|
+
})
|
|
982
|
+
expect(dexBalance).toBeGreaterThan(0n)
|
|
983
|
+
|
|
984
|
+
// Get wallet balance before withdraw
|
|
985
|
+
const walletBalanceBefore = await Actions.token.getBalance(config, {
|
|
986
|
+
token: quote,
|
|
987
|
+
account: account.address,
|
|
988
|
+
})
|
|
989
|
+
|
|
990
|
+
// Withdraw from DEX
|
|
991
|
+
const { receipt } = await result.current.withdraw.mutateAsync({
|
|
992
|
+
token: quote,
|
|
993
|
+
amount: dexBalance,
|
|
994
|
+
})
|
|
995
|
+
|
|
996
|
+
expect(receipt).toBeDefined()
|
|
997
|
+
expect(receipt.status).toBe('success')
|
|
998
|
+
|
|
999
|
+
await vi.waitFor(() =>
|
|
1000
|
+
expect(result.current.withdraw.isSuccess).toBeTruthy(),
|
|
1001
|
+
)
|
|
1002
|
+
|
|
1003
|
+
// Check DEX balance is now 0
|
|
1004
|
+
const dexBalanceAfter = await Actions.dex.getBalance(config, {
|
|
1005
|
+
account: account.address,
|
|
1006
|
+
token: quote,
|
|
1007
|
+
})
|
|
1008
|
+
expect(dexBalanceAfter).toBe(0n)
|
|
1009
|
+
|
|
1010
|
+
// Check wallet balance increased
|
|
1011
|
+
const walletBalanceAfter = await Actions.token.getBalance(config, {
|
|
1012
|
+
token: quote,
|
|
1013
|
+
account: account.address,
|
|
1014
|
+
})
|
|
1015
|
+
expect(walletBalanceAfter).toBeGreaterThan(walletBalanceBefore)
|
|
1016
|
+
})
|
|
1017
|
+
})
|