@dhedge/v2-sdk 2.1.7 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +400 -53
  2. package/dist/config.d.ts +14 -2
  3. package/dist/entities/pool.d.ts +25 -86
  4. package/dist/entities/utils.d.ts +15 -0
  5. package/dist/services/cowSwap/index.d.ts +10 -0
  6. package/dist/services/hyperliquid/index.d.ts +22 -0
  7. package/dist/services/kyberSwap/index.d.ts +1 -1
  8. package/dist/services/oneInch/index.d.ts +1 -1
  9. package/dist/services/toros/easySwapper.d.ts +14 -0
  10. package/dist/services/toros/swapData.d.ts +5 -5
  11. package/dist/services/uniswap/V3Liquidity.d.ts +2 -2
  12. package/dist/services/velodrome/liquidity.d.ts +3 -0
  13. package/dist/test/constants.d.ts +48 -3
  14. package/dist/test/utils/testingHelper.d.ts +4 -0
  15. package/dist/types.d.ts +21 -5
  16. package/dist/utils/contract.d.ts +20 -0
  17. package/dist/v2-sdk.cjs.development.js +5193 -6711
  18. package/dist/v2-sdk.cjs.development.js.map +1 -1
  19. package/dist/v2-sdk.cjs.production.min.js +1 -1
  20. package/dist/v2-sdk.cjs.production.min.js.map +1 -1
  21. package/dist/v2-sdk.esm.js +5198 -6711
  22. package/dist/v2-sdk.esm.js.map +1 -1
  23. package/package.json +1 -1
  24. package/src/abi/PoolFactory.json +414 -204
  25. package/src/abi/PoolLogic.json +160 -134
  26. package/src/config.ts +19 -9
  27. package/src/entities/pool.ts +103 -254
  28. package/src/entities/utils.ts +15 -0
  29. package/src/services/cowSwap/index.ts +281 -0
  30. package/src/services/hyperliquid/index.ts +22 -0
  31. package/src/services/kyberSwap/index.ts +5 -3
  32. package/src/services/oneInch/index.ts +5 -4
  33. package/src/services/toros/completeWithdrawal.ts +57 -40
  34. package/src/services/toros/easySwapper.ts +15 -1
  35. package/src/services/toros/initWithdrawal.ts +39 -31
  36. package/src/services/toros/swapData.ts +45 -131
  37. package/src/services/uniswap/V3Liquidity.ts +3 -24
  38. package/src/services/velodrome/liquidity.ts +3 -0
  39. package/src/test/aave.test.ts +99 -70
  40. package/src/test/aerodrome.test.ts +53 -24
  41. package/src/test/aerodromeCL.test.ts +64 -30
  42. package/src/test/arrakis.test.ts +23 -35
  43. package/src/test/balancer.test.ts +114 -106
  44. package/src/test/compoundV3.test.ts +45 -29
  45. package/src/test/constants.ts +57 -12
  46. package/src/test/cowswap.test.ts +79 -0
  47. package/src/test/dhedge.test.ts +45 -12
  48. package/src/test/flatmoney.test.ts +25 -39
  49. package/src/test/fluid.test.ts +33 -24
  50. package/src/test/hyperliquid.onchain.test.ts +131 -0
  51. package/src/test/kyberSwap.test.ts +37 -16
  52. package/src/test/lyra.test.ts +159 -150
  53. package/src/test/odos.test.ts +2 -2
  54. package/src/test/oneInch.test.ts +36 -22
  55. package/src/test/pancakeCL.test.ts +72 -31
  56. package/src/test/pendle.test.ts +94 -54
  57. package/src/test/{pendleMint.test.ts → pendleMint.onchain.test.ts} +22 -8
  58. package/src/test/pool.test.ts +152 -95
  59. package/src/test/toros.onchain.test.ts +92 -0
  60. package/src/test/toros.test.ts +74 -20
  61. package/src/test/torosLimitOrder.test.ts +87 -42
  62. package/src/test/uniswap.test.ts +77 -128
  63. package/src/test/utils/testingHelper.ts +120 -0
  64. package/src/test/velodrome.test.ts +126 -92
  65. package/src/test/velodromeCL.test.ts +43 -31
  66. package/src/test/velodromeV2.test.ts +153 -95
  67. package/src/types.ts +22 -6
  68. package/src/utils/contract.ts +20 -0
  69. package/dist/services/futures/constants.d.ts +0 -1
  70. package/dist/services/futures/index.d.ts +0 -2
  71. package/dist/services/futures/margin.d.ts +0 -2
  72. package/dist/services/futures/trade.d.ts +0 -3
  73. package/dist/services/ramses/vesting.d.ts +0 -4
  74. package/dist/services/uniswap/V3Trade.d.ts +0 -3
  75. package/dist/test/utils/futures.d.ts +0 -2
  76. package/src/abi/IRamsesNonfungiblePositionManager.json +0 -486
  77. package/src/abi/ISynthetiXFuturesMarketV2.json +0 -531
  78. package/src/abi/ISynthetix.json +0 -139
  79. package/src/abi/IUniswapV3Quoter.json +0 -195
  80. package/src/abi/IUniswapV3Router.json +0 -221
  81. package/src/abi/IXRam.json +0 -99
  82. package/src/services/futures/constants.ts +0 -1
  83. package/src/services/futures/index.ts +0 -2
  84. package/src/services/futures/margin.ts +0 -10
  85. package/src/services/futures/trade.ts +0 -32
  86. package/src/services/ramses/vesting.ts +0 -24
  87. package/src/services/uniswap/V3Trade.ts +0 -46
  88. package/src/test/futures.test.ts +0 -51
  89. package/src/test/hyperliquid.test.ts +0 -107
  90. package/src/test/ramses.test.ts +0 -190
  91. package/src/test/ramsesCL.test.ts +0 -155
  92. package/src/test/synthetix.test.ts +0 -36
  93. package/src/test/utils/futures.ts +0 -14
