@steerprotocol/sdk 1.30.8 → 1.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/base/PoolClient.js +137 -0
- package/dist/cjs/base/PoolClient.js.map +1 -1
- package/dist/cjs/base/VaultClient.js +111 -2
- package/dist/cjs/base/VaultClient.js.map +1 -1
- package/dist/cjs/const/feeManagerContracts.js +25 -0
- package/dist/cjs/const/feeManagerContracts.js.map +1 -0
- package/dist/cjs/const/network.js +7 -0
- package/dist/cjs/const/network.js.map +1 -1
- package/dist/cjs/const/protocol.js +68 -92
- package/dist/cjs/const/protocol.js.map +1 -1
- package/dist/cjs/scripts/processDeployments.js +1 -0
- package/dist/cjs/scripts/processDeployments.js.map +1 -1
- package/dist/esm/base/PoolClient.js +137 -0
- package/dist/esm/base/PoolClient.js.map +1 -1
- package/dist/esm/base/VaultClient.js +111 -2
- package/dist/esm/base/VaultClient.js.map +1 -1
- package/dist/esm/const/feeManagerContracts.js +25 -0
- package/dist/esm/const/feeManagerContracts.js.map +1 -0
- package/dist/esm/const/network.js +7 -0
- package/dist/esm/const/network.js.map +1 -1
- package/dist/esm/const/protocol.js +68 -92
- package/dist/esm/const/protocol.js.map +1 -1
- package/dist/esm/scripts/processDeployments.js +1 -0
- package/dist/esm/scripts/processDeployments.js.map +1 -1
- package/dist/types/base/PoolClient.d.ts +22 -0
- package/dist/types/base/PoolClient.d.ts.map +1 -1
- package/dist/types/base/VaultClient.d.ts +20 -0
- package/dist/types/base/VaultClient.d.ts.map +1 -1
- package/dist/types/const/feeManagerContracts.d.ts +6 -0
- package/dist/types/const/feeManagerContracts.d.ts.map +1 -0
- package/dist/types/const/network.d.ts +1 -0
- package/dist/types/const/network.d.ts.map +1 -1
- package/dist/types/const/protocol.d.ts +4 -19
- package/dist/types/const/protocol.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/__tests__/base/PoolClient.test.ts +355 -104
- package/src/__tests__/base/StakingClient.test.ts +72 -72
- package/src/__tests__/base/VaultClient.protocol-filter.test.ts +64 -137
- package/src/__tests__/base/VaultClient.test.ts +460 -60
- package/src/__tests__/base/vault/single-asset/calculateLimitPrice.test.ts +32 -14
- package/src/__tests__/base/vault/single-asset/calculateSwapAmount.test.ts +7 -4
- package/src/__tests__/base/vault/single-asset/estimateLpTokens.test.ts +105 -570
- package/src/__tests__/base/vault/single-asset/simulateSwap.test.ts +45 -66
- package/src/__tests__/base/vault/single-asset/singleAssetDepositClient.test.ts +178 -381
- package/src/__tests__/const/network.feeManager.test.ts +47 -0
- package/src/__tests__/fixtures/live/single-asset.fixture.json +116 -0
- package/src/__tests__/fixtures/live/staking-pools.fixture.json +353 -0
- package/src/__tests__/fixtures/live/vaults.fixture.json +5392 -0
- package/src/base/PoolClient.ts +200 -1
- package/src/base/VaultClient.ts +169 -2
- package/src/const/feeManagerContracts.ts +28 -0
- package/src/const/network.ts +10 -1
- package/src/const/protocol.ts +18 -39
- package/src/scripts/processDeployments.ts +1 -0
|
@@ -1,436 +1,107 @@
|
|
|
1
|
-
import { Address,
|
|
2
|
-
import { privateKeyToAccount } from 'viem/accounts';
|
|
3
|
-
import { foundry, polygon } from 'viem/chains';
|
|
1
|
+
import { Address, parseEther } from 'viem';
|
|
4
2
|
import { estimateLpTokens, getVaultReserves } from '../../../../base/vault/single-asset/estimateLpTokens.js';
|
|
5
3
|
|
|
6
|
-
// Mock the vault ABI import
|
|
7
|
-
jest.mock('../../../../const/deployments/abis', () => ({
|
|
8
|
-
abis: {
|
|
9
|
-
QuickSwapUniv3MultiPositionLiquidityManager: [
|
|
10
|
-
{
|
|
11
|
-
type: 'function',
|
|
12
|
-
name: 'totalSupply',
|
|
13
|
-
inputs: [],
|
|
14
|
-
outputs: [{ name: '', type: 'uint256' }],
|
|
15
|
-
stateMutability: 'view'
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
type: 'function',
|
|
19
|
-
name: 'token0',
|
|
20
|
-
inputs: [],
|
|
21
|
-
outputs: [{ name: '', type: 'address' }],
|
|
22
|
-
stateMutability: 'view'
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
type: 'function',
|
|
26
|
-
name: 'token1',
|
|
27
|
-
inputs: [],
|
|
28
|
-
outputs: [{ name: '', type: 'address' }],
|
|
29
|
-
stateMutability: 'view'
|
|
30
|
-
}
|
|
31
|
-
]
|
|
32
|
-
}
|
|
33
|
-
}));
|
|
34
|
-
|
|
35
4
|
describe('estimateLpTokens', () => {
|
|
36
5
|
let publicClient: any;
|
|
37
6
|
const mockVaultAddress = '0xd6bac58d87772fa3a7400f15fd121b7edc30ce3c' as Address;
|
|
38
|
-
const mockToken0Address = '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619';
|
|
39
|
-
const mockToken1Address = '0xc708d6f2153933daa50b2d0758955be0a93a8fec';
|
|
40
7
|
|
|
41
8
|
beforeEach(() => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
9
|
+
publicClient = {
|
|
10
|
+
getChainId: jest.fn().mockResolvedValue(137),
|
|
11
|
+
readContract: jest.fn(),
|
|
12
|
+
};
|
|
46
13
|
});
|
|
47
14
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
expect(result.success).toBe(true);
|
|
66
|
-
expect(result.status).toBe(200);
|
|
67
|
-
expect(result.data).toBeDefined();
|
|
68
|
-
|
|
69
|
-
// Final amounts should be:
|
|
70
|
-
// token0: 100 - 50 + 50 = 100 (but we get 50 because 50 was swapped out)
|
|
71
|
-
// token1: 0 + 50 = 50
|
|
72
|
-
const expectedFinalAmount0 = parseEther('50'); // 100 - 50 (swapped) + 50 (from swap, but this is already deducted)
|
|
73
|
-
const expectedFinalAmount1 = parseEther('50'); // 0 + 50 (from swap)
|
|
74
|
-
|
|
75
|
-
expect(result.data?.finalAmount0).toBe(expectedFinalAmount0);
|
|
76
|
-
expect(result.data?.finalAmount1).toBe(expectedFinalAmount1);
|
|
77
|
-
|
|
78
|
-
// LP tokens calculation: min(amount0 * totalSupply / reserve0, amount1 * totalSupply / reserve1)
|
|
79
|
-
// LP0 = 50 * 1000 / 500 = 100
|
|
80
|
-
// LP1 = 50 * 1000 / 500 = 100
|
|
81
|
-
// LP = min(100, 100) = 100
|
|
82
|
-
expect(result.data?.lpTokens).toBe(parseEther('100'));
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('should estimate LP tokens for token1 deposit', async () => {
|
|
86
|
-
const mockTotalSupply = parseEther('2000');
|
|
87
|
-
const mockToken0Balance = parseEther('1000');
|
|
88
|
-
const mockToken1Balance = parseEther('800');
|
|
89
|
-
|
|
90
|
-
publicClient.readContract = jest.fn()
|
|
91
|
-
.mockResolvedValueOnce(mockTotalSupply)
|
|
92
|
-
.mockResolvedValueOnce(mockToken0Address)
|
|
93
|
-
.mockResolvedValueOnce(mockToken1Address)
|
|
94
|
-
.mockResolvedValueOnce(mockToken0Balance)
|
|
95
|
-
.mockResolvedValueOnce(mockToken1Balance);
|
|
96
|
-
|
|
97
|
-
const params = {
|
|
98
|
-
vault: mockVaultAddress,
|
|
99
|
-
originalAssets: parseEther('80'), // Depositing 80 token1
|
|
100
|
-
swapAmount: parseEther('40'), // Swapping 40 token1
|
|
101
|
-
swapResult: {
|
|
102
|
-
amount0: parseEther('60'), // 60 token0 in
|
|
103
|
-
amount1: -parseEther('40'), // 40 token1 out
|
|
104
|
-
sqrtPriceX96After: BigInt('79228162514264337593543950336')
|
|
105
|
-
},
|
|
106
|
-
isToken0: false
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const result = await estimateLpTokens(publicClient, params);
|
|
110
|
-
|
|
111
|
-
expect(result.success).toBe(true);
|
|
112
|
-
|
|
113
|
-
// Final amounts should be:
|
|
114
|
-
// token0: 0 + 60 = 60
|
|
115
|
-
// token1: 80 - 40 + 40 = 80 (but we get 40 because 40 was swapped out)
|
|
116
|
-
const expectedFinalAmount0 = parseEther('60');
|
|
117
|
-
const expectedFinalAmount1 = parseEther('40'); // 80 - 40 (swapped out)
|
|
118
|
-
|
|
119
|
-
expect(result.data?.finalAmount0).toBe(expectedFinalAmount0);
|
|
120
|
-
expect(result.data?.finalAmount1).toBe(expectedFinalAmount1);
|
|
121
|
-
|
|
122
|
-
// LP calculation: min(60 * 2000 / 1000, 40 * 2000 / 800) = min(120, 100) = 100
|
|
123
|
-
expect(result.data?.lpTokens).toBe(parseEther('100'));
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('should handle first deposit (zero total supply)', async () => {
|
|
127
|
-
const mockTotalSupply = BigInt('0'); // First deposit
|
|
128
|
-
const mockToken0Balance = BigInt('0');
|
|
129
|
-
const mockToken1Balance = BigInt('0');
|
|
130
|
-
|
|
131
|
-
publicClient.readContract = jest.fn()
|
|
132
|
-
.mockResolvedValueOnce(mockTotalSupply)
|
|
133
|
-
.mockResolvedValueOnce(mockToken0Address)
|
|
134
|
-
.mockResolvedValueOnce(mockToken1Address)
|
|
135
|
-
.mockResolvedValueOnce(mockToken0Balance)
|
|
136
|
-
.mockResolvedValueOnce(mockToken1Balance);
|
|
137
|
-
|
|
138
|
-
const params = {
|
|
139
|
-
vault: mockVaultAddress,
|
|
140
|
-
originalAssets: parseEther('100'),
|
|
141
|
-
swapAmount: parseEther('50'),
|
|
142
|
-
swapResult: {
|
|
143
|
-
amount0: -parseEther('50'),
|
|
144
|
-
amount1: parseEther('100'),
|
|
145
|
-
sqrtPriceX96After: BigInt('79228162514264337593543950336')
|
|
146
|
-
},
|
|
147
|
-
isToken0: true
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const result = await estimateLpTokens(publicClient, params);
|
|
151
|
-
|
|
152
|
-
expect(result.success).toBe(true);
|
|
153
|
-
|
|
154
|
-
const expectedFinalAmount0 = parseEther('50');
|
|
155
|
-
const expectedFinalAmount1 = parseEther('100');
|
|
156
|
-
|
|
157
|
-
expect(result.data?.finalAmount0).toBe(expectedFinalAmount0);
|
|
158
|
-
expect(result.data?.finalAmount1).toBe(expectedFinalAmount1);
|
|
159
|
-
|
|
160
|
-
// For first deposit, LP = sqrt(amount0 * amount1) = sqrt(50 * 100) = sqrt(5000) ≈ 70.71
|
|
161
|
-
const expectedLpTokens = BigInt('70710678118654752440'); // sqrt(50 * 100 * 10^36)
|
|
162
|
-
expect(result.data?.lpTokens).toBe(expectedLpTokens);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('should handle imbalanced pool scenario', async () => {
|
|
166
|
-
const mockTotalSupply = parseEther('1500');
|
|
167
|
-
const mockToken0Balance = parseEther('300'); // Much less token0
|
|
168
|
-
const mockToken1Balance = parseEther('1200'); // Much more token1
|
|
169
|
-
|
|
170
|
-
publicClient.readContract = jest.fn()
|
|
171
|
-
.mockResolvedValueOnce(mockTotalSupply)
|
|
172
|
-
.mockResolvedValueOnce(mockToken0Address)
|
|
173
|
-
.mockResolvedValueOnce(mockToken1Address)
|
|
174
|
-
.mockResolvedValueOnce(mockToken0Balance)
|
|
175
|
-
.mockResolvedValueOnce(mockToken1Balance);
|
|
176
|
-
|
|
177
|
-
const params = {
|
|
178
|
-
vault: mockVaultAddress,
|
|
179
|
-
originalAssets: parseEther('100'),
|
|
180
|
-
swapAmount: parseEther('80'), // Large swap due to imbalance
|
|
181
|
-
swapResult: {
|
|
182
|
-
amount0: -parseEther('80'),
|
|
183
|
-
amount1: parseEther('40'), // Lower amount due to imbalanced pool
|
|
184
|
-
sqrtPriceX96After: BigInt('79228162514264337593543950336')
|
|
185
|
-
},
|
|
186
|
-
isToken0: true
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
const result = await estimateLpTokens(publicClient, params);
|
|
190
|
-
|
|
191
|
-
expect(result.success).toBe(true);
|
|
192
|
-
|
|
193
|
-
const expectedFinalAmount0 = parseEther('20'); // 100 - 80 (swapped out)
|
|
194
|
-
const expectedFinalAmount1 = parseEther('40'); // 0 + 40 (from swap)
|
|
195
|
-
|
|
196
|
-
expect(result.data?.finalAmount0).toBe(expectedFinalAmount0);
|
|
197
|
-
expect(result.data?.finalAmount1).toBe(expectedFinalAmount1);
|
|
198
|
-
|
|
199
|
-
// LP calculation: min(20 * 1500 / 300, 40 * 1500 / 1200) = min(100, 50) = 50
|
|
200
|
-
expect(result.data?.lpTokens).toBe(parseEther('50'));
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('should handle small amounts', async () => {
|
|
204
|
-
const mockTotalSupply = parseEther('1000000'); // Large pool
|
|
205
|
-
const mockToken0Balance = parseEther('500000');
|
|
206
|
-
const mockToken1Balance = parseEther('500000');
|
|
207
|
-
|
|
208
|
-
publicClient.readContract = jest.fn()
|
|
209
|
-
.mockResolvedValueOnce(mockTotalSupply)
|
|
210
|
-
.mockResolvedValueOnce(mockToken0Address)
|
|
211
|
-
.mockResolvedValueOnce(mockToken1Address)
|
|
212
|
-
.mockResolvedValueOnce(mockToken0Balance)
|
|
213
|
-
.mockResolvedValueOnce(mockToken1Balance);
|
|
214
|
-
|
|
215
|
-
const smallAmount = BigInt('1000000000000000'); // 0.001 tokens
|
|
216
|
-
const params = {
|
|
217
|
-
vault: mockVaultAddress,
|
|
218
|
-
originalAssets: smallAmount * 2n,
|
|
219
|
-
swapAmount: smallAmount,
|
|
220
|
-
swapResult: {
|
|
221
|
-
amount0: -smallAmount,
|
|
222
|
-
amount1: smallAmount,
|
|
223
|
-
sqrtPriceX96After: BigInt('79228162514264337593543950336')
|
|
224
|
-
},
|
|
225
|
-
isToken0: true
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
const result = await estimateLpTokens(publicClient, params);
|
|
229
|
-
|
|
230
|
-
expect(result.success).toBe(true);
|
|
231
|
-
expect(result.data?.finalAmount0).toBe(smallAmount);
|
|
232
|
-
expect(result.data?.finalAmount1).toBe(smallAmount);
|
|
233
|
-
|
|
234
|
-
// Should get proportional LP tokens
|
|
235
|
-
const expectedLpTokens = smallAmount * 2n; // 2 * 0.001 = 0.002 LP
|
|
236
|
-
expect(result.data?.lpTokens).toBe(expectedLpTokens);
|
|
15
|
+
it('estimates LP tokens for token0 deposit', async () => {
|
|
16
|
+
publicClient.readContract
|
|
17
|
+
.mockResolvedValueOnce(parseEther('1000'))
|
|
18
|
+
.mockResolvedValueOnce([parseEther('500'), parseEther('500')])
|
|
19
|
+
.mockResolvedValueOnce([parseEther('100'), parseEther('50'), parseEther('50')]);
|
|
20
|
+
|
|
21
|
+
const result = await estimateLpTokens(publicClient, {
|
|
22
|
+
vault: mockVaultAddress,
|
|
23
|
+
originalAssets: parseEther('100'),
|
|
24
|
+
swapAmount: parseEther('50'),
|
|
25
|
+
swapResult: {
|
|
26
|
+
amount0: -parseEther('50'),
|
|
27
|
+
amount1: parseEther('50'),
|
|
28
|
+
sqrtPriceX96After: 0n,
|
|
29
|
+
},
|
|
30
|
+
isToken0: true,
|
|
237
31
|
});
|
|
238
32
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
.mockResolvedValueOnce(mockTotalSupply)
|
|
246
|
-
.mockResolvedValueOnce(mockToken0Address)
|
|
247
|
-
.mockResolvedValueOnce(mockToken1Address)
|
|
248
|
-
.mockResolvedValueOnce(mockToken0Balance)
|
|
249
|
-
.mockResolvedValueOnce(mockToken1Balance);
|
|
250
|
-
|
|
251
|
-
const params = {
|
|
252
|
-
vault: mockVaultAddress,
|
|
253
|
-
originalAssets: parseEther('100'),
|
|
254
|
-
swapAmount: parseEther('50'),
|
|
255
|
-
swapResult: {
|
|
256
|
-
amount0: -parseEther('50'),
|
|
257
|
-
amount1: parseEther('50')
|
|
258
|
-
},
|
|
259
|
-
isToken0: true
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
const result = await estimateLpTokens(publicClient, params);
|
|
263
|
-
|
|
264
|
-
expect(result.success).toBe(true);
|
|
265
|
-
|
|
266
|
-
// With zero token0 reserve, LP from token0 should be 0
|
|
267
|
-
// LP = min(0, 50 * 1000 / 1000) = min(0, 50) = 0
|
|
268
|
-
expect(result.data?.lpTokens).toBe(BigInt('0'));
|
|
269
|
-
});
|
|
33
|
+
expect(result.success).toBe(true);
|
|
34
|
+
expect(result.status).toBe(200);
|
|
35
|
+
expect(result.data?.lpTokens).toBe(parseEther('100'));
|
|
36
|
+
expect(result.data?.finalAmount0).toBe(parseEther('50'));
|
|
37
|
+
expect(result.data?.finalAmount1).toBe(parseEther('50'));
|
|
38
|
+
expect(publicClient.getChainId).toHaveBeenCalledTimes(1);
|
|
270
39
|
});
|
|
271
40
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
expect(result.success).toBe(false);
|
|
290
|
-
expect(result.status).toBe(500);
|
|
291
|
-
expect(result.error).toBe('Contract call failed');
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
it('should handle network errors', async () => {
|
|
295
|
-
publicClient.readContract = jest.fn().mockRejectedValue(new Error('Network error'));
|
|
296
|
-
|
|
297
|
-
const params = {
|
|
298
|
-
vault: mockVaultAddress,
|
|
299
|
-
originalAssets: parseEther('100'),
|
|
300
|
-
swapAmount: parseEther('50'),
|
|
301
|
-
swapResult: {
|
|
302
|
-
amount0: -parseEther('50'),
|
|
303
|
-
amount1: parseEther('50')
|
|
304
|
-
},
|
|
305
|
-
isToken0: true
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
const result = await estimateLpTokens(publicClient, params);
|
|
309
|
-
|
|
310
|
-
expect(result.success).toBe(false);
|
|
311
|
-
expect(result.error).toBe('Network error');
|
|
41
|
+
it('estimates LP tokens for token1 deposit', async () => {
|
|
42
|
+
publicClient.readContract
|
|
43
|
+
.mockResolvedValueOnce(parseEther('1000'))
|
|
44
|
+
.mockResolvedValueOnce([parseEther('400'), parseEther('600')])
|
|
45
|
+
.mockResolvedValueOnce([parseEther('80'), parseEther('20'), parseEther('60')]);
|
|
46
|
+
|
|
47
|
+
const result = await estimateLpTokens(publicClient, {
|
|
48
|
+
vault: mockVaultAddress,
|
|
49
|
+
originalAssets: parseEther('100'),
|
|
50
|
+
swapAmount: parseEther('40'),
|
|
51
|
+
swapResult: {
|
|
52
|
+
amount0: parseEther('20'),
|
|
53
|
+
amount1: -parseEther('40'),
|
|
54
|
+
sqrtPriceX96After: 0n,
|
|
55
|
+
},
|
|
56
|
+
isToken0: false,
|
|
312
57
|
});
|
|
313
58
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
const params = {
|
|
320
|
-
vault: '0x0000000000000000000000000000000000000000' as Address,
|
|
321
|
-
originalAssets: parseEther('100'),
|
|
322
|
-
swapAmount: parseEther('50'),
|
|
323
|
-
swapResult: {
|
|
324
|
-
amount0: -parseEther('50'),
|
|
325
|
-
amount1: parseEther('50')
|
|
326
|
-
},
|
|
327
|
-
isToken0: true
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
const result = await estimateLpTokens(publicClient, params);
|
|
331
|
-
|
|
332
|
-
expect(result.success).toBe(false);
|
|
333
|
-
expect(result.error).toContain('Contract not found');
|
|
334
|
-
});
|
|
59
|
+
expect(result.success).toBe(true);
|
|
60
|
+
expect(result.data?.lpTokens).toBe(parseEther('80'));
|
|
61
|
+
expect(result.data?.finalAmount0).toBe(parseEther('20'));
|
|
62
|
+
expect(result.data?.finalAmount1).toBe(parseEther('60'));
|
|
335
63
|
});
|
|
336
64
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const params = {
|
|
351
|
-
vault: mockVaultAddress,
|
|
352
|
-
originalAssets: parseEther('100'),
|
|
353
|
-
swapAmount: parseEther('50'),
|
|
354
|
-
swapResult: {
|
|
355
|
-
amount0: parseEther('50'), // Positive amount0 (receiving)
|
|
356
|
-
amount1: -parseEther('25') // Negative amount1 (giving)
|
|
357
|
-
},
|
|
358
|
-
isToken0: false // Depositing token1, swapping to token0
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
const result = await estimateLpTokens(publicClient, params);
|
|
362
|
-
|
|
363
|
-
expect(result.success).toBe(true);
|
|
364
|
-
|
|
365
|
-
// For token1 deposit: finalAmount0 = 0 + |amount0| = 50
|
|
366
|
-
// finalAmount1 = originalAssets - swapAmount + |amount1| = 100 - 50 + 25 = 75
|
|
367
|
-
expect(result.data?.finalAmount0).toBe(parseEther('50'));
|
|
368
|
-
expect(result.data?.finalAmount1).toBe(parseEther('75'));
|
|
65
|
+
it('returns failure when on-chain reads fail', async () => {
|
|
66
|
+
publicClient.readContract.mockRejectedValue(new Error('Contract call failed'));
|
|
67
|
+
|
|
68
|
+
const result = await estimateLpTokens(publicClient, {
|
|
69
|
+
vault: mockVaultAddress,
|
|
70
|
+
originalAssets: parseEther('100'),
|
|
71
|
+
swapAmount: parseEther('50'),
|
|
72
|
+
swapResult: {
|
|
73
|
+
amount0: -parseEther('50'),
|
|
74
|
+
amount1: parseEther('50'),
|
|
75
|
+
sqrtPriceX96After: 0n,
|
|
76
|
+
},
|
|
77
|
+
isToken0: true,
|
|
369
78
|
});
|
|
370
79
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
publicClient.readContract = jest.fn()
|
|
376
|
-
.mockResolvedValueOnce(largeSupply)
|
|
377
|
-
.mockResolvedValueOnce(mockToken0Address)
|
|
378
|
-
.mockResolvedValueOnce(mockToken1Address)
|
|
379
|
-
.mockResolvedValueOnce(largeBalance)
|
|
380
|
-
.mockResolvedValueOnce(largeBalance);
|
|
381
|
-
|
|
382
|
-
const params = {
|
|
383
|
-
vault: mockVaultAddress,
|
|
384
|
-
originalAssets: parseEther('1000000'), // 1 million
|
|
385
|
-
swapAmount: parseEther('500000'), // 500k
|
|
386
|
-
swapResult: {
|
|
387
|
-
amount0: -parseEther('500000'),
|
|
388
|
-
amount1: parseEther('500000')
|
|
389
|
-
},
|
|
390
|
-
isToken0: true
|
|
391
|
-
};
|
|
392
|
-
|
|
393
|
-
const result = await estimateLpTokens(publicClient, params);
|
|
80
|
+
expect(result.success).toBe(false);
|
|
81
|
+
expect(result.status).toBe(500);
|
|
82
|
+
expect(result.error).toBe('Contract call failed');
|
|
83
|
+
});
|
|
394
84
|
|
|
395
|
-
|
|
396
|
-
|
|
85
|
+
it('returns failure when helper getShares reverts', async () => {
|
|
86
|
+
publicClient.readContract
|
|
87
|
+
.mockResolvedValueOnce(parseEther('1000'))
|
|
88
|
+
.mockResolvedValueOnce([parseEther('500'), parseEther('500')])
|
|
89
|
+
.mockRejectedValueOnce(new Error('getShares reverted'));
|
|
90
|
+
|
|
91
|
+
const result = await estimateLpTokens(publicClient, {
|
|
92
|
+
vault: mockVaultAddress,
|
|
93
|
+
originalAssets: parseEther('100'),
|
|
94
|
+
swapAmount: parseEther('50'),
|
|
95
|
+
swapResult: {
|
|
96
|
+
amount0: -parseEther('50'),
|
|
97
|
+
amount1: parseEther('50'),
|
|
98
|
+
sqrtPriceX96After: 0n,
|
|
99
|
+
},
|
|
100
|
+
isToken0: true,
|
|
397
101
|
});
|
|
398
102
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const mockToken0Balance = BigInt('500000000000000000000'); // 500 tokens
|
|
402
|
-
const mockToken1Balance = BigInt('500000000000000000000'); // 500 tokens
|
|
403
|
-
|
|
404
|
-
publicClient.readContract = jest.fn()
|
|
405
|
-
.mockResolvedValueOnce(mockTotalSupply)
|
|
406
|
-
.mockResolvedValueOnce(mockToken0Address)
|
|
407
|
-
.mockResolvedValueOnce(mockToken1Address)
|
|
408
|
-
.mockResolvedValueOnce(mockToken0Balance)
|
|
409
|
-
.mockResolvedValueOnce(mockToken1Balance);
|
|
410
|
-
|
|
411
|
-
// Very small amounts to test precision
|
|
412
|
-
const params = {
|
|
413
|
-
vault: mockVaultAddress,
|
|
414
|
-
originalAssets: BigInt('1000000000000000'), // 0.001 tokens
|
|
415
|
-
swapAmount: BigInt('500000000000000'), // 0.0005 tokens
|
|
416
|
-
swapResult: {
|
|
417
|
-
amount0: -BigInt('500000000000000'),
|
|
418
|
-
amount1: BigInt('500000000000000')
|
|
419
|
-
},
|
|
420
|
-
isToken0: true
|
|
421
|
-
};
|
|
422
|
-
|
|
423
|
-
const result = await estimateLpTokens(publicClient, params);
|
|
424
|
-
|
|
425
|
-
expect(result.success).toBe(true);
|
|
426
|
-
expect(result.data?.lpTokens).toBeGreaterThan(BigInt('0'));
|
|
427
|
-
|
|
428
|
-
// Should maintain precision
|
|
429
|
-
const expectedFinalAmount0 = BigInt('500000000000000');
|
|
430
|
-
const expectedFinalAmount1 = BigInt('500000000000000');
|
|
431
|
-
expect(result.data?.finalAmount0).toBe(expectedFinalAmount0);
|
|
432
|
-
expect(result.data?.finalAmount1).toBe(expectedFinalAmount1);
|
|
433
|
-
});
|
|
103
|
+
expect(result.success).toBe(false);
|
|
104
|
+
expect(result.error).toBe('getShares reverted');
|
|
434
105
|
});
|
|
435
106
|
});
|
|
436
107
|
|
|
@@ -441,172 +112,36 @@ describe('getVaultReserves', () => {
|
|
|
441
112
|
const mockToken1Address = '0x3456789012345678901234567890123456789012' as `0x${string}`;
|
|
442
113
|
|
|
443
114
|
beforeEach(() => {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
mode: 'anvil',
|
|
448
|
-
transport: http(),
|
|
449
|
-
}).extend(publicActions);
|
|
450
|
-
|
|
451
|
-
publicClient = testClient;
|
|
115
|
+
publicClient = {
|
|
116
|
+
readContract: jest.fn(),
|
|
117
|
+
};
|
|
452
118
|
});
|
|
453
119
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
.mockResolvedValueOnce(mockTotalSupply) // totalSupply
|
|
462
|
-
.mockResolvedValueOnce(mockToken0Address) // token0 address
|
|
463
|
-
.mockResolvedValueOnce(mockToken1Address) // token1 address
|
|
464
|
-
.mockResolvedValueOnce(mockToken0Balance) // token0 balanceOf
|
|
465
|
-
.mockResolvedValueOnce(mockToken1Balance); // token1 balanceOf
|
|
466
|
-
|
|
467
|
-
const result = await getVaultReserves(publicClient, mockVaultAddress);
|
|
120
|
+
it('gets vault reserves correctly', async () => {
|
|
121
|
+
publicClient.readContract
|
|
122
|
+
.mockResolvedValueOnce(parseEther('1000'))
|
|
123
|
+
.mockResolvedValueOnce(mockToken0Address)
|
|
124
|
+
.mockResolvedValueOnce(mockToken1Address)
|
|
125
|
+
.mockResolvedValueOnce(parseEther('400'))
|
|
126
|
+
.mockResolvedValueOnce(parseEther('600'));
|
|
468
127
|
|
|
469
|
-
|
|
470
|
-
expect(result.status).toBe(200);
|
|
471
|
-
expect(result.data).toEqual({
|
|
472
|
-
token0Balance: mockToken0Balance,
|
|
473
|
-
token1Balance: mockToken1Balance,
|
|
474
|
-
totalSupply: mockTotalSupply
|
|
475
|
-
});
|
|
128
|
+
const result = await getVaultReserves(publicClient, mockVaultAddress);
|
|
476
129
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
const mockTotalSupply = BigInt('0');
|
|
483
|
-
const mockToken0Balance = BigInt('0');
|
|
484
|
-
const mockToken1Balance = BigInt('0');
|
|
485
|
-
|
|
486
|
-
publicClient.readContract = jest.fn()
|
|
487
|
-
.mockResolvedValueOnce(mockTotalSupply)
|
|
488
|
-
.mockResolvedValueOnce(mockToken0Address)
|
|
489
|
-
.mockResolvedValueOnce(mockToken1Address)
|
|
490
|
-
.mockResolvedValueOnce(mockToken0Balance)
|
|
491
|
-
.mockResolvedValueOnce(mockToken1Balance);
|
|
492
|
-
|
|
493
|
-
const result = await getVaultReserves(publicClient, mockVaultAddress);
|
|
494
|
-
|
|
495
|
-
expect(result.success).toBe(true);
|
|
496
|
-
expect(result.data?.totalSupply).toBe(BigInt('0'));
|
|
497
|
-
expect(result.data?.token0Balance).toBe(BigInt('0'));
|
|
498
|
-
expect(result.data?.token1Balance).toBe(BigInt('0'));
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
it('should handle very large reserves', async () => {
|
|
502
|
-
const largeTotalSupply = parseEther('1000000000'); // 1 billion
|
|
503
|
-
const largeToken0Balance = parseEther('500000000');
|
|
504
|
-
const largeToken1Balance = parseEther('750000000');
|
|
505
|
-
|
|
506
|
-
publicClient.readContract = jest.fn()
|
|
507
|
-
.mockResolvedValueOnce(largeTotalSupply)
|
|
508
|
-
.mockResolvedValueOnce(mockToken0Address)
|
|
509
|
-
.mockResolvedValueOnce(mockToken1Address)
|
|
510
|
-
.mockResolvedValueOnce(largeToken0Balance)
|
|
511
|
-
.mockResolvedValueOnce(largeToken1Balance);
|
|
512
|
-
|
|
513
|
-
const result = await getVaultReserves(publicClient, mockVaultAddress);
|
|
514
|
-
|
|
515
|
-
expect(result.success).toBe(true);
|
|
516
|
-
expect(result.data?.totalSupply).toBe(largeTotalSupply);
|
|
517
|
-
expect(result.data?.token0Balance).toBe(largeToken0Balance);
|
|
518
|
-
expect(result.data?.token1Balance).toBe(largeToken1Balance);
|
|
519
|
-
});
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
describe('error handling', () => {
|
|
523
|
-
it('should handle vault contract errors', async () => {
|
|
524
|
-
publicClient.readContract = jest.fn().mockRejectedValue(new Error('Vault contract error'));
|
|
525
|
-
|
|
526
|
-
const result = await getVaultReserves(publicClient, mockVaultAddress);
|
|
527
|
-
|
|
528
|
-
expect(result.success).toBe(false);
|
|
529
|
-
expect(result.status).toBe(500);
|
|
530
|
-
expect(result.error).toBe('Vault contract error');
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
it('should handle token contract errors', async () => {
|
|
534
|
-
publicClient.readContract = jest.fn()
|
|
535
|
-
.mockResolvedValueOnce(parseEther('1000')) // totalSupply succeeds
|
|
536
|
-
.mockResolvedValueOnce(mockToken0Address) // token0 address succeeds
|
|
537
|
-
.mockResolvedValueOnce(mockToken1Address) // token1 address succeeds
|
|
538
|
-
.mockRejectedValue(new Error('Token contract error')); // balanceOf fails
|
|
539
|
-
|
|
540
|
-
const result = await getVaultReserves(publicClient, mockVaultAddress);
|
|
541
|
-
|
|
542
|
-
expect(result.success).toBe(false);
|
|
543
|
-
expect(result.error).toBe('Token contract error');
|
|
544
|
-
});
|
|
545
|
-
|
|
546
|
-
it('should handle invalid vault address', async () => {
|
|
547
|
-
const invalidVault = '0x0000000000000000000000000000000000000000' as `0x${string}`;
|
|
548
|
-
|
|
549
|
-
publicClient.readContract = jest.fn().mockRejectedValue(
|
|
550
|
-
new Error('Contract not found')
|
|
551
|
-
);
|
|
552
|
-
|
|
553
|
-
const result = await getVaultReserves(publicClient, invalidVault);
|
|
554
|
-
|
|
555
|
-
expect(result.success).toBe(false);
|
|
556
|
-
expect(result.error).toContain('Contract not found');
|
|
130
|
+
expect(result.success).toBe(true);
|
|
131
|
+
expect(result.data).toEqual({
|
|
132
|
+
token0Balance: parseEther('400'),
|
|
133
|
+
token1Balance: parseEther('600'),
|
|
134
|
+
totalSupply: parseEther('1000'),
|
|
557
135
|
});
|
|
558
136
|
});
|
|
559
137
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
// Simulate a realistic USDC/ETH vault
|
|
563
|
-
const mockTotalSupply = parseEther('50000'); // 50k LP tokens
|
|
564
|
-
const mockUsdcBalance = BigInt('25000000000'); // 25k USDC (6 decimals)
|
|
565
|
-
const mockEthBalance = parseEther('12.5'); // 12.5 ETH (18 decimals)
|
|
566
|
-
|
|
567
|
-
publicClient.readContract = jest.fn()
|
|
568
|
-
.mockResolvedValueOnce(mockTotalSupply)
|
|
569
|
-
.mockResolvedValueOnce(mockToken0Address) // USDC
|
|
570
|
-
.mockResolvedValueOnce(mockToken1Address) // ETH
|
|
571
|
-
.mockResolvedValueOnce(mockUsdcBalance)
|
|
572
|
-
.mockResolvedValueOnce(mockEthBalance);
|
|
138
|
+
it('returns failure when vault calls fail', async () => {
|
|
139
|
+
publicClient.readContract.mockRejectedValue(new Error('Vault contract error'));
|
|
573
140
|
|
|
574
|
-
|
|
141
|
+
const result = await getVaultReserves(publicClient, mockVaultAddress);
|
|
575
142
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
expect(result.data?.totalSupply).toBe(mockTotalSupply);
|
|
580
|
-
});
|
|
581
|
-
|
|
582
|
-
it('should handle sequential calls correctly', async () => {
|
|
583
|
-
const calls = [
|
|
584
|
-
parseEther('1000'), // totalSupply
|
|
585
|
-
mockToken0Address, // token0
|
|
586
|
-
mockToken1Address, // token1
|
|
587
|
-
parseEther('500'), // token0 balance
|
|
588
|
-
parseEther('500') // token1 balance
|
|
589
|
-
];
|
|
590
|
-
|
|
591
|
-
publicClient.readContract = jest.fn()
|
|
592
|
-
.mockResolvedValueOnce(calls[0])
|
|
593
|
-
.mockResolvedValueOnce(calls[1])
|
|
594
|
-
.mockResolvedValueOnce(calls[2])
|
|
595
|
-
.mockResolvedValueOnce(calls[3])
|
|
596
|
-
.mockResolvedValueOnce(calls[4]);
|
|
597
|
-
|
|
598
|
-
const result = await getVaultReserves(publicClient, mockVaultAddress);
|
|
599
|
-
|
|
600
|
-
expect(result.success).toBe(true);
|
|
601
|
-
expect(publicClient.readContract).toHaveBeenCalledTimes(5);
|
|
602
|
-
|
|
603
|
-
// Verify the correct ABI functions were called
|
|
604
|
-
const readContractCalls = (publicClient.readContract as jest.Mock).mock.calls;
|
|
605
|
-
expect(readContractCalls[0][0].functionName).toBe('totalSupply');
|
|
606
|
-
expect(readContractCalls[1][0].functionName).toBe('token0');
|
|
607
|
-
expect(readContractCalls[2][0].functionName).toBe('token1');
|
|
608
|
-
expect(readContractCalls[3][0].functionName).toBe('balanceOf');
|
|
609
|
-
expect(readContractCalls[4][0].functionName).toBe('balanceOf');
|
|
610
|
-
});
|
|
143
|
+
expect(result.success).toBe(false);
|
|
144
|
+
expect(result.status).toBe(500);
|
|
145
|
+
expect(result.error).toBe('Vault contract error');
|
|
611
146
|
});
|
|
612
|
-
});
|
|
147
|
+
});
|