@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
@@ -1,79 +1,125 @@
1
1
  /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
2
 
3
- // import { ethers } from "ethers";
3
+ import BigNumber from "bignumber.js";
4
+ import { ethers } from "ethers";
4
5
  import { Dhedge, Pool } from "..";
5
- // import { limitOrderAddress } from "../config";
6
+ import { limitOrderAddress, routerAddress } from "../config";
7
+
8
+ import { Dapp, Network } from "../types";
9
+ import { CONTRACT_ADDRESS, MAX_AMOUNT, TEST_POOL } from "./constants";
10
+ import {
11
+ fixOracleAggregatorStaleness,
12
+ runWithImpersonateAccount,
13
+ setChainlinkTimeout,
14
+ setUSDCAmount,
15
+ testingHelper,
16
+ TestingRunParams
17
+ } from "./utils/testingHelper";
18
+
19
+ const TOROS_BTCBEAR2X = "0x3e63f81b3aa4e821392fccdabdd7d0c960c0235e";
20
+ const BTC_PRICING_ASSET = "0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f"; // WBTC
21
+
22
+ const testTorosLimitOrder = ({
23
+ wallet,
24
+ network,
25
+ provider
26
+ }: TestingRunParams) => {
27
+ const ORDER_AMOUNT = ethers.utils.parseEther("0.001");
28
+ const STOP_LOSS = ethers.utils.parseEther("50000");
29
+ const TAKE_PROFIT = ethers.utils.parseEther("200000");
6
30
 
7
- import { Network } from "../types";
8
- // import { MAX_AMOUNT } from "./constants";
9
- import { testingHelper, TestingRunParams } from "./utils/testingHelper";
10
-
11
- // import { allowanceDelta } from "./utils/token";
12
-
13
- // cspell:ignore goldbull
14
- const TOROS_GOLDBULL2X = "0xc8e7e840ca82804c14061a27aaca1b97a5a592ab";
15
- // const GOLD_PRICING_ASSET = "0x7624cccCc59361D583F28BEC40D37e7771d2ef5D";
16
-
17
- const testTorosLimitOrder = ({ wallet, network }: TestingRunParams) => {
18
- // const ORDER_AMOUNT = ethers.utils.parseEther("0.001");
19
- // const STOP_LOSS = ethers.utils.parseEther("3000");
20
- // const TAKE_PROFIT = ethers.utils.parseEther("6000");
21
-
22
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
23
31
  let dhedge: Dhedge;
24
32
  let pool: Pool;
25
33
  jest.setTimeout(100000);
26
34
 
27
35
  describe(`toros limit order on ${network}`, () => {
28
36
  beforeAll(async () => {
37
+ // Top up gas
38
+ await provider.send("hardhat_setBalance", [
39
+ wallet.address,
40
+ "0x10000000000000000"
41
+ ]);
42
+ await provider.send("evm_mine", []);
43
+
29
44
  dhedge = new Dhedge(wallet, network);
30
- pool = await dhedge.loadPool(
31
- "0xdd3d575fae102a12aaa086d97c7a8814eff42ebc" // MCP test pool (with GOLDBULL2X)
45
+ pool = await dhedge.loadPool(TEST_POOL[network]);
46
+
47
+ await setChainlinkTimeout({ pool, provider }, 86400 * 365);
48
+ await fixOracleAggregatorStaleness({ pool, provider });
49
+
50
+ // Impersonate pool manager to set trader and enable BTCBEAR2X asset
51
+ await runWithImpersonateAccount(
52
+ { provider, account: await pool.managerLogic.manager() },
53
+ async ({ signer }) => {
54
+ await pool.managerLogic.connect(signer).setTrader(wallet.address);
55
+ const USDC = CONTRACT_ADDRESS[network].USDC;
56
+ const easySwapperV2 = routerAddress[network][Dapp.TOROS]!;
57
+ await pool.managerLogic.connect(signer).changeAssets(
58
+ [
59
+ [TOROS_BTCBEAR2X, false],
60
+ [USDC, true],
61
+ [easySwapperV2, false]
62
+ ],
63
+ []
64
+ );
65
+ }
32
66
  );
67
+
68
+ // Fund pool with USDC and trade into BTCBEAR2X
69
+ const USDC = CONTRACT_ADDRESS[network].USDC;
70
+ const usdcAmount = new BigNumber(100).times(1e6).toFixed(0);
71
+ await setUSDCAmount({
72
+ amount: usdcAmount,
73
+ userAddress: pool.address,
74
+ network,
75
+ provider
76
+ });
77
+ await pool.approve(Dapp.TOROS, USDC, MAX_AMOUNT);
78
+ await pool.trade(Dapp.TOROS, USDC, TOROS_BTCBEAR2X, usdcAmount, 1.5);
33
79
  });
34
80
 
35
81
  it("approves Toros vault token for LimitOrderManager", async () => {
36
- const tx = await pool.approveTorosLimitOrder(
37
- TOROS_GOLDBULL2X,
38
- MAX_AMOUNT
39
- );
82
+ const tx = await pool.approveTorosLimitOrder(TOROS_BTCBEAR2X, MAX_AMOUNT);
40
83
  await tx.wait(1);
41
- const delta = await allowanceDelta(
42
- pool.address,
43
- TOROS_GOLDBULL2X,
44
- limitOrderAddress[network],
84
+ const iERC20 = new ethers.Contract(
85
+ TOROS_BTCBEAR2X,
86
+ ["function allowance(address,address) view returns (uint256)"],
45
87
  pool.signer
46
88
  );
47
- expect(delta.gt(0)).toBe(true);
89
+ const allowance = await iERC20.allowance(
90
+ pool.address,
91
+ limitOrderAddress[network]
92
+ );
93
+ expect(allowance.gt(0)).toBe(true);
48
94
  });
49
95
 
50
96
  it("creates a limit order", async () => {
51
97
  const tx = await pool.createTorosLimitOrder(
52
- TOROS_GOLDBULL2X,
98
+ TOROS_BTCBEAR2X,
53
99
  ORDER_AMOUNT,
54
100
  STOP_LOSS,
55
101
  TAKE_PROFIT,
56
- GOLD_PRICING_ASSET
102
+ BTC_PRICING_ASSET
57
103
  );
58
104
  await tx.wait(1);
59
105
  const order = await pool.getTorosLimitOrder(
60
106
  pool.address,
61
- TOROS_GOLDBULL2X
107
+ TOROS_BTCBEAR2X
62
108
  );
63
109
  expect(order).not.toBeNull();
64
110
  expect(order!.amount.eq(ORDER_AMOUNT)).toBe(true);
65
111
  expect(order!.stopLossPriceD18.eq(STOP_LOSS)).toBe(true);
66
112
  expect(order!.takeProfitPriceD18.eq(TAKE_PROFIT)).toBe(true);
67
- expect(order!.pool.toLowerCase()).toBe(TOROS_GOLDBULL2X.toLowerCase());
113
+ expect(order!.pool.toLowerCase()).toBe(TOROS_BTCBEAR2X.toLowerCase());
68
114
  expect(order!.pricingAsset.toLowerCase()).toBe(
69
- GOLD_PRICING_ASSET.toLowerCase()
115
+ BTC_PRICING_ASSET.toLowerCase()
70
116
  );
71
117
  });
72
118
 
73
119
  it("modifies a toros limit order", async () => {
74
120
  const order = await pool.getTorosLimitOrder(
75
121
  pool.address,
76
- TOROS_GOLDBULL2X
122
+ TOROS_BTCBEAR2X
77
123
  );
78
124
  if (!order) throw new Error("No existing order found");
79
125
 
@@ -87,7 +133,7 @@ const testTorosLimitOrder = ({ wallet, network }: TestingRunParams) => {
87
133
  .toString();
88
134
 
89
135
  const tx = await pool.modifyTorosLimitOrder(
90
- TOROS_GOLDBULL2X,
136
+ TOROS_BTCBEAR2X,
91
137
  order.amount,
92
138
  newStopLoss,
93
139
  newTakeProfit,
@@ -97,7 +143,7 @@ const testTorosLimitOrder = ({ wallet, network }: TestingRunParams) => {
97
143
 
98
144
  const modifiedOrder = await pool.getTorosLimitOrder(
99
145
  pool.address,
100
- TOROS_GOLDBULL2X
146
+ TOROS_BTCBEAR2X
101
147
  );
102
148
  expect(modifiedOrder).not.toBeNull();
103
149
  expect(modifiedOrder!.stopLossPriceD18.toString()).toBe(newStopLoss);
@@ -107,16 +153,16 @@ const testTorosLimitOrder = ({ wallet, network }: TestingRunParams) => {
107
153
  it("deletes a toros limit order", async () => {
108
154
  const order = await pool.getTorosLimitOrder(
109
155
  pool.address,
110
- TOROS_GOLDBULL2X
156
+ TOROS_BTCBEAR2X
111
157
  );
112
158
  if (!order) throw new Error("No existing order found");
113
159
 
114
- const tx = await pool.deleteTorosLimitOrder(TOROS_GOLDBULL2X);
160
+ const tx = await pool.deleteTorosLimitOrder(TOROS_BTCBEAR2X);
115
161
  await tx.wait(1);
116
162
 
117
163
  const deletedOrder = await pool.getTorosLimitOrder(
118
164
  pool.address,
119
- TOROS_GOLDBULL2X
165
+ TOROS_BTCBEAR2X
120
166
  );
121
167
  expect(deletedOrder).toBeNull();
122
168
  });
@@ -125,6 +171,5 @@ const testTorosLimitOrder = ({ wallet, network }: TestingRunParams) => {
125
171
 
126
172
  testingHelper({
127
173
  network: Network.ARBITRUM,
128
- testingRun: testTorosLimitOrder,
129
- onFork: false
174
+ testingRun: testTorosLimitOrder
130
175
  });
@@ -1,11 +1,12 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import { Dhedge, ethers, Pool } from "..";
3
- import { routerAddress } from "../config";
4
- import { AssetEnabled, Dapp, Network } from "../types";
3
+ import { nonfungiblePositionManagerAddress } from "../config";
4
+ import { Dapp, Network } from "../types";
5
5
  import { CONTRACT_ADDRESS, TEST_POOL } from "./constants";
6
- import { allowanceDelta, balanceDelta } from "./utils/token";
7
6
  import {
8
7
  setUSDCAmount,
8
+ setWETHAmount,
9
+ runWithImpersonateAccount,
9
10
  testingHelper,
10
11
  TestingRunParams
11
12
  } from "./utils/testingHelper";
@@ -30,22 +31,37 @@ const testUniswapV3 = ({ wallet, network, provider }: TestingRunParams) => {
30
31
  "0x10000000000000000"
31
32
  ]);
32
33
  await provider.send("evm_mine", []);
34
+
35
+ // Fund pool with USDC and WETH
33
36
  await setUSDCAmount({
34
- amount: new BigNumber(1000000).times(1e6).toFixed(0),
37
+ amount: new BigNumber(10000).times(1e6).toFixed(0),
38
+ userAddress: pool.address,
39
+ network,
40
+ provider
41
+ });
42
+ await setWETHAmount({
43
+ amount: new BigNumber(5).times(1e18).toFixed(0),
35
44
  userAddress: pool.address,
36
45
  network,
37
46
  provider
38
47
  });
39
48
 
40
- const newAssets: AssetEnabled[] = [
41
- { asset: CONTRACT_ADDRESS[network].USDC, isDeposit: true },
42
- { asset: CONTRACT_ADDRESS[network].WETH, isDeposit: true },
43
- {
44
- asset: CONTRACT_ADDRESS[network].uniswapV3.nonfungiblePositionManager,
45
- isDeposit: false
49
+ // Impersonate the pool manager to set trader and configure assets
50
+ await runWithImpersonateAccount(
51
+ { provider, account: await pool.managerLogic.manager() },
52
+ async ({ signer }) => {
53
+ await pool.managerLogic.connect(signer).setTrader(wallet.address);
54
+ const newAssets = [
55
+ [CONTRACT_ADDRESS[network].USDC, true],
56
+ [CONTRACT_ADDRESS[network].WETH, true],
57
+ [
58
+ CONTRACT_ADDRESS[network].uniswapV3.nonfungiblePositionManager,
59
+ false
60
+ ]
61
+ ];
62
+ await pool.managerLogic.connect(signer).changeAssets(newAssets, []);
46
63
  }
47
- ];
48
- await pool.changeAssets(newAssets);
64
+ );
49
65
 
50
66
  nonfungiblePositionManager = new ethers.Contract(
51
67
  CONTRACT_ADDRESS[network].uniswapV3.nonfungiblePositionManager,
@@ -54,60 +70,42 @@ const testUniswapV3 = ({ wallet, network, provider }: TestingRunParams) => {
54
70
  );
55
71
  });
56
72
 
57
- it("approves unlimited USDC on for trading on UniswapV3", async () => {
58
- await pool.approve(
59
- Dapp.UNISWAPV3,
60
- CONTRACT_ADDRESS[network].USDC,
61
- ethers.constants.MaxUint256
62
- );
63
- const UsdcAllowanceDelta = await allowanceDelta(
64
- pool.address,
65
- CONTRACT_ADDRESS[network].USDC,
66
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
67
- routerAddress[network].uniswapV3!,
68
- pool.signer
69
- );
70
- expect(UsdcAllowanceDelta.gte(0));
71
- });
73
+ // Note: tradeUniswapV3 (UniswapV3RouterGuard) is deprecated on all chains.
74
+ // Swap tests removed. Use Dapp.ONEINCH or Dapp.KYBERSWAP for trading instead.
72
75
 
73
- it("should swap 5000 USDC into WETH on UniswapV3", async () => {
74
- await pool.tradeUniswapV3(
75
- CONTRACT_ADDRESS[network].USDC,
76
- CONTRACT_ADDRESS[network].WETH,
77
- new BigNumber(5000).times(1e6).toFixed(0),
78
- 500,
79
- 0.5
80
- );
81
-
82
- const wethAllowanceDelta = await balanceDelta(
83
- pool.address,
84
- CONTRACT_ADDRESS[network].WETH,
85
- pool.signer
86
- );
87
- expect(wethAllowanceDelta.gt(0));
88
- });
89
-
90
- it("approves unlimited WETH on for UniswapV3 LP", async () => {
91
- await pool.approveUniswapV3Liquidity(
76
+ it("approves unlimited USDC and WETH for UniswapV3 LP", async () => {
77
+ const usdcTx = await pool.approveUniswapV3Liquidity(
92
78
  CONTRACT_ADDRESS[network].USDC,
93
79
  ethers.constants.MaxInt256
94
80
  );
95
- await pool.approveUniswapV3Liquidity(
81
+ await usdcTx.wait(1);
82
+ const wethTx = await pool.approveUniswapV3Liquidity(
96
83
  CONTRACT_ADDRESS[network].WETH,
97
84
  ethers.constants.MaxInt256
98
85
  );
99
- const UsdcAllowanceDelta = await allowanceDelta(
100
- pool.address,
86
+ await wethTx.wait(1);
87
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
88
+ const positionManager = nonfungiblePositionManagerAddress[network][
89
+ Dapp.UNISWAPV3
90
+ ]!;
91
+ const allowanceAbi = [
92
+ "function allowance(address,address) view returns (uint256)"
93
+ ];
94
+ const usdcAllowance = await new ethers.Contract(
101
95
  CONTRACT_ADDRESS[network].USDC,
102
- pool.address,
96
+ allowanceAbi,
103
97
  pool.signer
104
- );
105
-
106
- expect(UsdcAllowanceDelta).not.toBe(null);
98
+ ).allowance(pool.address, positionManager);
99
+ const wethAllowance = await new ethers.Contract(
100
+ CONTRACT_ADDRESS[network].WETH,
101
+ allowanceAbi,
102
+ pool.signer
103
+ ).allowance(pool.address, positionManager);
104
+ expect(usdcAllowance.gt(0)).toBe(true);
105
+ expect(wethAllowance.gt(0)).toBe(true);
107
106
  });
108
107
 
109
108
  it("adds WETH and USDC to a new V3 pool", async () => {
110
- let result = null;
111
109
  const pool = await dhedge.loadPool(TEST_POOL[network]);
112
110
  const usdcBalance = await dhedge.utils.getBalance(
113
111
  CONTRACT_ADDRESS[network].USDC,
@@ -118,39 +116,33 @@ const testUniswapV3 = ({ wallet, network, provider }: TestingRunParams) => {
118
116
  pool.address
119
117
  );
120
118
 
121
- try {
122
- result = await pool.addLiquidityUniswapV3(
123
- Dapp.UNISWAPV3,
124
- CONTRACT_ADDRESS[network].WETH,
125
- CONTRACT_ADDRESS[network].USDC,
126
- wethBalance,
127
- usdcBalance,
128
- 3500,
129
- 4000,
130
- null,
131
- null,
132
- 500
133
- // options
134
- );
135
- await result.wait(1);
136
-
137
- tokenId = await nonfungiblePositionManager.tokenOfOwnerByIndex(
138
- pool.address,
139
- 0
140
- );
141
- } catch (e) {
142
- console.log("e", e);
143
- }
119
+ const result = await pool.addLiquidityUniswapV3(
120
+ Dapp.UNISWAPV3,
121
+ CONTRACT_ADDRESS[network].WETH,
122
+ CONTRACT_ADDRESS[network].USDC,
123
+ wethBalance,
124
+ usdcBalance,
125
+ 3500,
126
+ 4000,
127
+ null,
128
+ null,
129
+ 500
130
+ );
131
+ await result.wait(1);
132
+
133
+ tokenId = await nonfungiblePositionManager.tokenOfOwnerByIndex(
134
+ pool.address,
135
+ 0
136
+ );
144
137
  expect(result).not.toBe(null);
145
138
  });
146
139
 
147
- it("should remove liquidity from an existing pool ", async () => {
140
+ it("should remove 50% liquidity from an existing pool", async () => {
148
141
  const result = await pool.decreaseLiquidity(
149
142
  Dapp.UNISWAPV3,
150
143
  tokenId.toString(),
151
- 50 // precent
144
+ 50 // percent
152
145
  );
153
- // console.log("result", result);
154
146
  expect(result).not.toBe(null);
155
147
  });
156
148
 
@@ -168,63 +160,20 @@ const testUniswapV3 = ({ wallet, network, provider }: TestingRunParams) => {
168
160
  Dapp.UNISWAPV3,
169
161
  tokenId.toString(),
170
162
  wethBalance,
171
- usdcBalance // eth
163
+ usdcBalance
172
164
  );
173
- // console.log("result", result);
174
165
  expect(result).not.toBe(null);
175
166
  });
176
167
 
177
- it("should claim fees an existing pool", async () => {
168
+ it("should claim fees from an existing pool", async () => {
178
169
  const result = await pool.claimFees(Dapp.UNISWAPV3, tokenId.toString());
179
- // console.log("result", result);
180
170
  expect(result).not.toBe(null);
181
171
  });
182
-
183
- // it("approves unlimited USDC to swap on UniswapV3", async () => {
184
- // let result;
185
- // const pool = await dhedge.loadPool(TEST_POOL);
186
- // try {
187
- // result = await pool.approve(
188
- // Dapp.UNISWAPV3,
189
- // USDC,
190
- // ethers.constants.MaxInt256,
191
- // options
192
- // );
193
- // console.log(result);
194
- // } catch (e) {
195
- // console.log(e);
196
- // }
197
- // expect(result).not.toBe(null);
198
- // });
199
-
200
- // it("should swap USDC into WETH on UniswapV3 pool", async () => {
201
- // const pool = await dhedge.loadPool(TEST_POOL);
202
- // const result = await pool.tradeUniswapV3(
203
- // USDC,
204
- // WETH,
205
- // "1000000",
206
- // FeeAmount.LOW,
207
- // 1,
208
- // options
209
- // );
210
-
211
- // console.log(result);
212
- // expect(result).not.toBe(null);
213
- // });
214
172
  });
215
173
  };
216
174
 
217
175
  testingHelper({
218
- network: Network.OPTIMISM,
219
- testingRun: testUniswapV3
176
+ network: Network.ARBITRUM,
177
+ testingRun: testUniswapV3,
178
+ onFork: true
220
179
  });
221
-
222
- // testingHelper({
223
- // network: Network.POLYGON,
224
- // testingRun: testUniswapV3
225
- // });
226
-
227
- // testingHelper({
228
- // network: Network.ARBITRUM,
229
- // testingRun: testUniswapV3
230
- // });
@@ -9,6 +9,8 @@ import {
9
9
  } from "../constants";
10
10
  import { Pool } from "../../entities";
11
11
  import AssetHandler from "../../abi/AssetHandler.json";
12
+ import PoolLogicAbi from "../../abi/PoolLogic.json";
13
+ import PoolManagerLogicAbi from "../../abi/PoolManagerLogic.json";
12
14
 
13
15
  export type TestingRunParams = {
14
16
  network: Network;
@@ -146,6 +148,124 @@ export const runWithImpersonateAccount = async (
146
148
  await provider.send("hardhat_stopImpersonatingAccount", [account]);
147
149
  };
148
150
 
151
+ // Fixes oracle staleness reverts on hardhat forks for ChainlinkPythPriceAggregator and PythPriceAggregator.
152
+ // These aggregators have their own maxAge staleness checks separate from the AssetHandler's chainlinkTimeout.
153
+ // This function dynamically finds all such aggregators for a pool's supported assets and overrides
154
+ // their maxAge fields to max uint32 (0xffffffff, ~136 years).
155
+ //
156
+ // Slot layouts below are written in big-endian hex order (high-order bytes first), matching the 64-char
157
+ // string returned by eth_getStorageAt. Within a packed slot, Solidity lays fields out from the low-order
158
+ // end in declaration order — so the first declared field ends up on the right side of the hex string.
159
+ //
160
+ // ChainlinkPythPriceAggregator storage layout:
161
+ // slot 0: asset (address)
162
+ // slot 1: oracleData.onchainOracle = [8 bytes padding][4 bytes maxAge][20 bytes oracleContract]
163
+ // → maxAge at hex offset 16
164
+ // slot 2: oracleData.offchainOracle.priceId (bytes32)
165
+ // slot 3: oracleData.offchainOracle = [24 bytes padding][4 bytes maxAge][4 bytes minConfidenceRatio]
166
+ // → maxAge at hex offset 48
167
+ //
168
+ // PythPriceAggregator storage layout:
169
+ // slot 0: asset (address)
170
+ // slot 1: oracleData.priceId (bytes32)
171
+ // slot 2: oracleData = [24 bytes padding][4 bytes maxAge][4 bytes minConfidenceRatio]
172
+ // → maxAge at hex offset 48
173
+ const MAX_UINT32_HEX = "ffffffff";
174
+
175
+ const overrideMaxAgeInSlot = async (
176
+ provider: ethers.providers.JsonRpcProvider,
177
+ contractAddress: string,
178
+ slotIndex: string,
179
+ maxAgeHexOffset: number // character offset of maxAge within the 64-char hex string
180
+ ): Promise<void> => {
181
+ const slot: string = await provider.send("eth_getStorageAt", [
182
+ contractAddress,
183
+ slotIndex,
184
+ "latest"
185
+ ]);
186
+ const hex = slot.slice(2); // remove 0x
187
+ const newHex =
188
+ hex.slice(0, maxAgeHexOffset) +
189
+ MAX_UINT32_HEX +
190
+ hex.slice(maxAgeHexOffset + 8);
191
+ await provider.send("hardhat_setStorageAt", [
192
+ contractAddress,
193
+ slotIndex,
194
+ "0x" + newHex
195
+ ]);
196
+ };
197
+
198
+ export const fixOracleAggregatorStaleness = async ({
199
+ pool,
200
+ provider
201
+ }: {
202
+ pool: Pool;
203
+ provider: ethers.providers.JsonRpcProvider;
204
+ }): Promise<void> => {
205
+ const assetHandlerAddress = await pool.factory.callStatic.getAssetHandler();
206
+ const assetHandlerContract = new Contract(
207
+ assetHandlerAddress,
208
+ AssetHandler.abi,
209
+ provider
210
+ );
211
+
212
+ // Resolve managerLogic from poolLogic to support both isDhedge=true and isDhedge=false pools
213
+ const poolLogic = new Contract(pool.address, PoolLogicAbi.abi, provider);
214
+ const managerLogicAddress: string = await poolLogic.poolManagerLogic();
215
+ const managerLogic = new Contract(
216
+ managerLogicAddress,
217
+ PoolManagerLogicAbi.abi,
218
+ provider
219
+ );
220
+ const supportedAssets: {
221
+ asset: string;
222
+ }[] = await managerLogic.getSupportedAssets();
223
+
224
+ // ABI fragments to detect aggregator type
225
+ const chainlinkPythAbi = [
226
+ "function oracleData() view returns (address oracleContract, uint32 maxAge)"
227
+ ];
228
+ const pythAbi = [
229
+ "function oracleData() view returns (bytes32 priceId, uint32 maxAge, uint32 minConfidenceRatio)"
230
+ ];
231
+
232
+ for (const { asset } of supportedAssets) {
233
+ try {
234
+ const aggregatorAddress: string = await assetHandlerContract.priceAggregators(
235
+ asset
236
+ );
237
+ if (aggregatorAddress === ethers.constants.AddressZero) continue;
238
+
239
+ // Try ChainlinkPythPriceAggregator first (has onchainOracle + offchainOracle)
240
+ const agg = new Contract(aggregatorAddress, chainlinkPythAbi, provider);
241
+ try {
242
+ await agg.oracleData();
243
+ // ChainlinkPythPriceAggregator detected.
244
+ // Slot 1 layout: [8 bytes padding][4 bytes maxAge][20 bytes oracleContract] → maxAge at offset 16
245
+ await overrideMaxAgeInSlot(provider, aggregatorAddress, "0x1", 16);
246
+ // Slot 3 layout: [24 bytes padding][4 bytes maxAge][4 bytes minConfidenceRatio] → maxAge at offset 48
247
+ await overrideMaxAgeInSlot(provider, aggregatorAddress, "0x3", 48);
248
+ continue;
249
+ } catch {
250
+ // Not a ChainlinkPythPriceAggregator
251
+ }
252
+
253
+ // Try PythPriceAggregator (has only offchainOracle)
254
+ const pythAgg = new Contract(aggregatorAddress, pythAbi, provider);
255
+ try {
256
+ await pythAgg.oracleData();
257
+ // PythPriceAggregator detected.
258
+ // Slot 2 layout: [24 bytes padding][4 bytes maxAge][4 bytes minConfidenceRatio] → maxAge at offset 48
259
+ await overrideMaxAgeInSlot(provider, aggregatorAddress, "0x2", 48);
260
+ } catch {
261
+ // Not a PythPriceAggregator either — skip
262
+ }
263
+ } catch {
264
+ // No aggregator set for this asset — skip
265
+ }
266
+ }
267
+ };
268
+
149
269
  export const setChainlinkTimeout = async (
150
270
  {
151
271
  pool,