@@ -35,8 +35,6 @@ export const SUSD = "0x8c6f28f2f1a3c87f0f938b96d27520d9751ec8d9";
35
35
  export const SETH = "0xE405de8F52ba7559f9df3C368500B6E6ae6Cee49";
36
36
  export const ARRAKIS_USDC_WETH_GAUGE =
37
37
  "0xb8888ea29e2f70ad62a3b69b1a1342720612a00d";
38
- export const KWENTA_ETH_PERP = "0xf86048dff23cf130107dfb4e6386f574231a5c65";
39
- export const KWENTA_ETH_PERP_V2 = "0x2b3bb4c683bfc5239b029131eef3b1d214478d93";
40
38
 
41
39
  export const TEST_POOL = {
42
40
  [Network.POLYGON]: "0x699fd4d6eadb216704c7e355cfa0a12f51813163",
@@ -50,7 +48,7 @@ export const TEST_POOL = {
50
48
 
51
49
  export const CONTRACT_ADDRESS = {
52
50
  [Network.POLYGON]: {
53
- USDC: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
51
+ USDC: "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
54
52
  USDT: "",
55
53
  USDE: "",
56
54
  SUSD: "",
@@ -68,7 +66,10 @@ export const CONTRACT_ADDRESS = {
68
66
  COMPOUNDV3_WETH: "",
69
67
  FLUID_WETH: "",
70
68
  TOROS: "",
71
- UNIT: ""
69
+ UNIT: "",
70
+ WSTETH: "",
71
+ BALANCER_WSTETH_WETH_POOL: "",
72
+ BALANCER_WSTETH_WETH_GAUGE: ""
72
73
  },
73
74
 
74
75
  [Network.OPTIMISM]: {
@@ -79,7 +80,6 @@ export const CONTRACT_ADDRESS = {
79
80
  SUSD: "0x8c6f28f2f1a3c87f0f938b96d27520d9751ec8d9",
80
81
  WETH: "0x4200000000000000000000000000000000000006",
81
82
  WBTC: "0x68f180fcCe6836688e9084f035309E29Bf0A2095",
82
- KWENTA_ETH_PERP_V2: "0x2b3bb4c683bfc5239b029131eef3b1d214478d93",
83
83
  uniswapV3: {
84
84
  nonfungiblePositionManager: "0xC36442b4a4522E871399CD717aBDD847Ab11FE88"
85
85
  },
@@ -93,7 +93,10 @@ export const CONTRACT_ADDRESS = {
93
93
  VELO: "0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db",
94
94
  COMPOUNDV3_WETH: "",
95
95
  FLUID_WETH: "",
96
- TOROS: "0xcacb5a722a36cff6baeb359e21c098a4acbffdfa" //ETHBEAR1X
96
+ TOROS: "0xcacb5a722a36cff6baeb359e21c098a4acbffdfa", //ETHBEAR1X
97
+ WSTETH: "",
98
+ BALANCER_WSTETH_WETH_POOL: "",
99
+ BALANCER_WSTETH_WETH_GAUGE: ""
97
100
  },
98
101
  [Network.ARBITRUM]: {
99
102
  USDC: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
@@ -121,7 +124,7 @@ export const CONTRACT_ADDRESS = {
121
124
  VELO: "",
122
125
  COMPOUNDV3_WETH: "0x6f7D514bbD4aFf3BcD1140B7344b32f063dEe486",
123
126
  FLUID_WETH: "0x45df0656f8adf017590009d2f1898eeca4f0a205",
124
- TOROS: ""
127
+ TOROS: "0xA6711f8a184E352c5A0714a48912cD33ca4a16A0" //DYTMT
125
128
  },
126
129
  [Network.BASE]: {
127
130
  USDC: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
@@ -143,7 +146,10 @@ export const CONTRACT_ADDRESS = {
143
146
  VELO: "0x940181a94A35A4569E4529A3CDfB74e38FD98631",
144
147
  COMPOUNDV3_WETH: "",
145
148
  FLUID_WETH: "",
146
- TOROS: ""
149
+ TOROS: "",
150
+ WSTETH: "",
151
+ BALANCER_WSTETH_WETH_POOL: "",
152
+ BALANCER_WSTETH_WETH_GAUGE: ""
147
153
  },
148
154
  [Network.ETHEREUM]: {
149
155
  USDC: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
@@ -165,17 +171,56 @@ export const CONTRACT_ADDRESS = {
165
171
  COMPOUNDV3_WETH: "",
166
172
  FLUID_WETH: "",
167
173
  TOROS: "",
168
- UNIT: ""
174
+ UNIT: "",
175
+ WSTETH: "",
176
+ BALANCER_WSTETH_WETH_POOL: "",
177
+ BALANCER_WSTETH_WETH_GAUGE: ""
169
178
  },
170
179
  [Network.PLASMA]: {
171
180
  USDC: "",
172
181
  WETH: "0x9895d81bb462a195b4922ed7de0e3acd007c32cb",
182
+ WBTC: "",
173
183
  USDT: "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb",
174
- USDE: "0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34"
184
+ USDE: "0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34",
185
+ VELO: "",
186
+ uniswapV3: {
187
+ nonfungiblePositionManager: ""
188
+ },
189
+ COMPOUNDV3_WETH: "",
190
+ FLUID_WETH: "",
191
+ UNIT: "",
192
+ TOROS: "",
193
+ SUSD: "",
194
+ VELODROME_CL_USDC_WETH_GAUGE: "",
195
+ ARRAKIS_USDC_WETH_GAUGE: "",
196
+ ARRAKIS_USDC_WETH_LP: "",
197
+ WMATIC: "",
198
+ WSTETH: "",
199
+ BALANCER_WSTETH_WETH_POOL: "",
200
+ BALANCER_WSTETH_WETH_GAUGE: ""
175
201
  },
176
202
  [Network.HYPERLIQUID]: {
177
203
  USDC: "0xb88339cb7199b77e23db6e890353e22632ba630f",
178
- WETH: ""
204
+ USDT: "",
205
+ USDE: "",
206
+ WETH: "",
207
+ WBTC: "",
208
+ VELO: "",
209
+ uniswapV3: {
210
+ nonfungiblePositionManager: ""
211
+ },
212
+ COMPOUNDV3_WETH: "",
213
+ FLUID_WETH: "",
214
+ UNIT: "",
215
+ TOROS: "",
216
+ SUSD: "",
217
+ VELODROME_CL_USDC_WETH_GAUGE: "",
218
+ ARRAKIS_USDC_WETH_GAUGE: "",
219
+ ARRAKIS_USDC_WETH_LP: "",
220
+ WMATIC: "",
221
+ WSTETH: "",
222
+ BALANCER_WSTETH_WETH_POOL: "",
223
+ BALANCER_WSTETH_WETH_GAUGE: ""
179
224
  }
180
225
  };
181
226
 
@@ -195,7 +240,7 @@ export const WETH_BALANCEOF_SLOT = {
195
240
  [Network.OPTIMISM]: 3,
196
241
  [Network.ARBITRUM]: 51,
197
242
  [Network.POLYGON]: 0,
198
- [Network.BASE]: 0,
243
+ [Network.BASE]: 3,
199
244
  [Network.ETHEREUM]: 3,
200
245
  [Network.PLASMA]: 1,
201
246
  [Network.HYPERLIQUID]: 1
@@ -0,0 +1,79 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+
3
+ /**
4
+ * CowSwap tests require a live chain connection (onFork: false).
5
+ * CowSwap orders are settled off-chain by solvers, so they cannot
6
+ * be tested on a Hardhat fork. Run with a funded wallet and
7
+ * PRIVATE_KEY + POLYGON_URL set in .env.
8
+ *
9
+ * Flow: approve CoW vault relayer → trade
10
+ * Note: estimateGas and onlyGetTxData are not supported for CowSwap
11
+ * because it requires two sequential transactions (submit + preSign).
12
+ */
13
+
14
+ import { Dhedge, Pool } from "..";
15
+
16
+ import { Dapp, Network } from "../types";
17
+ import { CONTRACT_ADDRESS, MAX_AMOUNT, TEST_POOL } from "./constants";
18
+ import { getTxOptions } from "./txOptions";
19
+ import { TestingRunParams, testingHelper } from "./utils/testingHelper";
20
+ import { allowanceDelta, balanceDelta } from "./utils/token";
21
+
22
+ // CoW Protocol Vault Relayer — must be approved before CowSwap trades
23
+ const COWSWAP_VAULT_RELAYER = "0xC92E8bdf79f0507f65a392b0ab4667716BFE0110";
24
+
25
+ const testCowswap = ({ wallet, network }: TestingRunParams) => {
26
+ const USDC = CONTRACT_ADDRESS[network].USDC;
27
+ const WETH = CONTRACT_ADDRESS[network].WETH;
28
+
29
+ let dhedge: Dhedge;
30
+ let pool: Pool;
31
+ jest.setTimeout(100000);
32
+
33
+ describe(`[${network}] cowswap tests`, () => {
34
+ beforeAll(async () => {
35
+ dhedge = new Dhedge(wallet, network);
36
+ pool = await dhedge.loadPool(TEST_POOL[network]);
37
+ });
38
+
39
+ it("approves unlimited USDC on Cowswap vault relayer", async () => {
40
+ await pool.approveSpender(
41
+ COWSWAP_VAULT_RELAYER,
42
+ USDC,
43
+ MAX_AMOUNT,
44
+ await getTxOptions(network)
45
+ );
46
+ const usdcAllowanceDelta = await allowanceDelta(
47
+ pool.address,
48
+ USDC,
49
+ COWSWAP_VAULT_RELAYER,
50
+ pool.signer
51
+ );
52
+ expect(usdcAllowanceDelta.gt(0)).toBe(true);
53
+ });
54
+
55
+ it("trades 2 USDC into WETH on Cowswap", async () => {
56
+ await pool.trade(
57
+ Dapp.COWSWAP,
58
+ USDC,
59
+ WETH,
60
+ "2000000",
61
+ 0.5,
62
+ await getTxOptions(network)
63
+ );
64
+ const wethBalanceDelta = await balanceDelta(
65
+ pool.address,
66
+ WETH,
67
+ pool.signer
68
+ );
69
+ expect(wethBalanceDelta.gt(0)).toBe(true);
70
+ });
71
+ });
72
+ };
73
+
74
+ // CowSwap requires live chain — cannot run on fork
75
+ testingHelper({
76
+ network: Network.POLYGON,
77
+ testingRun: testCowswap,
78
+ onFork: false
79
+ });
@@ -1,32 +1,65 @@
1
1
  import { Dhedge } from "..";
2
2
  import { Network } from "../types";
3
+ import { CONTRACT_ADDRESS, TEST_POOL } from "./constants";
4
+ import {
5
+ testingHelper,
6
+ TestingRunParams,
7
+ beforeAfterReset
8
+ } from "./utils/testingHelper";
3
9
 
4
- import { testingHelper, TestingRunParams } from "./utils/testingHelper";
5
-
6
- const testDhedge = ({ wallet, network }: TestingRunParams) => {
10
+ const testDhedge = ({ wallet, network, provider }: TestingRunParams) => {
7
11
  let dhedge: Dhedge;
8
12
 
9
13
  jest.setTimeout(200000);
10
14
 
11
15
  describe(`dHEDGE on ${network}`, () => {
12
16
  beforeAll(async () => {
17
+ // Fund the test wallet with gas
18
+ await provider.send("hardhat_setBalance", [
19
+ wallet.address,
20
+ "0x10000000000000000"
21
+ ]);
13
22
  dhedge = new Dhedge(wallet, network);
14
23
  });
15
24
 
16
- it("create a pool", async () => {
17
- const pool = await dhedge.createPool("Test", "Test", "TEST", [
18
- ["0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34", true],
19
- ["0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb", false],
20
- ["0x925a2A7214Ed92428B5b1B090F80b25700095e12", false]
21
- ]);
22
- console.log(pool.address);
25
+ beforeAfterReset({ beforeAll, afterAll, provider });
26
+
27
+ it("creates a pool with USDC and WETH", async () => {
28
+ const pool = await dhedge.createPool(
29
+ "Test Manager",
30
+ "Test Fund",
31
+ "TEST",
32
+ [
33
+ [CONTRACT_ADDRESS[network].USDC, true],
34
+ [CONTRACT_ADDRESS[network].WETH, false]
35
+ ]
36
+ );
37
+ expect(pool.address).toBeDefined();
23
38
  expect(pool.poolLogic.address).toBe(pool.address);
24
39
  });
40
+
41
+ it("loads an existing pool", async () => {
42
+ const pool = await dhedge.loadPool(TEST_POOL[network]);
43
+ expect(pool.address).toBe(TEST_POOL[network]);
44
+ expect(pool.poolLogic.address).toBe(TEST_POOL[network]);
45
+ });
46
+
47
+ it("validates a real pool address", async () => {
48
+ const isValid = await dhedge.validatePool(TEST_POOL[network]);
49
+ expect(isValid).toBe(true);
50
+ });
51
+
52
+ it("rejects an invalid pool address", async () => {
53
+ const isValid = await dhedge.validatePool(
54
+ "0x0000000000000000000000000000000000000001"
55
+ );
56
+ expect(isValid).toBe(false);
57
+ });
25
58
  });
26
59
  };
27
60
 
28
61
  testingHelper({
29
- network: Network.PLASMA,
30
- onFork: false,
62
+ network: Network.ARBITRUM,
63
+ onFork: true,
31
64
  testingRun: testDhedge
32
65
  });
@@ -1,7 +1,6 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
1
  import BigNumber from "bignumber.js";
3
2
  import { Dhedge, Pool } from "../entities";
4
- import { AssetEnabled, Network } from "../types";
3
+ import { Network } from "../types";
5
4
  import {
6
5
  TestingRunParams,
7
6
  runWithImpersonateAccount,
@@ -12,7 +11,6 @@ import { Contract, ethers } from "ethers";
12
11
  import { CONTRACT_ADDRESS, MAX_AMOUNT, TEST_POOL } from "./constants";
13
12
  import { flatMoneyContractAddresses } from "../config";
14
13
  import DelayedOrderAbi from "../abi/flatmoney/DelayedOrder.json";
15
- import { allowanceDelta } from "./utils/token";
16
14
  import { getKeeperFee } from "../services/flatmoney/keeperFee";
17
15
 
18
16
  const COLLATERAL_SLOT = 0; // same for RETH(base) and WBTC(optimism)
@@ -32,19 +30,15 @@ const testFlatMoney = ({
32
30
  let delayOrderContract: Contract;
33
31
  let COLLATERAL: string;
34
32
  jest.setTimeout(200000);
33
+ // FlatMoney's OracleModule reverts with ETHPriceStale() when the forked block's
34
+ // Chainlink ETH/USD price is older than maxAge. Re-run the fork before this suite
35
+ // so the oracle timestamp is fresh (there is no helper to override maxAge here).
35
36
  describe(`flatmoney on ${network}`, () => {
36
37
  beforeAll(async () => {
37
38
  await provider.send("evm_mine", []);
38
39
  dhedge = new Dhedge(wallet, network);
39
40
  pool = await dhedge.loadPool(TEST_POOL[network]);
40
41
 
41
- await runWithImpersonateAccount(
42
- { provider, account: await pool.managerLogic.manager() },
43
- async ({ signer }) => {
44
- await pool.managerLogic.connect(signer).setTrader(wallet.address);
45
- }
46
- );
47
-
48
42
  const flatMoneyContracts = flatMoneyContractAddresses[pool.network];
49
43
  if (!flatMoneyContracts) {
50
44
  throw new Error("testFlatMoney: network not supported");
@@ -56,7 +50,20 @@ const testFlatMoney = ({
56
50
  pool.signer
57
51
  );
58
52
 
59
- // top up gas
53
+ await runWithImpersonateAccount(
54
+ { provider, account: await pool.managerLogic.manager() },
55
+ async ({ signer }) => {
56
+ await pool.managerLogic.connect(signer).setTrader(wallet.address);
57
+ await pool.managerLogic.connect(signer).changeAssets(
58
+ [
59
+ [COLLATERAL, false],
60
+ [CONTRACT_ADDRESS[network].UNIT, false]
61
+ ],
62
+ []
63
+ );
64
+ }
65
+ );
66
+
60
67
  await provider.send("hardhat_setBalance", [
61
68
  wallet.address,
62
69
  "0x10000000000000000"
@@ -77,45 +84,24 @@ const testFlatMoney = ({
77
84
  slot: UNIT_SLOT,
78
85
  userAddress: pool.address
79
86
  });
80
-
81
- const currentAssets: any[] = await pool.managerLogic.getSupportedAssets();
82
- const exisitingAssets = currentAssets.map(item => {
83
- return {
84
- asset: item[0],
85
- isDeposit: item[1]
86
- };
87
- });
88
-
89
- const newAssets: AssetEnabled[] = [
90
- ...exisitingAssets,
91
- { asset: CONTRACT_ADDRESS[network].USDC, isDeposit: true },
92
- { asset: CONTRACT_ADDRESS[network].WETH, isDeposit: true },
93
- {
94
- asset: CONTRACT_ADDRESS[network].UNIT,
95
- isDeposit: false
96
- },
97
- {
98
- asset: COLLATERAL,
99
- isDeposit: false
100
- }
101
- ];
102
- await pool.changeAssets(newAssets);
103
87
  });
104
88
 
105
89
  it("mint UNIT", async () => {
106
- //approve
107
90
  await pool.approveSpender(
108
91
  delayOrderContract.address,
109
92
  COLLATERAL,
110
93
  MAX_AMOUNT
111
94
  );
112
- const collateralAllowanceDelta = await allowanceDelta(
113
- pool.address,
95
+ const iERC20 = new ethers.Contract(
114
96
  COLLATERAL,
115
- delayOrderContract.address,
97
+ ["function allowance(address,address) view returns (uint256)"],
116
98
  pool.signer
117
99
  );
118
- await expect(collateralAllowanceDelta.gt(0));
100
+ const collateralAllowance = await iERC20.allowance(
101
+ pool.address,
102
+ delayOrderContract.address
103
+ );
104
+ expect(collateralAllowance.gt(0)).toBe(true);
119
105
 
120
106
  const depositAmountStr = new BigNumber(1).times(1e18).toString();
121
107
  const estimateData = await pool.mintUnitViaFlatMoney(
@@ -1,19 +1,20 @@
1
1
  /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
2
  /* eslint-disable @typescript-eslint/no-explicit-any */
3
3
  import BigNumber from "bignumber.js";
4
+ import { ethers } from "ethers";
4
5
  import { Dhedge, Pool } from "..";
5
- import { AssetEnabled, Network } from "../types";
6
+ import { Network } from "../types";
6
7
  import { CONTRACT_ADDRESS, MAX_AMOUNT, TEST_POOL } from "./constants";
7
8
  import {
8
9
  TestingRunParams,
9
10
  beforeAfterReset,
10
11
  setWETHAmount,
12
+ runWithImpersonateAccount,
11
13
  testingHelper
12
14
  } from "./utils/testingHelper";
13
- import { allowanceDelta, balanceDelta } from "./utils/token";
14
- import { getWalletData } from "./wallet";
15
+ import { balanceDelta } from "./utils/token";
15
16
 
16
- const testFluid = ({ network, provider }: TestingRunParams) => {
17
+ const testFluid = ({ wallet, network, provider }: TestingRunParams) => {
17
18
  const WETH = CONTRACT_ADDRESS[network].WETH;
18
19
  const FLUID_WETH = CONTRACT_ADDRESS[network].FLUID_WETH;
19
20
 
@@ -21,16 +22,15 @@ const testFluid = ({ network, provider }: TestingRunParams) => {
21
22
  let pool: Pool;
22
23
  jest.setTimeout(100000);
23
24
 
24
- describe(`[${network}] compound V3 tests`, () => {
25
+ describe(`[${network}] fluid tests`, () => {
25
26
  beforeAll(async () => {
26
- const { wallet } = getWalletData(network);
27
- // top up ETH (gas)
28
27
  await provider.send("hardhat_setBalance", [
29
28
  wallet.address,
30
- "0x100000000000000"
29
+ "0x10000000000000000"
31
30
  ]);
32
31
  dhedge = new Dhedge(wallet, network);
33
32
  pool = await dhedge.loadPool(TEST_POOL[network]);
33
+
34
34
  await setWETHAmount({
35
35
  amount: new BigNumber(1e18).toFixed(0),
36
36
  userAddress: pool.address,
@@ -38,26 +38,31 @@ const testFluid = ({ network, provider }: TestingRunParams) => {
38
38
  provider
39
39
  });
40
40
 
41
- const newAssets: AssetEnabled[] = [
42
- { asset: WETH, isDeposit: true },
43
- {
44
- asset: FLUID_WETH,
45
- isDeposit: false
41
+ // Impersonate pool manager to set trader and configure assets
42
+ await runWithImpersonateAccount(
43
+ { provider, account: await pool.managerLogic.manager() },
44
+ async ({ signer }) => {
45
+ await pool.managerLogic.connect(signer).setTrader(wallet.address);
46
+ const newAssets = [
47
+ [WETH, true],
48
+ [FLUID_WETH, false]
49
+ ];
50
+ await pool.managerLogic.connect(signer).changeAssets(newAssets, []);
46
51
  }
47
- ];
48
- await pool.managerLogic.changeAssets(newAssets, []);
52
+ );
49
53
  });
50
54
  beforeAfterReset({ beforeAll, afterAll, provider });
51
55
 
52
56
  it("approves unlimited WETH for fWETH market", async () => {
53
- await pool.approveSpender(FLUID_WETH, WETH, MAX_AMOUNT);
54
- const wethAllowanceDelta = await allowanceDelta(
55
- pool.address,
57
+ const tx = await pool.approveSpender(FLUID_WETH, WETH, MAX_AMOUNT);
58
+ await tx.wait(1);
59
+ const iERC20 = new ethers.Contract(
56
60
  WETH,
57
- FLUID_WETH,
61
+ ["function allowance(address,address) view returns (uint256)"],
58
62
  pool.signer
59
63
  );
60
- await expect(wethAllowanceDelta.gt(0));
64
+ const wethAllowance = await iERC20.allowance(pool.address, FLUID_WETH);
65
+ expect(wethAllowance.gt(0)).toBe(true);
61
66
  });
62
67
 
63
68
  it("lends WETH to Fluid WETH market", async () => {
@@ -69,17 +74,21 @@ const testFluid = ({ network, provider }: TestingRunParams) => {
69
74
  FLUID_WETH,
70
75
  pool.signer
71
76
  );
72
- expect(fWETHTokenDelta.gt(0));
77
+ expect(fWETHTokenDelta.gt(0)).toBe(true);
73
78
  });
74
79
 
75
- it("withdraw WETH from Fluid WETH market", async () => {
80
+ it("withdraws WETH from Fluid WETH market", async () => {
76
81
  const fWETHBalance = await pool.utils.getBalance(
77
82
  FLUID_WETH,
78
83
  pool.address
79
84
  );
80
85
  await pool.withdrawCompoundV3(FLUID_WETH, WETH, fWETHBalance);
81
- const wethBalance = await balanceDelta(pool.address, WETH, pool.signer);
82
- expect(wethBalance.gt(0));
86
+ const wethBalanceDelta = await balanceDelta(
87
+ pool.address,
88
+ WETH,
89
+ pool.signer
90
+ );
91
+ expect(wethBalanceDelta.gt(0)).toBe(true);
83
92
  });
84
93
  });
85
94
  };
@@ -0,0 +1,131 @@
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+
3
+ /**
4
+ * Hyperliquid on-chain tests for deposit/withdraw/perp-spot/open/close flows.
5
+ * These require a live chain connection (onFork: false) because Hyperliquid
6
+ * precompile and CoreWriter state cannot be faithfully forked.
7
+ *
8
+ * Prerequisites:
9
+ * - PRIVATE_KEY in .env (must be the pool manager or trader)
10
+ * - HYPERLIQUID_URL in .env
11
+ * - The test pool must hold USDC and an existing ETH perp position
12
+ * - Spot wallet must hold the amount expected by the withdraw test
13
+ */
14
+
15
+ import { ethers } from "ethers";
16
+ import { Dhedge, Pool } from "..";
17
+
18
+ import { Dapp, Network } from "../types";
19
+ import { CONTRACT_ADDRESS, MAX_AMOUNT, TEST_POOL } from "./constants";
20
+ import { routerAddress } from "../config";
21
+ import { getTxOptions } from "./txOptions";
22
+
23
+ import { TestingRunParams, testingHelper } from "./utils/testingHelper";
24
+
25
+ const testHyperliquid = ({ wallet, network }: TestingRunParams) => {
26
+ const USDC = CONTRACT_ADDRESS[network].USDC;
27
+
28
+ let dhedge: Dhedge;
29
+ let pool: Pool;
30
+ jest.setTimeout(200000);
31
+
32
+ describe(`pool on ${network}`, () => {
33
+ beforeAll(async () => {
34
+ if (!process.env.PRIVATE_KEY || !process.env.HYPERLIQUID_URL) {
35
+ console.warn(
36
+ "Skipping hyperliquid on-chain tests: PRIVATE_KEY and HYPERLIQUID_URL env vars required"
37
+ );
38
+ return;
39
+ }
40
+ dhedge = new Dhedge(wallet, network);
41
+ pool = await dhedge.loadPool(TEST_POOL[network]);
42
+ });
43
+
44
+ it("approves unlimited USDC on Hyperliquid Core Wallet", async () => {
45
+ if (!process.env.PRIVATE_KEY || !process.env.HYPERLIQUID_URL) return;
46
+ const tx = await pool.approve(
47
+ Dapp.HYPERLIQUID,
48
+ USDC,
49
+ MAX_AMOUNT,
50
+ await getTxOptions(network)
51
+ );
52
+ await tx.wait(1);
53
+ const iERC20 = new ethers.Contract(
54
+ USDC,
55
+ ["function allowance(address,address) view returns (uint256)"],
56
+ pool.signer
57
+ );
58
+ const allowance = await iERC20.allowance(
59
+ pool.address,
60
+ routerAddress[network][Dapp.HYPERLIQUID]!
61
+ );
62
+ expect(allowance.gt(0)).toBe(true);
63
+ });
64
+
65
+ it("deposits 30 USDC into Hyperliquid Core Wallet", async () => {
66
+ if (!process.env.PRIVATE_KEY || !process.env.HYPERLIQUID_URL) return;
67
+ const usdcBefore = await pool.utils.getBalance(USDC, pool.address);
68
+ const tx = await pool.depositHyperliquid(
69
+ "30000000", // 30 USDC (6 decimals)
70
+ 4294967295,
71
+ await getTxOptions(network)
72
+ );
73
+ await tx.wait(1);
74
+ const usdcAfter = await pool.utils.getBalance(USDC, pool.address);
75
+ expect(usdcBefore.sub(usdcAfter).eq("30000000")).toBe(true);
76
+ });
77
+
78
+ it("moves 5 USDC from Perp (dex 0) to Spot wallet", async () => {
79
+ if (!process.env.PRIVATE_KEY || !process.env.HYPERLIQUID_URL) return;
80
+ const tx = await pool.perpToSpotHyperliquid(
81
+ 0,
82
+ "5000000", // 5 USDC (6 decimals)
83
+ await getTxOptions(network)
84
+ );
85
+ await tx.wait(1);
86
+ expect(tx).toBeDefined();
87
+ });
88
+
89
+ it("withdraws USDC from Hyperliquid Spot Wallet", async () => {
90
+ if (!process.env.PRIVATE_KEY || !process.env.HYPERLIQUID_URL) return;
91
+ const tx = await pool.withdrawHyperliquid(
92
+ "784577548", // 784.577548 USDC (6 decimals)
93
+ await getTxOptions(network)
94
+ );
95
+ await tx.wait(1);
96
+ expect(tx).toBeDefined();
97
+ });
98
+
99
+ it("opens a XAUT0 spot buy order", async () => {
100
+ if (!process.env.PRIVATE_KEY || !process.env.HYPERLIQUID_URL) return;
101
+ const tx = await pool.openMarketOrderHyperliquid(
102
+ 10182, // XAUT0/USDC spot asset id (10000 + spot index 182)
103
+ true, // long
104
+ 25, // $25 notional
105
+ 1, // 1% slippage
106
+ await getTxOptions(network)
107
+ );
108
+ await tx.wait(1);
109
+ expect(tx).toBeDefined();
110
+ });
111
+
112
+ it("closes 50% of ETH perp position", async () => {
113
+ if (!process.env.PRIVATE_KEY || !process.env.HYPERLIQUID_URL) return;
114
+ const tx = await pool.closePositionHyperliquid(
115
+ 1, // ETH Perp asset id
116
+ 50, // 50% to close
117
+ 1, // 1% slippage
118
+ await getTxOptions(network)
119
+ );
120
+ await tx.wait(1);
121
+ expect(tx).toBeDefined();
122
+ });
123
+ });
124
+ };
125
+
126
+ // Requires live chain — cannot run on fork
127
+ testingHelper({
128
+ network: Network.HYPERLIQUID,
129
+ testingRun: testHyperliquid,
130
+ onFork: false
131
+ });