tempo.ts 0.11.0 → 0.12.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.
Files changed (196) hide show
  1. package/CHANGELOG.md +67 -4
  2. package/README.md +3 -34
  3. package/dist/server/Handler.d.ts +14 -14
  4. package/dist/server/Handler.d.ts.map +1 -1
  5. package/dist/server/Handler.js +16 -17
  6. package/dist/server/Handler.js.map +1 -1
  7. package/dist/wagmi/Actions/amm.d.ts +51 -51
  8. package/dist/wagmi/Actions/amm.d.ts.map +1 -1
  9. package/dist/wagmi/Actions/amm.js +37 -37
  10. package/dist/wagmi/Actions/amm.js.map +1 -1
  11. package/dist/wagmi/Actions/dex.d.ts +129 -129
  12. package/dist/wagmi/Actions/dex.d.ts.map +1 -1
  13. package/dist/wagmi/Actions/dex.js +73 -73
  14. package/dist/wagmi/Actions/dex.js.map +1 -1
  15. package/dist/wagmi/Actions/faucet.d.ts +9 -9
  16. package/dist/wagmi/Actions/faucet.d.ts.map +1 -1
  17. package/dist/wagmi/Actions/faucet.js +7 -7
  18. package/dist/wagmi/Actions/faucet.js.map +1 -1
  19. package/dist/wagmi/Actions/fee.d.ts +17 -17
  20. package/dist/wagmi/Actions/fee.d.ts.map +1 -1
  21. package/dist/wagmi/Actions/fee.js +10 -10
  22. package/dist/wagmi/Actions/fee.js.map +1 -1
  23. package/dist/wagmi/Actions/nonce.d.ts +9 -79
  24. package/dist/wagmi/Actions/nonce.d.ts.map +1 -1
  25. package/dist/wagmi/Actions/nonce.js +7 -89
  26. package/dist/wagmi/Actions/nonce.js.map +1 -1
  27. package/dist/wagmi/Actions/policy.d.ts +69 -70
  28. package/dist/wagmi/Actions/policy.d.ts.map +1 -1
  29. package/dist/wagmi/Actions/policy.js +43 -43
  30. package/dist/wagmi/Actions/policy.js.map +1 -1
  31. package/dist/wagmi/Actions/reward.d.ts +51 -51
  32. package/dist/wagmi/Actions/reward.d.ts.map +1 -1
  33. package/dist/wagmi/Actions/reward.js +31 -31
  34. package/dist/wagmi/Actions/reward.js.map +1 -1
  35. package/dist/wagmi/Actions/token.d.ts +238 -238
  36. package/dist/wagmi/Actions/token.d.ts.map +1 -1
  37. package/dist/wagmi/Actions/token.js +136 -136
  38. package/dist/wagmi/Actions/token.js.map +1 -1
  39. package/dist/wagmi/Connector.d.ts +2 -1
  40. package/dist/wagmi/Connector.d.ts.map +1 -1
  41. package/dist/wagmi/Connector.js +83 -22
  42. package/dist/wagmi/Connector.js.map +1 -1
  43. package/dist/wagmi/Hooks/nonce.d.ts +1 -52
  44. package/dist/wagmi/Hooks/nonce.d.ts.map +1 -1
  45. package/dist/wagmi/Hooks/nonce.js +1 -70
  46. package/dist/wagmi/Hooks/nonce.js.map +1 -1
  47. package/dist/wagmi/Hooks/policy.d.ts +0 -1
  48. package/dist/wagmi/Hooks/policy.d.ts.map +1 -1
  49. package/dist/wagmi/Hooks/policy.js.map +1 -1
  50. package/dist/wagmi/KeyManager.d.ts +6 -3
  51. package/dist/wagmi/KeyManager.d.ts.map +1 -1
  52. package/dist/wagmi/KeyManager.js +9 -4
  53. package/dist/wagmi/KeyManager.js.map +1 -1
  54. package/package.json +2 -2
  55. package/src/server/Handler.test.ts +2 -2
  56. package/src/server/Handler.ts +16 -17
  57. package/src/wagmi/Actions/amm.ts +63 -63
  58. package/src/wagmi/Actions/dex.test.ts +1 -1
  59. package/src/wagmi/Actions/dex.ts +153 -153
  60. package/src/wagmi/Actions/faucet.ts +11 -11
  61. package/src/wagmi/Actions/fee.ts +20 -20
  62. package/src/wagmi/Actions/nonce.test.ts +1 -64
  63. package/src/wagmi/Actions/nonce.ts +10 -142
  64. package/src/wagmi/Actions/policy.ts +83 -85
  65. package/src/wagmi/Actions/reward.ts +64 -61
  66. package/src/wagmi/Actions/token.ts +287 -283
  67. package/src/wagmi/Connector.ts +105 -31
  68. package/src/wagmi/Hooks/dex.test.ts +1 -1
  69. package/src/wagmi/Hooks/fee.test.ts +0 -6
  70. package/src/wagmi/Hooks/nonce.test.ts +1 -66
  71. package/src/wagmi/Hooks/nonce.ts +1 -114
  72. package/src/wagmi/Hooks/policy.ts +0 -2
  73. package/src/wagmi/KeyManager.ts +18 -5
  74. package/dist/viem/Abis.d.ts +0 -2649
  75. package/dist/viem/Abis.d.ts.map +0 -1
  76. package/dist/viem/Abis.js +0 -1677
  77. package/dist/viem/Abis.js.map +0 -1
  78. package/dist/viem/Account.d.ts +0 -244
  79. package/dist/viem/Account.d.ts.map +0 -1
  80. package/dist/viem/Account.js +0 -382
  81. package/dist/viem/Account.js.map +0 -1
  82. package/dist/viem/Actions/amm.d.ts +0 -1991
  83. package/dist/viem/Actions/amm.d.ts.map +0 -1
  84. package/dist/viem/Actions/amm.js +0 -814
  85. package/dist/viem/Actions/amm.js.map +0 -1
  86. package/dist/viem/Actions/dex.d.ts +0 -3900
  87. package/dist/viem/Actions/dex.d.ts.map +0 -1
  88. package/dist/viem/Actions/dex.js +0 -1414
  89. package/dist/viem/Actions/dex.js.map +0 -1
  90. package/dist/viem/Actions/faucet.d.ts +0 -69
  91. package/dist/viem/Actions/faucet.d.ts.map +0 -1
  92. package/dist/viem/Actions/faucet.js +0 -73
  93. package/dist/viem/Actions/faucet.js.map +0 -1
  94. package/dist/viem/Actions/fee.d.ts +0 -360
  95. package/dist/viem/Actions/fee.d.ts.map +0 -1
  96. package/dist/viem/Actions/fee.js +0 -237
  97. package/dist/viem/Actions/fee.js.map +0 -1
  98. package/dist/viem/Actions/nonce.d.ts +0 -257
  99. package/dist/viem/Actions/nonce.d.ts.map +0 -1
  100. package/dist/viem/Actions/nonce.js +0 -228
  101. package/dist/viem/Actions/nonce.js.map +0 -1
  102. package/dist/viem/Actions/policy.d.ts +0 -1680
  103. package/dist/viem/Actions/policy.d.ts.map +0 -1
  104. package/dist/viem/Actions/policy.js +0 -875
  105. package/dist/viem/Actions/policy.js.map +0 -1
  106. package/dist/viem/Actions/reward.d.ts +0 -2422
  107. package/dist/viem/Actions/reward.d.ts.map +0 -1
  108. package/dist/viem/Actions/reward.js +0 -651
  109. package/dist/viem/Actions/reward.js.map +0 -1
  110. package/dist/viem/Actions/token.d.ts +0 -16007
  111. package/dist/viem/Actions/token.d.ts.map +0 -1
  112. package/dist/viem/Actions/token.js +0 -2936
  113. package/dist/viem/Actions/token.js.map +0 -1
  114. package/dist/viem/Addresses.d.ts +0 -9
  115. package/dist/viem/Addresses.d.ts.map +0 -1
  116. package/dist/viem/Addresses.js +0 -9
  117. package/dist/viem/Addresses.js.map +0 -1
  118. package/dist/viem/Chain.d.ts +0 -451
  119. package/dist/viem/Chain.d.ts.map +0 -1
  120. package/dist/viem/Chain.js +0 -96
  121. package/dist/viem/Chain.js.map +0 -1
  122. package/dist/viem/Formatters.d.ts +0 -10
  123. package/dist/viem/Formatters.d.ts.map +0 -1
  124. package/dist/viem/Formatters.js +0 -104
  125. package/dist/viem/Formatters.js.map +0 -1
  126. package/dist/viem/Storage.d.ts +0 -24
  127. package/dist/viem/Storage.d.ts.map +0 -1
  128. package/dist/viem/Storage.js +0 -68
  129. package/dist/viem/Storage.js.map +0 -1
  130. package/dist/viem/Transaction.d.ts +0 -76
  131. package/dist/viem/Transaction.d.ts.map +0 -1
  132. package/dist/viem/Transaction.js +0 -176
  133. package/dist/viem/Transaction.js.map +0 -1
  134. package/dist/viem/Transport.d.ts +0 -33
  135. package/dist/viem/Transport.d.ts.map +0 -1
  136. package/dist/viem/Transport.js +0 -138
  137. package/dist/viem/Transport.js.map +0 -1
  138. package/dist/viem/WebAuthnP256.d.ts +0 -82
  139. package/dist/viem/WebAuthnP256.d.ts.map +0 -1
  140. package/dist/viem/WebAuthnP256.js +0 -97
  141. package/dist/viem/WebAuthnP256.js.map +0 -1
  142. package/dist/viem/WebCryptoP256.d.ts +0 -2
  143. package/dist/viem/WebCryptoP256.d.ts.map +0 -1
  144. package/dist/viem/WebCryptoP256.js +0 -2
  145. package/dist/viem/WebCryptoP256.js.map +0 -1
  146. package/dist/viem/internal/types.d.ts +0 -20
  147. package/dist/viem/internal/types.d.ts.map +0 -1
  148. package/dist/viem/internal/types.js +0 -2
  149. package/dist/viem/internal/types.js.map +0 -1
  150. package/dist/viem/internal/utils.d.ts +0 -14
  151. package/dist/viem/internal/utils.d.ts.map +0 -1
  152. package/dist/viem/internal/utils.js +0 -33
  153. package/dist/viem/internal/utils.js.map +0 -1
  154. package/src/chains.ts +0 -54
  155. package/src/viem/Abis.ts +0 -1688
  156. package/src/viem/Account.test.ts +0 -444
  157. package/src/viem/Account.ts +0 -601
  158. package/src/viem/Actions/account.test.ts +0 -414
  159. package/src/viem/Actions/account.ts +0 -106
  160. package/src/viem/Actions/amm.test.ts +0 -381
  161. package/src/viem/Actions/amm.ts +0 -1227
  162. package/src/viem/Actions/dex.test.ts +0 -1549
  163. package/src/viem/Actions/dex.ts +0 -2150
  164. package/src/viem/Actions/faucet.ts +0 -121
  165. package/src/viem/Actions/fee.test.ts +0 -259
  166. package/src/viem/Actions/fee.ts +0 -372
  167. package/src/viem/Actions/index.ts +0 -9
  168. package/src/viem/Actions/nonce.test.ts +0 -206
  169. package/src/viem/Actions/nonce.ts +0 -347
  170. package/src/viem/Actions/policy.test.ts +0 -534
  171. package/src/viem/Actions/policy.ts +0 -1335
  172. package/src/viem/Actions/reward.test.ts +0 -434
  173. package/src/viem/Actions/reward.ts +0 -944
  174. package/src/viem/Actions/token.test.ts +0 -3029
  175. package/src/viem/Actions/token.ts +0 -4458
  176. package/src/viem/Addresses.ts +0 -9
  177. package/src/viem/Chain.bench-d.ts +0 -12
  178. package/src/viem/Chain.test.ts +0 -168
  179. package/src/viem/Chain.ts +0 -157
  180. package/src/viem/Decorator.bench-d.ts +0 -11
  181. package/src/viem/Decorator.test.ts +0 -39
  182. package/src/viem/Decorator.ts +0 -3179
  183. package/src/viem/Formatters.ts +0 -164
  184. package/src/viem/P256.ts +0 -1
  185. package/src/viem/Secp256k1.ts +0 -1
  186. package/src/viem/Storage.ts +0 -110
  187. package/src/viem/TokenIds.ts +0 -1
  188. package/src/viem/Transaction.ts +0 -382
  189. package/src/viem/Transport.ts +0 -191
  190. package/src/viem/WebAuthnP256.ts +0 -146
  191. package/src/viem/WebCryptoP256.ts +0 -1
  192. package/src/viem/e2e.test.ts +0 -1602
  193. package/src/viem/index.ts +0 -30
  194. package/src/viem/internal/types.ts +0 -69
  195. package/src/viem/internal/utils.ts +0 -58
  196. package/src/wagmi/internal/types.ts +0 -16
