tempo.ts 0.11.1 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +67 -4
- package/README.md +3 -34
- package/dist/server/Handler.d.ts +14 -14
- package/dist/server/Handler.d.ts.map +1 -1
- package/dist/server/Handler.js +16 -17
- package/dist/server/Handler.js.map +1 -1
- package/dist/wagmi/Actions/amm.d.ts +51 -51
- package/dist/wagmi/Actions/amm.d.ts.map +1 -1
- package/dist/wagmi/Actions/amm.js +37 -37
- package/dist/wagmi/Actions/amm.js.map +1 -1
- package/dist/wagmi/Actions/dex.d.ts +129 -129
- package/dist/wagmi/Actions/dex.d.ts.map +1 -1
- package/dist/wagmi/Actions/dex.js +73 -73
- package/dist/wagmi/Actions/dex.js.map +1 -1
- package/dist/wagmi/Actions/faucet.d.ts +9 -9
- package/dist/wagmi/Actions/faucet.d.ts.map +1 -1
- package/dist/wagmi/Actions/faucet.js +7 -7
- package/dist/wagmi/Actions/faucet.js.map +1 -1
- package/dist/wagmi/Actions/fee.d.ts +17 -17
- package/dist/wagmi/Actions/fee.d.ts.map +1 -1
- package/dist/wagmi/Actions/fee.js +10 -10
- package/dist/wagmi/Actions/fee.js.map +1 -1
- package/dist/wagmi/Actions/nonce.d.ts +9 -79
- package/dist/wagmi/Actions/nonce.d.ts.map +1 -1
- package/dist/wagmi/Actions/nonce.js +7 -89
- package/dist/wagmi/Actions/nonce.js.map +1 -1
- package/dist/wagmi/Actions/policy.d.ts +69 -70
- package/dist/wagmi/Actions/policy.d.ts.map +1 -1
- package/dist/wagmi/Actions/policy.js +43 -43
- package/dist/wagmi/Actions/policy.js.map +1 -1
- package/dist/wagmi/Actions/reward.d.ts +51 -51
- package/dist/wagmi/Actions/reward.d.ts.map +1 -1
- package/dist/wagmi/Actions/reward.js +31 -31
- package/dist/wagmi/Actions/reward.js.map +1 -1
- package/dist/wagmi/Actions/token.d.ts +238 -238
- package/dist/wagmi/Actions/token.d.ts.map +1 -1
- package/dist/wagmi/Actions/token.js +136 -136
- package/dist/wagmi/Actions/token.js.map +1 -1
- package/dist/wagmi/Connector.d.ts +2 -1
- package/dist/wagmi/Connector.d.ts.map +1 -1
- package/dist/wagmi/Connector.js +83 -22
- package/dist/wagmi/Connector.js.map +1 -1
- package/dist/wagmi/Hooks/nonce.d.ts +1 -52
- package/dist/wagmi/Hooks/nonce.d.ts.map +1 -1
- package/dist/wagmi/Hooks/nonce.js +1 -70
- package/dist/wagmi/Hooks/nonce.js.map +1 -1
- package/dist/wagmi/Hooks/policy.d.ts +0 -1
- package/dist/wagmi/Hooks/policy.d.ts.map +1 -1
- package/dist/wagmi/Hooks/policy.js.map +1 -1
- package/dist/wagmi/KeyManager.d.ts +6 -3
- package/dist/wagmi/KeyManager.d.ts.map +1 -1
- package/dist/wagmi/KeyManager.js +9 -4
- package/dist/wagmi/KeyManager.js.map +1 -1
- package/package.json +3 -13
- package/src/server/Handler.test.ts +2 -2
- package/src/server/Handler.ts +16 -17
- package/src/wagmi/Actions/amm.ts +63 -63
- package/src/wagmi/Actions/dex.test.ts +1 -1
- package/src/wagmi/Actions/dex.ts +153 -153
- package/src/wagmi/Actions/faucet.ts +11 -11
- package/src/wagmi/Actions/fee.ts +20 -20
- package/src/wagmi/Actions/nonce.test.ts +1 -64
- package/src/wagmi/Actions/nonce.ts +10 -142
- package/src/wagmi/Actions/policy.ts +83 -85
- package/src/wagmi/Actions/reward.ts +64 -61
- package/src/wagmi/Actions/token.ts +287 -283
- package/src/wagmi/Connector.ts +105 -31
- package/src/wagmi/Hooks/dex.test.ts +1 -1
- package/src/wagmi/Hooks/fee.test.ts +0 -6
- package/src/wagmi/Hooks/nonce.test.ts +1 -66
- package/src/wagmi/Hooks/nonce.ts +1 -114
- package/src/wagmi/Hooks/policy.ts +0 -2
- package/src/wagmi/KeyManager.ts +18 -5
- package/dist/chains.d.ts +0 -73
- package/dist/chains.d.ts.map +0 -1
- package/dist/chains.js +0 -51
- package/dist/chains.js.map +0 -1
- package/dist/viem/Abis.d.ts +0 -2649
- package/dist/viem/Abis.d.ts.map +0 -1
- package/dist/viem/Abis.js +0 -1677
- package/dist/viem/Abis.js.map +0 -1
- package/dist/viem/Account.d.ts +0 -244
- package/dist/viem/Account.d.ts.map +0 -1
- package/dist/viem/Account.js +0 -382
- package/dist/viem/Account.js.map +0 -1
- package/dist/viem/Actions/account.d.ts +0 -40
- package/dist/viem/Actions/account.d.ts.map +0 -1
- package/dist/viem/Actions/account.js +0 -86
- package/dist/viem/Actions/account.js.map +0 -1
- package/dist/viem/Actions/amm.d.ts +0 -1991
- package/dist/viem/Actions/amm.d.ts.map +0 -1
- package/dist/viem/Actions/amm.js +0 -814
- package/dist/viem/Actions/amm.js.map +0 -1
- package/dist/viem/Actions/dex.d.ts +0 -3900
- package/dist/viem/Actions/dex.d.ts.map +0 -1
- package/dist/viem/Actions/dex.js +0 -1414
- package/dist/viem/Actions/dex.js.map +0 -1
- package/dist/viem/Actions/faucet.d.ts +0 -69
- package/dist/viem/Actions/faucet.d.ts.map +0 -1
- package/dist/viem/Actions/faucet.js +0 -73
- package/dist/viem/Actions/faucet.js.map +0 -1
- package/dist/viem/Actions/fee.d.ts +0 -360
- package/dist/viem/Actions/fee.d.ts.map +0 -1
- package/dist/viem/Actions/fee.js +0 -237
- package/dist/viem/Actions/fee.js.map +0 -1
- package/dist/viem/Actions/index.d.ts +0 -10
- package/dist/viem/Actions/index.d.ts.map +0 -1
- package/dist/viem/Actions/index.js +0 -10
- package/dist/viem/Actions/index.js.map +0 -1
- package/dist/viem/Actions/nonce.d.ts +0 -257
- package/dist/viem/Actions/nonce.d.ts.map +0 -1
- package/dist/viem/Actions/nonce.js +0 -228
- package/dist/viem/Actions/nonce.js.map +0 -1
- package/dist/viem/Actions/policy.d.ts +0 -1680
- package/dist/viem/Actions/policy.d.ts.map +0 -1
- package/dist/viem/Actions/policy.js +0 -875
- package/dist/viem/Actions/policy.js.map +0 -1
- package/dist/viem/Actions/reward.d.ts +0 -2422
- package/dist/viem/Actions/reward.d.ts.map +0 -1
- package/dist/viem/Actions/reward.js +0 -651
- package/dist/viem/Actions/reward.js.map +0 -1
- package/dist/viem/Actions/token.d.ts +0 -16007
- package/dist/viem/Actions/token.d.ts.map +0 -1
- package/dist/viem/Actions/token.js +0 -2936
- package/dist/viem/Actions/token.js.map +0 -1
- package/dist/viem/Addresses.d.ts +0 -9
- package/dist/viem/Addresses.d.ts.map +0 -1
- package/dist/viem/Addresses.js +0 -9
- package/dist/viem/Addresses.js.map +0 -1
- package/dist/viem/Chain.d.ts +0 -451
- package/dist/viem/Chain.d.ts.map +0 -1
- package/dist/viem/Chain.js +0 -96
- package/dist/viem/Chain.js.map +0 -1
- package/dist/viem/Decorator.d.ts +0 -2783
- package/dist/viem/Decorator.d.ts.map +0 -1
- package/dist/viem/Decorator.js +0 -137
- package/dist/viem/Decorator.js.map +0 -1
- package/dist/viem/Formatters.d.ts +0 -10
- package/dist/viem/Formatters.d.ts.map +0 -1
- package/dist/viem/Formatters.js +0 -104
- package/dist/viem/Formatters.js.map +0 -1
- package/dist/viem/P256.d.ts +0 -2
- package/dist/viem/P256.d.ts.map +0 -1
- package/dist/viem/P256.js +0 -2
- package/dist/viem/P256.js.map +0 -1
- package/dist/viem/Secp256k1.d.ts +0 -2
- package/dist/viem/Secp256k1.d.ts.map +0 -1
- package/dist/viem/Secp256k1.js +0 -2
- package/dist/viem/Secp256k1.js.map +0 -1
- package/dist/viem/Storage.d.ts +0 -24
- package/dist/viem/Storage.d.ts.map +0 -1
- package/dist/viem/Storage.js +0 -68
- package/dist/viem/Storage.js.map +0 -1
- package/dist/viem/TokenIds.d.ts +0 -2
- package/dist/viem/TokenIds.d.ts.map +0 -1
- package/dist/viem/TokenIds.js +0 -2
- package/dist/viem/TokenIds.js.map +0 -1
- package/dist/viem/Transaction.d.ts +0 -76
- package/dist/viem/Transaction.d.ts.map +0 -1
- package/dist/viem/Transaction.js +0 -176
- package/dist/viem/Transaction.js.map +0 -1
- package/dist/viem/Transport.d.ts +0 -33
- package/dist/viem/Transport.d.ts.map +0 -1
- package/dist/viem/Transport.js +0 -138
- package/dist/viem/Transport.js.map +0 -1
- package/dist/viem/WebAuthnP256.d.ts +0 -82
- package/dist/viem/WebAuthnP256.d.ts.map +0 -1
- package/dist/viem/WebAuthnP256.js +0 -97
- package/dist/viem/WebAuthnP256.js.map +0 -1
- package/dist/viem/WebCryptoP256.d.ts +0 -2
- package/dist/viem/WebCryptoP256.d.ts.map +0 -1
- package/dist/viem/WebCryptoP256.js +0 -2
- package/dist/viem/WebCryptoP256.js.map +0 -1
- package/dist/viem/index.d.ts +0 -26
- package/dist/viem/index.d.ts.map +0 -1
- package/dist/viem/index.js +0 -17
- package/dist/viem/index.js.map +0 -1
- package/dist/viem/internal/types.d.ts +0 -20
- package/dist/viem/internal/types.d.ts.map +0 -1
- package/dist/viem/internal/types.js +0 -2
- package/dist/viem/internal/types.js.map +0 -1
- package/dist/viem/internal/utils.d.ts +0 -14
- package/dist/viem/internal/utils.d.ts.map +0 -1
- package/dist/viem/internal/utils.js +0 -33
- package/dist/viem/internal/utils.js.map +0 -1
- package/src/chains.ts +0 -54
- package/src/viem/Abis.ts +0 -1688
- package/src/viem/Account.test.ts +0 -444
- package/src/viem/Account.ts +0 -601
- package/src/viem/Actions/account.test.ts +0 -414
- package/src/viem/Actions/account.ts +0 -106
- package/src/viem/Actions/amm.test.ts +0 -381
- package/src/viem/Actions/amm.ts +0 -1227
- package/src/viem/Actions/dex.test.ts +0 -1549
- package/src/viem/Actions/dex.ts +0 -2150
- package/src/viem/Actions/faucet.ts +0 -121
- package/src/viem/Actions/fee.test.ts +0 -259
- package/src/viem/Actions/fee.ts +0 -372
- package/src/viem/Actions/index.ts +0 -9
- package/src/viem/Actions/nonce.test.ts +0 -206
- package/src/viem/Actions/nonce.ts +0 -347
- package/src/viem/Actions/policy.test.ts +0 -534
- package/src/viem/Actions/policy.ts +0 -1335
- package/src/viem/Actions/reward.test.ts +0 -434
- package/src/viem/Actions/reward.ts +0 -944
- package/src/viem/Actions/token.test.ts +0 -3029
- package/src/viem/Actions/token.ts +0 -4458
- package/src/viem/Addresses.ts +0 -9
- package/src/viem/Chain.bench-d.ts +0 -12
- package/src/viem/Chain.test.ts +0 -168
- package/src/viem/Chain.ts +0 -157
- package/src/viem/Decorator.bench-d.ts +0 -11
- package/src/viem/Decorator.test.ts +0 -39
- package/src/viem/Decorator.ts +0 -3179
- package/src/viem/Formatters.ts +0 -164
- package/src/viem/P256.ts +0 -1
- package/src/viem/Secp256k1.ts +0 -1
- package/src/viem/Storage.ts +0 -110
- package/src/viem/TokenIds.ts +0 -1
- package/src/viem/Transaction.ts +0 -382
- package/src/viem/Transport.ts +0 -191
- package/src/viem/WebAuthnP256.ts +0 -146
- package/src/viem/WebCryptoP256.ts +0 -1
- package/src/viem/e2e.test.ts +0 -1602
- package/src/viem/index.ts +0 -30
- package/src/viem/internal/types.ts +0 -69
- package/src/viem/internal/utils.ts +0 -58
- 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
|
-
})
|