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