@@ -1,1549 +0,0 @@
1
- import { Actions, Tick } from 'tempo.ts/viem'
2
- import { parseUnits } from 'viem'
3
- import { describe, expect, test } from 'vitest'
4
- import {
5
- accounts,
6
- clientWithAccount,
7
- setupTokenPair,
8
- } from '../../../test/viem/config.js'
9
-
10
- describe('buy', () => {
11
- test('default', async () => {
12
- const { base, quote } = await setupTokenPair(clientWithAccount)
13
-
14
- // Place ask order to create liquidity
15
- await Actions.dex.placeSync(clientWithAccount, {
16
- token: base,
17
- amount: parseUnits('500', 6),
18
- type: 'sell',
19
- tick: Tick.fromPrice('1.001'),
20
- })
21
-
22
- // Get initial balances
23
- const baseBalanceBefore = await Actions.token.getBalance(
24
- clientWithAccount,
25
- {
26
- token: base,
27
- },
28
- )
29
-
30
- // Buy base tokens with quote tokens
31
- const { receipt } = await Actions.dex.buySync(clientWithAccount, {
32
- tokenIn: quote,
33
- tokenOut: base,
34
- amountOut: parseUnits('100', 6),
35
- maxAmountIn: parseUnits('150', 6),
36
- })
37
-
38
- expect(receipt).toBeDefined()
39
- expect(receipt.status).toBe('success')
40
-
41
- // Verify balances changed
42
- const baseBalanceAfter = await Actions.token.getBalance(clientWithAccount, {
43
- token: base,
44
- })
45
-
46
- // Should have received base tokens
47
- expect(baseBalanceAfter).toBeGreaterThan(baseBalanceBefore)
48
- })
49
-
50
- test('behavior: respects maxAmountIn', async () => {
51
- const { base, quote } = await setupTokenPair(clientWithAccount)
52
-
53
- // Place ask order at high price
54
- await Actions.dex.placeSync(clientWithAccount, {
55
- token: base,
56
- amount: parseUnits('500', 6),
57
- type: 'sell',
58
- tick: Tick.fromPrice('1.01'), // 1% above peg
59
- })
60
-
61
- // Try to buy with insufficient maxAmountIn - should fail
62
- await expect(
63
- Actions.dex.buySync(clientWithAccount, {
64
- tokenIn: quote,
65
- tokenOut: base,
66
- amountOut: parseUnits('100', 6),
67
- maxAmountIn: parseUnits('50', 6), // Way too low for 1% premium
68
- }),
69
- ).rejects.toThrow('The contract function "swapExactAmountOut" reverted')
70
- })
71
-
72
- test('behavior: fails with insufficient liquidity', async () => {
73
- const { base, quote } = await setupTokenPair(clientWithAccount)
74
-
75
- // Don't place any orders - no liquidity
76
-
77
- // Try to buy - should fail due to no liquidity
78
- await expect(
79
- Actions.dex.buySync(clientWithAccount, {
80
- tokenIn: quote,
81
- tokenOut: base,
82
- amountOut: parseUnits('100', 6),
83
- maxAmountIn: parseUnits('150', 6),
84
- }),
85
- ).rejects.toThrow('The contract function "swapExactAmountOut" reverted')
86
- })
87
- })
88
-
89
- describe('cancel', () => {
90
- test('default', async () => {
91
- const { base, quote } = await setupTokenPair(clientWithAccount)
92
-
93
- // Place a bid order
94
- const { orderId } = await Actions.dex.placeSync(clientWithAccount, {
95
- token: base,
96
- amount: parseUnits('100', 6),
97
- type: 'buy',
98
- tick: Tick.fromPrice('1.001'),
99
- })
100
-
101
- // Check initial DEX balance (should be 0)
102
- const dexBalanceBefore = await Actions.dex.getBalance(clientWithAccount, {
103
- account: clientWithAccount.account.address,
104
- token: quote,
105
- })
106
- expect(dexBalanceBefore).toBe(0n)
107
-
108
- // Cancel the order
109
- const { receipt, orderId: returnedOrderId } = await Actions.dex.cancelSync(
110
- clientWithAccount,
111
- {
112
- orderId,
113
- },
114
- )
115
-
116
- expect(receipt).toBeDefined()
117
- expect(receipt.status).toBe('success')
118
- expect(returnedOrderId).toBe(orderId)
119
-
120
- // Check DEX balance after cancel - tokens should be refunded to internal balance
121
- const dexBalanceAfter = await Actions.dex.getBalance(clientWithAccount, {
122
- account: clientWithAccount.account.address,
123
- token: quote,
124
- })
125
- expect(dexBalanceAfter).toBeGreaterThan(0n)
126
- })
127
-
128
- test('behavior: only maker can cancel', async () => {
129
- const { base } = await setupTokenPair(clientWithAccount)
130
-
131
- // Account places order
132
- const { orderId } = await Actions.dex.placeSync(clientWithAccount, {
133
- token: base,
134
- amount: parseUnits('100', 6),
135
- type: 'buy',
136
- tick: Tick.fromPrice('1.001'),
137
- })
138
-
139
- // Create another account
140
- const account2 = accounts[1]
141
-
142
- // Transfer gas to account2
143
- await Actions.token.transferSync(clientWithAccount, {
144
- to: account2.address,
145
- amount: parseUnits('1', 6),
146
- token: 1n,
147
- })
148
-
149
- // Account2 tries to cancel - should fail
150
- await expect(
151
- Actions.dex.cancelSync(clientWithAccount, {
152
- account: account2,
153
- orderId,
154
- }),
155
- ).rejects.toThrow('The contract function "cancel" reverted')
156
- })
157
-
158
- test('behavior: cannot cancel non-existent order', async () => {
159
- await setupTokenPair(clientWithAccount)
160
-
161
- // Try to cancel an order that doesn't exist
162
- await expect(
163
- Actions.dex.cancelSync(clientWithAccount, {
164
- orderId: 999n,
165
- }),
166
- ).rejects.toThrow('The contract function "cancel" reverted')
167
- })
168
- })
169
-
170
- describe('createPair', () => {
171
- test('default', async () => {
172
- const { token: baseToken } = await Actions.token.createSync(
173
- clientWithAccount,
174
- {
175
- name: 'Test Base Token',
176
- symbol: 'BASE',
177
- currency: 'USD',
178
- },
179
- )
180
-
181
- const { receipt, key, base, quote } = await Actions.dex.createPairSync(
182
- clientWithAccount,
183
- {
184
- base: baseToken,
185
- },
186
- )
187
-
188
- expect(receipt).toBeDefined()
189
- expect(receipt.status).toBe('success')
190
- expect(key).toBeDefined()
191
- expect(base).toBe(baseToken)
192
- expect(quote).toBeDefined()
193
- })
194
- })
195
-
196
- describe('getBalance', () => {
197
- test('default', async () => {
198
- const { base, quote } = await setupTokenPair(clientWithAccount)
199
-
200
- // Initial balance should be 0
201
- const initialBalance = await Actions.dex.getBalance(clientWithAccount, {
202
- account: clientWithAccount.account.address,
203
- token: quote,
204
- })
205
- expect(initialBalance).toBe(0n)
206
-
207
- // Place and cancel order to create internal balance
208
- const { orderId } = await Actions.dex.placeSync(clientWithAccount, {
209
- token: base,
210
- amount: parseUnits('50', 6),
211
- type: 'buy',
212
- tick: Tick.fromPrice('1.0005'),
213
- })
214
-
215
- await Actions.dex.cancelSync(clientWithAccount, {
216
- orderId,
217
- })
218
-
219
- // Now balance should be > 0 (refunded quote tokens)
220
- const balance = await Actions.dex.getBalance(clientWithAccount, {
221
- account: clientWithAccount.account.address,
222
- token: quote,
223
- })
224
- expect(balance).toBeGreaterThan(0n)
225
- })
226
-
227
- test('behavior: check different account', async () => {
228
- const { quote } = await setupTokenPair(clientWithAccount)
229
-
230
- const account2 = accounts[1]
231
-
232
- // Check account2's balance (should be 0)
233
- const balance = await Actions.dex.getBalance(clientWithAccount, {
234
- account: account2.address,
235
- token: quote,
236
- })
237
- expect(balance).toBe(0n)
238
- })
239
-
240
- test('behavior: balances are per-token', async () => {
241
- const { base, quote } = await setupTokenPair(clientWithAccount)
242
-
243
- // Create balance in quote token
244
- const { orderId } = await Actions.dex.placeSync(clientWithAccount, {
245
- token: base,
246
- amount: parseUnits('100', 6),
247
- type: 'buy',
248
- tick: Tick.fromPrice('1.001'),
249
- })
250
- await Actions.dex.cancelSync(clientWithAccount, { orderId })
251
-
252
- // Check quote balance (should have refunded tokens)
253
- const quoteBalance = await Actions.dex.getBalance(clientWithAccount, {
254
- account: clientWithAccount.account.address,
255
- token: quote,
256
- })
257
- expect(quoteBalance).toBeGreaterThan(0n)
258
-
259
- // Check base balance (should still be 0)
260
- const baseBalance = await Actions.dex.getBalance(clientWithAccount, {
261
- account: clientWithAccount.account.address,
262
- token: base,
263
- })
264
- expect(baseBalance).toBe(0n)
265
- })
266
- })
267
-
268
- describe('getBuyQuote', () => {
269
- test('default', async () => {
270
- const { base, quote } = await setupTokenPair(clientWithAccount)
271
-
272
- // Place ask orders to create liquidity
273
- await Actions.dex.placeSync(clientWithAccount, {
274
- token: base,
275
- amount: parseUnits('500', 6),
276
- type: 'sell',
277
- tick: Tick.fromPrice('1.001'),
278
- })
279
-
280
- // Get quote for buying base tokens
281
- const amountIn = await Actions.dex.getBuyQuote(clientWithAccount, {
282
- tokenIn: quote,
283
- tokenOut: base,
284
- amountOut: parseUnits('100', 6),
285
- })
286
-
287
- expect(amountIn).toBeGreaterThan(0n)
288
- // Should be approximately 100 * 1.001 = 100.1
289
- expect(amountIn).toBeGreaterThan(parseUnits('100', 6))
290
- })
291
-
292
- test('behavior: fails with no liquidity', async () => {
293
- const { base, quote } = await setupTokenPair(clientWithAccount)
294
-
295
- // No orders placed - no liquidity
296
-
297
- // Quote should fail
298
- await expect(
299
- Actions.dex.getBuyQuote(clientWithAccount, {
300
- tokenIn: quote,
301
- tokenOut: base,
302
- amountOut: parseUnits('100', 6),
303
- }),
304
- ).rejects.toThrow(
305
- 'The contract function "quoteSwapExactAmountOut" reverted',
306
- )
307
- })
308
- })
309
-
310
- describe('getOrder', () => {
311
- test('default', async () => {
312
- const { base } = await setupTokenPair(clientWithAccount)
313
-
314
- // Place an order to get an order ID
315
- const { orderId } = await Actions.dex.placeSync(clientWithAccount, {
316
- token: base,
317
- amount: parseUnits('100', 6),
318
- type: 'buy',
319
- tick: Tick.fromPrice('1.001'),
320
- })
321
-
322
- // Get the order details
323
- const order = await Actions.dex.getOrder(clientWithAccount, {
324
- orderId,
325
- })
326
-
327
- expect(order).toBeDefined()
328
- expect(order.maker).toBe(clientWithAccount.account.address)
329
- expect(order.isBid).toBe(true)
330
- expect(order.tick).toBe(Tick.fromPrice('1.001'))
331
- expect(order.amount).toBe(parseUnits('100', 6))
332
- expect(order.remaining).toBe(parseUnits('100', 6))
333
- expect(order.isFlip).toBe(false)
334
- })
335
-
336
- test('behavior: returns flip order details', async () => {
337
- const { base } = await setupTokenPair(clientWithAccount)
338
-
339
- // Place a flip order
340
- const { orderId } = await Actions.dex.placeFlipSync(clientWithAccount, {
341
- token: base,
342
- amount: parseUnits('50', 6),
343
- type: 'buy',
344
- tick: Tick.fromPrice('1.001'),
345
- flipTick: Tick.fromPrice('1.002'),
346
- })
347
-
348
- // Get the order details
349
- const order = await Actions.dex.getOrder(clientWithAccount, {
350
- orderId,
351
- })
352
-
353
- expect(order).toBeDefined()
354
- expect(order.maker).toBe(clientWithAccount.account.address)
355
- expect(order.isBid).toBe(true)
356
- expect(order.tick).toBe(Tick.fromPrice('1.001'))
357
- expect(order.amount).toBe(parseUnits('50', 6))
358
- expect(order.isFlip).toBe(true)
359
- expect(order.flipTick).toBe(Tick.fromPrice('1.002'))
360
- })
361
-
362
- test('behavior: fails for non-existent order', async () => {
363
- await setupTokenPair(clientWithAccount)
364
-
365
- // Try to get an order that doesn't exist
366
- await expect(
367
- Actions.dex.getOrder(clientWithAccount, {
368
- orderId: 999n,
369
- }),
370
- ).rejects.toThrow('The contract function "getOrder" reverted')
371
- })
372
-
373
- test('behavior: reflects order state after partial fill', async () => {
374
- const { base, quote } = await setupTokenPair(clientWithAccount)
375
-
376
- // Place a large sell order
377
- const { orderId } = await Actions.dex.placeSync(clientWithAccount, {
378
- token: base,
379
- amount: parseUnits('500', 6),
380
- type: 'sell',
381
- tick: Tick.fromPrice('1.001'),
382
- })
383
-
384
- // Get initial order state
385
- const orderBefore = await Actions.dex.getOrder(clientWithAccount, {
386
- orderId,
387
- })
388
- expect(orderBefore.amount).toBe(parseUnits('500', 6))
389
- expect(orderBefore.remaining).toBe(parseUnits('500', 6))
390
-
391
- // Partially fill the order with a buy
392
- await Actions.dex.buySync(clientWithAccount, {
393
- tokenIn: quote,
394
- tokenOut: base,
395
- amountOut: parseUnits('100', 6),
396
- maxAmountIn: parseUnits('150', 6),
397
- })
398
-
399
- // Get order state after partial fill
400
- const orderAfter = await Actions.dex.getOrder(clientWithAccount, {
401
- orderId,
402
- })
403
- expect(orderAfter.amount).toBe(parseUnits('500', 6)) // amount unchanged
404
- expect(orderAfter.remaining).toBeLessThan(parseUnits('500', 6)) // remaining decreased
405
- })
406
-
407
- test('behavior: linked list pointers for multiple orders at same tick', async () => {
408
- const { base } = await setupTokenPair(clientWithAccount)
409
-
410
- const tick = Tick.fromPrice('1.001')
411
-
412
- // Place first order
413
- const { orderId: orderId1 } = await Actions.dex.placeSync(
414
- clientWithAccount,
415
- {
416
- token: base,
417
- amount: parseUnits('100', 6),
418
- type: 'buy',
419
- tick,
420
- },
421
- )
422
-
423
- // Place second order at same tick
424
- const { orderId: orderId2 } = await Actions.dex.placeSync(
425
- clientWithAccount,
426
- {
427
- token: base,
428
- amount: parseUnits('50', 6),
429
- type: 'buy',
430
- tick,
431
- },
432
- )
433
-
434
- // Get first order
435
- const order1 = await Actions.dex.getOrder(clientWithAccount, {
436
- orderId: orderId1,
437
- })
438
- expect(order1.prev).toBe(0n) // should be 0 as it's first
439
- expect(order1.next).toBe(orderId2) // should point to second order
440
-
441
- // Get second order
442
- const order2 = await Actions.dex.getOrder(clientWithAccount, {
443
- orderId: orderId2,
444
- })
445
- expect(order2.prev).toBe(orderId1) // should point to first order
446
- expect(order2.next).toBe(0n) // should be 0 as it's last
447
- })
448
- })
449
-
450
- describe('getOrderbook', () => {
451
- test('default', async () => {
452
- const { base, quote } = await setupTokenPair(clientWithAccount)
453
-
454
- // Get orderbook information
455
- const book = await Actions.dex.getOrderbook(clientWithAccount, {
456
- base,
457
- quote,
458
- })
459
-
460
- expect(book).toBeDefined()
461
- expect(book.base).toBe(base)
462
- expect(book.quote).toBe(quote)
463
- expect(book.bestBidTick).toBeDefined()
464
- expect(book.bestAskTick).toBeDefined()
465
- })
466
-
467
- test('behavior: shows best bid and ask after orders placed', async () => {
468
- const { base, quote } = await setupTokenPair(clientWithAccount)
469
-
470
- const bidTick = Tick.fromPrice('0.999')
471
- const askTick = Tick.fromPrice('1.001')
472
-
473
- // Place a bid order
474
- await Actions.dex.placeSync(clientWithAccount, {
475
- token: base,
476
- amount: parseUnits('100', 6),
477
- type: 'buy',
478
- tick: bidTick,
479
- })
480
-
481
- // Place an ask order
482
- await Actions.dex.placeSync(clientWithAccount, {
483
- token: base,
484
- amount: parseUnits('100', 6),
485
- type: 'sell',
486
- tick: askTick,
487
- })
488
-
489
- // Get orderbook
490
- const book = await Actions.dex.getOrderbook(clientWithAccount, {
491
- base,
492
- quote,
493
- })
494
-
495
- expect(book.bestBidTick).toBe(bidTick)
496
- expect(book.bestAskTick).toBe(askTick)
497
- })
498
-
499
- test('behavior: best ticks update after better orders placed', async () => {
500
- const { base, quote } = await setupTokenPair(clientWithAccount)
501
-
502
- // Place initial bid at 0.999
503
- await Actions.dex.placeSync(clientWithAccount, {
504
- token: base,
505
- amount: parseUnits('100', 6),
506
- type: 'buy',
507
- tick: Tick.fromPrice('0.999'),
508
- })
509
-
510
- // Get orderbook
511
- const bookBefore = await Actions.dex.getOrderbook(clientWithAccount, {
512
- base,
513
- quote,
514
- })
515
- expect(bookBefore.bestBidTick).toBe(Tick.fromPrice('0.999'))
516
-
517
- // Place better bid at 1.0
518
- await Actions.dex.placeSync(clientWithAccount, {
519
- token: base,
520
- amount: parseUnits('100', 6),
521
- type: 'buy',
522
- tick: Tick.fromPrice('1.0'),
523
- })
524
-
525
- // Get orderbook again
526
- const bookAfter = await Actions.dex.getOrderbook(clientWithAccount, {
527
- base,
528
- quote,
529
- })
530
- expect(bookAfter.bestBidTick).toBe(Tick.fromPrice('1.0'))
531
- })
532
-
533
- test.skip('behavior: best ticks update after order cancellation', async () => {
534
- const { base, quote } = await setupTokenPair(clientWithAccount)
535
-
536
- // Place two bid orders at different ticks
537
- await Actions.dex.placeSync(clientWithAccount, {
538
- token: base,
539
- amount: parseUnits('50', 6),
540
- type: 'buy',
541
- tick: Tick.fromPrice('0.999'),
542
- })
543
-
544
- const { orderId } = await Actions.dex.placeSync(clientWithAccount, {
545
- token: base,
546
- amount: parseUnits('100', 6),
547
- type: 'buy',
548
- tick: Tick.fromPrice('1.0'),
549
- })
550
-
551
- // Get orderbook - best bid should be 1.0
552
- const bookBefore = await Actions.dex.getOrderbook(clientWithAccount, {
553
- base,
554
- quote,
555
- })
556
- expect(bookBefore.bestBidTick).toBe(Tick.fromPrice('1.0'))
557
-
558
- // Cancel the better order
559
- await Actions.dex.cancelSync(clientWithAccount, { orderId })
560
-
561
- // Get orderbook again - best bid should fall back to 0.999
562
- const bookAfter = await Actions.dex.getOrderbook(clientWithAccount, {
563
- base,
564
- quote,
565
- })
566
- expect(bookAfter.bestBidTick).toBe(Tick.fromPrice('0.999'))
567
- })
568
-
569
- test('behavior: multiple pairs have independent orderbooks', async () => {
570
- const { base: base1, quote: quote1 } =
571
- await setupTokenPair(clientWithAccount)
572
- const { base: base2, quote: quote2 } =
573
- await setupTokenPair(clientWithAccount)
574
-
575
- // Place order on first pair
576
- await Actions.dex.placeSync(clientWithAccount, {
577
- token: base1,
578
- amount: parseUnits('100', 6),
579
- type: 'buy',
580
- tick: Tick.fromPrice('1.001'),
581
- })
582
-
583
- // Place order on second pair at different tick
584
- await Actions.dex.placeSync(clientWithAccount, {
585
- token: base2,
586
- amount: parseUnits('100', 6),
587
- type: 'buy',
588
- tick: Tick.fromPrice('0.999'),
589
- })
590
-
591
- // Get orderbooks
592
- const book1 = await Actions.dex.getOrderbook(clientWithAccount, {
593
- base: base1,
594
- quote: quote1,
595
- })
596
-
597
- const book2 = await Actions.dex.getOrderbook(clientWithAccount, {
598
- base: base2,
599
- quote: quote2,
600
- })
601
-
602
- // Each pair should have its own best tick
603
- expect(book1.bestBidTick).toBe(Tick.fromPrice('1.001'))
604
- expect(book2.bestBidTick).toBe(Tick.fromPrice('0.999'))
605
- })
606
- })
607
-
608
- describe('getTickLevel', () => {
609
- test('default', async () => {
610
- const { base } = await setupTokenPair(clientWithAccount)
611
-
612
- const tick = Tick.fromPrice('1.001')
613
-
614
- // Place an order to create liquidity at this tick
615
- const { orderId } = await Actions.dex.placeSync(clientWithAccount, {
616
- token: base,
617
- amount: parseUnits('100', 6),
618
- type: 'buy',
619
- tick,
620
- })
621
-
622
- // Get the price level
623
- const level = await Actions.dex.getTickLevel(clientWithAccount, {
624
- base,
625
- tick,
626
- isBid: true,
627
- })
628
-
629
- expect(level).toBeDefined()
630
- expect(level.head).toBe(orderId) // head should be our order
631
- expect(level.tail).toBe(orderId) // tail should also be our order (only one)
632
- expect(level.totalLiquidity).toBeGreaterThan(0n)
633
- })
634
-
635
- test('behavior: empty price level', async () => {
636
- const { base } = await setupTokenPair(clientWithAccount)
637
-
638
- const tick = Tick.fromPrice('1.001')
639
-
640
- // Query a tick with no orders
641
- const level = await Actions.dex.getTickLevel(clientWithAccount, {
642
- base,
643
- tick,
644
- isBid: true,
645
- })
646
-
647
- expect(level).toBeDefined()
648
- expect(level.head).toBe(0n)
649
- expect(level.tail).toBe(0n)
650
- expect(level.totalLiquidity).toBe(0n)
651
- })
652
-
653
- test('behavior: multiple orders at same tick', async () => {
654
- const { base } = await setupTokenPair(clientWithAccount)
655
-
656
- const tick = Tick.fromPrice('1.001')
657
-
658
- // Place first order
659
- const { orderId: orderId1 } = await Actions.dex.placeSync(
660
- clientWithAccount,
661
- {
662
- token: base,
663
- amount: parseUnits('100', 6),
664
- type: 'buy',
665
- tick,
666
- },
667
- )
668
-
669
- // Place second order at same tick
670
- const { orderId: orderId2 } = await Actions.dex.placeSync(
671
- clientWithAccount,
672
- {
673
- token: base,
674
- amount: parseUnits('50', 6),
675
- type: 'buy',
676
- tick,
677
- },
678
- )
679
-
680
- // Get the price level
681
- const level = await Actions.dex.getTickLevel(clientWithAccount, {
682
- base,
683
- tick,
684
- isBid: true,
685
- })
686
-
687
- expect(level.head).toBe(orderId1) // head should be first order
688
- expect(level.tail).toBe(orderId2) // tail should be last order
689
- // Total liquidity should be sum of both orders (approximately)
690
- expect(level.totalLiquidity).toBeGreaterThan(parseUnits('145', 6))
691
- })
692
-
693
- test('behavior: bid vs ask sides', async () => {
694
- const { base } = await setupTokenPair(clientWithAccount)
695
-
696
- const tick = Tick.fromPrice('1.001')
697
-
698
- // Place a buy order (bid)
699
- await Actions.dex.placeSync(clientWithAccount, {
700
- token: base,
701
- amount: parseUnits('100', 6),
702
- type: 'buy',
703
- tick,
704
- })
705
-
706
- // Place a sell order (ask) at same tick
707
- await Actions.dex.placeSync(clientWithAccount, {
708
- token: base,
709
- amount: parseUnits('50', 6),
710
- type: 'sell',
711
- tick,
712
- })
713
-
714
- // Get bid side
715
- const bidLevel = await Actions.dex.getTickLevel(clientWithAccount, {
716
- base,
717
- tick,
718
- isBid: true,
719
- })
720
-
721
- // Get ask side
722
- const askLevel = await Actions.dex.getTickLevel(clientWithAccount, {
723
- base,
724
- tick,
725
- isBid: false,
726
- })
727
-
728
- // Both should have liquidity but different amounts
729
- expect(bidLevel.totalLiquidity).toBeGreaterThan(0n)
730
- expect(askLevel.totalLiquidity).toBeGreaterThan(0n)
731
- expect(bidLevel.head).not.toBe(askLevel.head)
732
- })
733
-
734
- test('behavior: liquidity changes after order cancellation', async () => {
735
- const { base } = await setupTokenPair(clientWithAccount)
736
-
737
- const tick = Tick.fromPrice('1.001')
738
-
739
- // Place orders
740
- const { orderId: orderId1 } = await Actions.dex.placeSync(
741
- clientWithAccount,
742
- {
743
- token: base,
744
- amount: parseUnits('100', 6),
745
- type: 'buy',
746
- tick,
747
- },
748
- )
749
-
750
- await Actions.dex.placeSync(clientWithAccount, {
751
- token: base,
752
- amount: parseUnits('50', 6),
753
- type: 'buy',
754
- tick,
755
- })
756
-
757
- // Get level before cancellation
758
- const levelBefore = await Actions.dex.getTickLevel(clientWithAccount, {
759
- base,
760
- tick,
761
- isBid: true,
762
- })
763
-
764
- // Cancel first order
765
- await Actions.dex.cancelSync(clientWithAccount, {
766
- orderId: orderId1,
767
- })
768
-
769
- // Get level after cancellation
770
- const levelAfter = await Actions.dex.getTickLevel(clientWithAccount, {
771
- base,
772
- tick,
773
- isBid: true,
774
- })
775
-
776
- // Total liquidity should decrease
777
- expect(levelAfter.totalLiquidity).toBeLessThan(levelBefore.totalLiquidity)
778
- })
779
-
780
- test('behavior: liquidity changes after partial fill', async () => {
781
- const { base, quote } = await setupTokenPair(clientWithAccount)
782
-
783
- const tick = Tick.fromPrice('1.001')
784
-
785
- // Place sell order
786
- await Actions.dex.placeSync(clientWithAccount, {
787
- token: base,
788
- amount: parseUnits('500', 6),
789
- type: 'sell',
790
- tick,
791
- })
792
-
793
- // Get level before fill
794
- const levelBefore = await Actions.dex.getTickLevel(clientWithAccount, {
795
- base,
796
- tick,
797
- isBid: false,
798
- })
799
-
800
- // Partially fill the order
801
- await Actions.dex.buySync(clientWithAccount, {
802
- tokenIn: quote,
803
- tokenOut: base,
804
- amountOut: parseUnits('100', 6),
805
- maxAmountIn: parseUnits('150', 6),
806
- })
807
-
808
- // Get level after fill
809
- const levelAfter = await Actions.dex.getTickLevel(clientWithAccount, {
810
- base,
811
- tick,
812
- isBid: false,
813
- })
814
-
815
- // Total liquidity should decrease
816
- expect(levelAfter.totalLiquidity).toBeLessThan(levelBefore.totalLiquidity)
817
- })
818
-
819
- test('behavior: tick at boundaries', async () => {
820
- const { base } = await setupTokenPair(clientWithAccount)
821
-
822
- // Place order at min tick
823
- await Actions.dex.placeSync(clientWithAccount, {
824
- token: base,
825
- amount: parseUnits('10', 6),
826
- type: 'sell',
827
- tick: Tick.minTick,
828
- })
829
-
830
- // Query min tick
831
- const minLevel = await Actions.dex.getTickLevel(clientWithAccount, {
832
- base,
833
- tick: Tick.minTick,
834
- isBid: false,
835
- })
836
- expect(minLevel.totalLiquidity).toBeGreaterThan(0n)
837
-
838
- // Place order at max tick
839
- await Actions.dex.placeSync(clientWithAccount, {
840
- token: base,
841
- amount: parseUnits('10', 6),
842
- type: 'buy',
843
- tick: Tick.maxTick,
844
- })
845
-
846
- // Query max tick
847
- const maxLevel = await Actions.dex.getTickLevel(clientWithAccount, {
848
- base,
849
- tick: Tick.maxTick,
850
- isBid: true,
851
- })
852
- expect(maxLevel.totalLiquidity).toBeGreaterThan(0n)
853
- })
854
- })
855
-
856
- describe('getSellQuote', () => {
857
- test('default', async () => {
858
- const { base, quote } = await setupTokenPair(clientWithAccount)
859
-
860
- // Place bid orders to create liquidity
861
- await Actions.dex.placeSync(clientWithAccount, {
862
- token: base,
863
- amount: parseUnits('500', 6),
864
- type: 'buy',
865
- tick: Tick.fromPrice('0.999'),
866
- })
867
-
868
- // Get quote for selling base tokens
869
- const amountOut = await Actions.dex.getSellQuote(clientWithAccount, {
870
- tokenIn: base,
871
- tokenOut: quote,
872
- amountIn: parseUnits('100', 6),
873
- })
874
-
875
- expect(amountOut).toBeGreaterThan(0n)
876
- // Should be approximately 100 * 0.999 = 99.9
877
- expect(amountOut).toBeLessThan(parseUnits('100', 6))
878
- })
879
-
880
- test('behavior: fails with no liquidity', async () => {
881
- const { base, quote } = await setupTokenPair(clientWithAccount)
882
-
883
- // Quote should fail with no liquidity
884
- await expect(
885
- Actions.dex.getSellQuote(clientWithAccount, {
886
- tokenIn: base,
887
- tokenOut: quote,
888
- amountIn: parseUnits('100', 6),
889
- }),
890
- ).rejects.toThrow('The contract function "quoteSwapExactAmountIn" reverted')
891
- })
892
- })
893
-
894
- describe('place', () => {
895
- test('default', async () => {
896
- // Setup token pair
897
- const { base } = await setupTokenPair(clientWithAccount)
898
-
899
- // Place a sell order
900
- const { receipt, orderId, token, ...result } = await Actions.dex.placeSync(
901
- clientWithAccount,
902
- {
903
- token: base,
904
- amount: parseUnits('100', 6),
905
- type: 'sell',
906
- tick: Tick.fromPrice('1.001'),
907
- },
908
- )
909
-
910
- expect(receipt).toBeDefined()
911
- expect(receipt.status).toBe('success')
912
- expect(orderId).toBeGreaterThan(0n)
913
- expect(token).toBe(base)
914
- expect(result).toMatchInlineSnapshot(`
915
- {
916
- "amount": 100000000n,
917
- "isBid": false,
918
- "maker": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
919
- "tick": 100,
920
- }
921
- `)
922
-
923
- // Place a buy order
924
- const {
925
- receipt: receipt2,
926
- orderId: orderId2,
927
- token: token2,
928
- ...result2
929
- } = await Actions.dex.placeSync(clientWithAccount, {
930
- token: base,
931
- amount: parseUnits('100', 6),
932
- type: 'buy',
933
- tick: Tick.fromPrice('1.001'),
934
- })
935
- expect(receipt2.status).toBe('success')
936
- expect(orderId2).toBeGreaterThan(0n)
937
- expect(token2).toBe(base)
938
- expect(result2).toMatchInlineSnapshot(`
939
- {
940
- "amount": 100000000n,
941
- "isBid": true,
942
- "maker": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
943
- "tick": 100,
944
- }
945
- `)
946
- })
947
-
948
- test('behavior: tick at boundaries', async () => {
949
- const { base } = await setupTokenPair(clientWithAccount)
950
-
951
- // Test at min tick (-2000)
952
- const { receipt: receipt1, ...result1 } = await Actions.dex.placeSync(
953
- clientWithAccount,
954
- {
955
- token: base,
956
- amount: parseUnits('10', 6),
957
- type: 'sell',
958
- tick: Tick.minTick,
959
- },
960
- )
961
- expect(receipt1.status).toBe('success')
962
- expect(result1.tick).toBe(-2000)
963
-
964
- // Test at max tick (2000)
965
- const { receipt: receipt2, ...result2 } = await Actions.dex.placeSync(
966
- clientWithAccount,
967
- {
968
- token: base,
969
- amount: parseUnits('10', 6),
970
- type: 'buy',
971
- tick: Tick.maxTick,
972
- },
973
- )
974
- expect(receipt2.status).toBe('success')
975
- expect(result2.tick).toBe(2000)
976
- })
977
-
978
- test('behavior: tick validation fails outside bounds', async () => {
979
- const { base } = await setupTokenPair(clientWithAccount)
980
-
981
- // Test tick above max tix should fail
982
- await expect(
983
- Actions.dex.placeSync(clientWithAccount, {
984
- token: base,
985
- amount: parseUnits('10', 6),
986
- type: 'buy',
987
- tick: Tick.maxTick + 1,
988
- }),
989
- ).rejects.toThrow('The contract function "place" reverted')
990
-
991
- // Test tick below min tick should fail
992
- await expect(
993
- Actions.dex.placeSync(clientWithAccount, {
994
- token: base,
995
- amount: parseUnits('10', 6),
996
- type: 'sell',
997
- tick: Tick.minTick - 1,
998
- }),
999
- ).rejects.toThrow('The contract function "place" reverted')
1000
- })
1001
-
1002
- test('behavior: transfers from wallet', async () => {
1003
- const { base, quote } = await setupTokenPair(clientWithAccount)
1004
-
1005
- // Get balances before placing order
1006
- const baseBalanceBefore = await Actions.token.getBalance(
1007
- clientWithAccount,
1008
- {
1009
- token: base,
1010
- },
1011
- )
1012
- const quoteBalanceBefore = await Actions.token.getBalance(
1013
- clientWithAccount,
1014
- {
1015
- token: quote,
1016
- },
1017
- )
1018
-
1019
- // Place a buy order - should transfer quote tokens to escrow
1020
- const orderAmount = parseUnits('100', 6)
1021
- const tick = Tick.fromPrice('1.001')
1022
- await Actions.dex.placeSync(clientWithAccount, {
1023
- token: base,
1024
- amount: orderAmount,
1025
- type: 'buy',
1026
- tick,
1027
- })
1028
-
1029
- // Get balances after placing order
1030
- const baseBalanceAfter = await Actions.token.getBalance(clientWithAccount, {
1031
- token: base,
1032
- })
1033
- const quoteBalanceAfter = await Actions.token.getBalance(
1034
- clientWithAccount,
1035
- {
1036
- token: quote,
1037
- },
1038
- )
1039
-
1040
- // Base token balance should be unchanged (we're buying base, not selling)
1041
- expect(baseBalanceAfter).toBe(baseBalanceBefore)
1042
-
1043
- // Quote token balance should decrease (escrowed for the bid)
1044
- // Amount = orderAmount * (1 + tick/1000) for bids
1045
- const expectedQuoteEscrowed =
1046
- (orderAmount * BigInt(100000 + tick)) / BigInt(100000)
1047
- expect(quoteBalanceBefore - quoteBalanceAfter).toBeGreaterThanOrEqual(
1048
- expectedQuoteEscrowed,
1049
- )
1050
- })
1051
-
1052
- test('behavior: multiple orders at same tick', async () => {
1053
- const { base } = await setupTokenPair(clientWithAccount)
1054
-
1055
- const tick = Tick.fromPrice('1.0005')
1056
-
1057
- // Place first order
1058
- const { orderId: orderId1 } = await Actions.dex.placeSync(
1059
- clientWithAccount,
1060
- {
1061
- token: base,
1062
- amount: parseUnits('100', 6),
1063
- type: 'buy',
1064
- tick,
1065
- },
1066
- )
1067
-
1068
- // Place second order at same tick
1069
- const { orderId: orderId2 } = await Actions.dex.placeSync(
1070
- clientWithAccount,
1071
- {
1072
- token: base,
1073
- amount: parseUnits('50', 6),
1074
- type: 'buy',
1075
- tick,
1076
- },
1077
- )
1078
-
1079
- // Order IDs should be different and sequential
1080
- expect(orderId2).toBeGreaterThan(orderId1)
1081
- })
1082
- })
1083
-
1084
- describe('placeFlip', () => {
1085
- test('default', async () => {
1086
- const { base } = await setupTokenPair(clientWithAccount)
1087
-
1088
- // Place a flip bid order
1089
- const { receipt, orderId, token, ...result } =
1090
- await Actions.dex.placeFlipSync(clientWithAccount, {
1091
- token: base,
1092
- amount: parseUnits('100', 6),
1093
- type: 'buy',
1094
- tick: Tick.fromPrice('1.001'),
1095
- flipTick: Tick.fromPrice('1.002'),
1096
- })
1097
-
1098
- expect(receipt).toBeDefined()
1099
- expect(receipt.status).toBe('success')
1100
- expect(orderId).toBeGreaterThan(0n)
1101
- expect(token).toBe(base)
1102
- expect(result.flipTick).toBe(Tick.fromPrice('1.002'))
1103
- expect(result).toMatchInlineSnapshot(`
1104
- {
1105
- "amount": 100000000n,
1106
- "flipTick": 200,
1107
- "isBid": true,
1108
- "maker": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
1109
- "tick": 100,
1110
- }
1111
- `)
1112
- })
1113
-
1114
- test('behavior: flip bid requires flipTick > tick', async () => {
1115
- const { base } = await setupTokenPair(clientWithAccount)
1116
-
1117
- // Valid: flipTick > tick for bid
1118
- const { receipt: receipt1 } = await Actions.dex.placeFlipSync(
1119
- clientWithAccount,
1120
- {
1121
- token: base,
1122
- amount: parseUnits('10', 6),
1123
- type: 'buy',
1124
- tick: Tick.fromPrice('1.0005'),
1125
- flipTick: Tick.fromPrice('1.001'),
1126
- },
1127
- )
1128
- expect(receipt1.status).toBe('success')
1129
-
1130
- // Invalid: flipTick <= tick for bid should fail
1131
- await expect(
1132
- Actions.dex.placeFlipSync(clientWithAccount, {
1133
- token: base,
1134
- amount: parseUnits('10', 6),
1135
- type: 'buy',
1136
- tick: Tick.fromPrice('1.001'),
1137
- flipTick: Tick.fromPrice('1.001'), // Equal
1138
- }),
1139
- ).rejects.toThrow('The contract function "placeFlip" reverted')
1140
-
1141
- await expect(
1142
- Actions.dex.placeFlipSync(clientWithAccount, {
1143
- token: base,
1144
- amount: parseUnits('10', 6),
1145
- type: 'buy',
1146
- tick: Tick.fromPrice('1.001'),
1147
- flipTick: Tick.fromPrice('1.0005'), // Less than
1148
- }),
1149
- ).rejects.toThrow('The contract function "placeFlip" reverted')
1150
- })
1151
-
1152
- test('behavior: flip ask requires flipTick < tick', async () => {
1153
- const { base } = await setupTokenPair(clientWithAccount)
1154
-
1155
- // Valid: flipTick < tick for ask
1156
- const { receipt: receipt1 } = await Actions.dex.placeFlipSync(
1157
- clientWithAccount,
1158
- {
1159
- token: base,
1160
- amount: parseUnits('10', 6),
1161
- type: 'sell',
1162
- tick: Tick.fromPrice('1.001'),
1163
- flipTick: Tick.fromPrice('1.0005'),
1164
- },
1165
- )
1166
- expect(receipt1.status).toBe('success')
1167
-
1168
- // Invalid: flipTick >= tick for ask should fail
1169
- await expect(
1170
- Actions.dex.placeFlipSync(clientWithAccount, {
1171
- token: base,
1172
- amount: parseUnits('10', 6),
1173
- type: 'sell',
1174
- tick: Tick.fromPrice('1.0005'),
1175
- flipTick: Tick.fromPrice('1.0005'), // Equal
1176
- }),
1177
- ).rejects.toThrow('The contract function "placeFlip" reverted')
1178
-
1179
- await expect(
1180
- Actions.dex.placeFlipSync(clientWithAccount, {
1181
- token: base,
1182
- amount: parseUnits('10', 6),
1183
- type: 'sell',
1184
- tick: Tick.fromPrice('1.0005'),
1185
- flipTick: Tick.fromPrice('1.001'), // Greater than
1186
- }),
1187
- ).rejects.toThrow('The contract function "placeFlip" reverted')
1188
- })
1189
-
1190
- test('behavior: flip ticks at boundaries', async () => {
1191
- const { base } = await setupTokenPair(clientWithAccount)
1192
-
1193
- // Flip order with ticks at extreme boundaries
1194
- const { receipt } = await Actions.dex.placeFlipSync(clientWithAccount, {
1195
- token: base,
1196
- amount: parseUnits('10', 6),
1197
- type: 'buy',
1198
- tick: Tick.minTick,
1199
- flipTick: Tick.maxTick,
1200
- })
1201
- expect(receipt.status).toBe('success')
1202
- })
1203
- })
1204
-
1205
- describe('sell', () => {
1206
- test('default', async () => {
1207
- const { base, quote } = await setupTokenPair(clientWithAccount)
1208
-
1209
- // Place bid order to create liquidity
1210
- await Actions.dex.placeSync(clientWithAccount, {
1211
- token: base,
1212
- amount: parseUnits('500', 6),
1213
- type: 'buy',
1214
- tick: Tick.fromPrice('0.999'),
1215
- })
1216
-
1217
- // Sell base tokens
1218
- const { receipt } = await Actions.dex.sellSync(clientWithAccount, {
1219
- tokenIn: base,
1220
- tokenOut: quote,
1221
- amountIn: parseUnits('100', 6),
1222
- minAmountOut: parseUnits('50', 6),
1223
- })
1224
-
1225
- expect(receipt).toBeDefined()
1226
- expect(receipt.status).toBe('success')
1227
- })
1228
-
1229
- test('behavior: respects minAmountOut', async () => {
1230
- const { base, quote } = await setupTokenPair(clientWithAccount)
1231
-
1232
- // Place bid order at low price
1233
- await Actions.dex.placeSync(clientWithAccount, {
1234
- token: base,
1235
- amount: parseUnits('500', 6),
1236
- type: 'buy',
1237
- tick: Tick.fromPrice('0.99'), // 1% below peg
1238
- })
1239
-
1240
- // Try to sell with too high minAmountOut - should fail
1241
- await expect(
1242
- Actions.dex.sellSync(clientWithAccount, {
1243
- tokenIn: base,
1244
- tokenOut: quote,
1245
- amountIn: parseUnits('100', 6),
1246
- minAmountOut: parseUnits('150', 6), // Way too high
1247
- }),
1248
- ).rejects.toThrow('The contract function "swapExactAmountIn" reverted')
1249
- })
1250
-
1251
- test('behavior: fails with insufficient liquidity', async () => {
1252
- const { base, quote } = await setupTokenPair(clientWithAccount)
1253
-
1254
- // No orders - no liquidity
1255
-
1256
- // Try to sell - should fail
1257
- await expect(
1258
- Actions.dex.sellSync(clientWithAccount, {
1259
- tokenIn: base,
1260
- tokenOut: quote,
1261
- amountIn: parseUnits('100', 6),
1262
- minAmountOut: parseUnits('50', 6),
1263
- }),
1264
- ).rejects.toThrow('The contract function "swapExactAmountIn" reverted')
1265
- })
1266
- })
1267
-
1268
- describe('watchFlipOrderPlaced', () => {
1269
- test('default', async () => {
1270
- const { base } = await setupTokenPair(clientWithAccount)
1271
-
1272
- const receivedOrders: Array<{
1273
- args: Actions.dex.watchFlipOrderPlaced.Args
1274
- log: Actions.dex.watchFlipOrderPlaced.Log
1275
- }> = []
1276
-
1277
- const unwatch = Actions.dex.watchFlipOrderPlaced(clientWithAccount, {
1278
- onFlipOrderPlaced: (args, log) => {
1279
- receivedOrders.push({ args, log })
1280
- },
1281
- })
1282
-
1283
- try {
1284
- // Place flip order
1285
- await Actions.dex.placeFlipSync(clientWithAccount, {
1286
- token: base,
1287
- amount: parseUnits('100', 6),
1288
- type: 'buy',
1289
- tick: Tick.fromPrice('1.001'),
1290
- flipTick: Tick.fromPrice('1.002'),
1291
- })
1292
-
1293
- await new Promise((resolve) => setTimeout(resolve, 200))
1294
-
1295
- expect(receivedOrders).toHaveLength(1)
1296
- expect(receivedOrders[0]?.args.flipTick).toBe(Tick.fromPrice('1.002'))
1297
- expect(receivedOrders[0]?.args.tick).toBe(Tick.fromPrice('1.001'))
1298
- } finally {
1299
- unwatch()
1300
- }
1301
- })
1302
- })
1303
-
1304
- describe('watchOrderCancelled', () => {
1305
- test('default', async () => {
1306
- const { base } = await setupTokenPair(clientWithAccount)
1307
-
1308
- const receivedCancellations: Array<{
1309
- args: Actions.dex.watchOrderCancelled.Args
1310
- log: Actions.dex.watchOrderCancelled.Log
1311
- }> = []
1312
-
1313
- const unwatch = Actions.dex.watchOrderCancelled(clientWithAccount, {
1314
- onOrderCancelled: (args, log) => {
1315
- receivedCancellations.push({ args, log })
1316
- },
1317
- })
1318
-
1319
- try {
1320
- // Place order
1321
- const { orderId } = await Actions.dex.placeSync(clientWithAccount, {
1322
- token: base,
1323
- amount: parseUnits('100', 6),
1324
- type: 'buy',
1325
- tick: Tick.fromPrice('1.001'),
1326
- })
1327
-
1328
- // Cancel order
1329
- await Actions.dex.cancelSync(clientWithAccount, {
1330
- orderId,
1331
- })
1332
-
1333
- await new Promise((resolve) => setTimeout(resolve, 200))
1334
-
1335
- expect(receivedCancellations).toHaveLength(1)
1336
- expect(receivedCancellations[0]?.args.orderId).toBe(orderId)
1337
- } finally {
1338
- unwatch()
1339
- }
1340
- })
1341
-
1342
- test('behavior: filter by orderId', async () => {
1343
- const { base } = await setupTokenPair(clientWithAccount)
1344
-
1345
- // Place two orders
1346
- const { orderId: orderId1 } = await Actions.dex.placeSync(
1347
- clientWithAccount,
1348
- {
1349
- token: base,
1350
- amount: parseUnits('100', 6),
1351
- type: 'buy',
1352
- tick: Tick.fromPrice('1.001'),
1353
- },
1354
- )
1355
-
1356
- const { orderId: orderId2 } = await Actions.dex.placeSync(
1357
- clientWithAccount,
1358
- {
1359
- token: base,
1360
- amount: parseUnits('50', 6),
1361
- type: 'buy',
1362
- tick: Tick.fromPrice('1.001'),
1363
- },
1364
- )
1365
-
1366
- const receivedCancellations: Array<{
1367
- args: Actions.dex.watchOrderCancelled.Args
1368
- log: Actions.dex.watchOrderCancelled.Log
1369
- }> = []
1370
-
1371
- // Watch only for cancellation of orderId1
1372
- const unwatch = Actions.dex.watchOrderCancelled(clientWithAccount, {
1373
- orderId: orderId1,
1374
- onOrderCancelled: (args, log) => {
1375
- receivedCancellations.push({ args, log })
1376
- },
1377
- })
1378
-
1379
- try {
1380
- // Cancel orderId1 (should be captured)
1381
- await Actions.dex.cancelSync(clientWithAccount, {
1382
- orderId: orderId1,
1383
- })
1384
-
1385
- // Cancel orderId2 (should NOT be captured)
1386
- await Actions.dex.cancelSync(clientWithAccount, {
1387
- orderId: orderId2,
1388
- })
1389
-
1390
- await new Promise((resolve) => setTimeout(resolve, 200))
1391
-
1392
- // Should only receive 1 event
1393
- expect(receivedCancellations).toHaveLength(1)
1394
- expect(receivedCancellations[0]?.args.orderId).toBe(orderId1)
1395
- } finally {
1396
- unwatch()
1397
- }
1398
- })
1399
- })
1400
-
1401
- describe.todo('watchOrderFilled')
1402
-
1403
- describe('watchOrderPlaced', () => {
1404
- test('default', async () => {
1405
- const { base } = await setupTokenPair(clientWithAccount)
1406
-
1407
- const receivedOrders: Array<{
1408
- args: Actions.dex.watchOrderPlaced.Args
1409
- log: Actions.dex.watchOrderPlaced.Log
1410
- }> = []
1411
-
1412
- const unwatch = Actions.dex.watchOrderPlaced(clientWithAccount, {
1413
- onOrderPlaced: (args, log) => {
1414
- receivedOrders.push({ args, log })
1415
- },
1416
- })
1417
-
1418
- try {
1419
- // Place first order
1420
- await Actions.dex.placeSync(clientWithAccount, {
1421
- token: base,
1422
- amount: parseUnits('100', 6),
1423
- type: 'buy',
1424
- tick: Tick.fromPrice('1.001'),
1425
- })
1426
-
1427
- // Place second order
1428
- await Actions.dex.placeSync(clientWithAccount, {
1429
- token: base,
1430
- amount: parseUnits('50', 6),
1431
- type: 'sell',
1432
- tick: Tick.fromPrice('0.999'),
1433
- })
1434
-
1435
- // Wait for events
1436
- await new Promise((resolve) => setTimeout(resolve, 200))
1437
-
1438
- expect(receivedOrders).toHaveLength(2)
1439
- expect(receivedOrders[0]?.args.isBid).toBe(true)
1440
- expect(receivedOrders[0]?.args.amount).toBe(parseUnits('100', 6))
1441
- expect(receivedOrders[1]?.args.isBid).toBe(false)
1442
- expect(receivedOrders[1]?.args.amount).toBe(parseUnits('50', 6))
1443
- } finally {
1444
- unwatch()
1445
- }
1446
- })
1447
-
1448
- test('behavior: filter by token', async () => {
1449
- const { base } = await setupTokenPair(clientWithAccount)
1450
- const { base: base2 } = await setupTokenPair(clientWithAccount)
1451
-
1452
- const receivedOrders: Array<{
1453
- args: Actions.dex.watchOrderPlaced.Args
1454
- log: Actions.dex.watchOrderPlaced.Log
1455
- }> = []
1456
-
1457
- // Watch only for orders on base
1458
- const unwatch = Actions.dex.watchOrderPlaced(clientWithAccount, {
1459
- token: base,
1460
- onOrderPlaced: (args, log) => {
1461
- receivedOrders.push({ args, log })
1462
- },
1463
- })
1464
-
1465
- try {
1466
- // Place order on base (should be captured)
1467
- await Actions.dex.placeSync(clientWithAccount, {
1468
- token: base,
1469
- amount: parseUnits('100', 6),
1470
- type: 'buy',
1471
- tick: Tick.fromPrice('1.001'),
1472
- })
1473
-
1474
- // Place order on base2 (should NOT be captured)
1475
- await Actions.dex.placeSync(clientWithAccount, {
1476
- token: base2,
1477
- amount: parseUnits('50', 6),
1478
- type: 'buy',
1479
- tick: Tick.fromPrice('1.001'),
1480
- })
1481
-
1482
- await new Promise((resolve) => setTimeout(resolve, 200))
1483
-
1484
- // Should only receive 1 event
1485
- expect(receivedOrders).toHaveLength(1)
1486
- expect(receivedOrders[0]?.args.token.toLowerCase()).toBe(
1487
- base.toLowerCase(),
1488
- )
1489
- } finally {
1490
- unwatch()
1491
- }
1492
- })
1493
- })
1494
-
1495
- describe('withdraw', () => {
1496
- test('default', async () => {
1497
- const { base, quote } = await setupTokenPair(clientWithAccount)
1498
-
1499
- // Create internal balance
1500
- const { orderId } = await Actions.dex.placeSync(clientWithAccount, {
1501
- token: base,
1502
- amount: parseUnits('100', 6),
1503
- type: 'buy',
1504
- tick: Tick.fromPrice('1.001'),
1505
- })
1506
-
1507
- await Actions.dex.cancelSync(clientWithAccount, { orderId })
1508
-
1509
- // Get DEX balance
1510
- const dexBalance = await Actions.dex.getBalance(clientWithAccount, {
1511
- account: clientWithAccount.account.address,
1512
- token: quote,
1513
- })
1514
- expect(dexBalance).toBeGreaterThan(0n)
1515
-
1516
- // Get wallet balance before withdraw
1517
- const walletBalanceBefore = await Actions.token.getBalance(
1518
- clientWithAccount,
1519
- {
1520
- token: quote,
1521
- },
1522
- )
1523
-
1524
- // Withdraw from DEX
1525
- const { receipt } = await Actions.dex.withdrawSync(clientWithAccount, {
1526
- token: quote,
1527
- amount: dexBalance,
1528
- })
1529
-
1530
- expect(receipt).toBeDefined()
1531
- expect(receipt.status).toBe('success')
1532
-
1533
- // Check DEX balance is now 0
1534
- const dexBalanceAfter = await Actions.dex.getBalance(clientWithAccount, {
1535
- account: clientWithAccount.account.address,
1536
- token: quote,
1537
- })
1538
- expect(dexBalanceAfter).toBe(0n)
1539
-
1540
- // Check wallet balance increased
1541
- const walletBalanceAfter = await Actions.token.getBalance(
1542
- clientWithAccount,
1543
- {
1544
- token: quote,
1545
- },
1546
- )
1547
- expect(walletBalanceAfter).toBeGreaterThan(walletBalanceBefore)
1548
- })
1549
- })