@dhedge/v2-sdk 2.1.8 → 2.2.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.
Files changed (95) hide show
  1. package/README.md +404 -54
  2. package/dist/config.d.ts +13 -2
  3. package/dist/entities/pool.d.ts +25 -86
  4. package/dist/entities/utils.d.ts +15 -0
  5. package/dist/services/hyperliquid/index.d.ts +22 -0
  6. package/dist/services/kyberSwap/index.d.ts +1 -1
  7. package/dist/services/ondo/index.d.ts +5 -0
  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 +5133 -6641
  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 +5138 -6641
  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/abi/ondo/IOndoGMSwap.json +30 -0
  27. package/src/config.ts +15 -9
  28. package/src/entities/pool.ts +56 -253
  29. package/src/entities/utils.ts +15 -0
  30. package/src/services/hyperliquid/index.ts +22 -0
  31. package/src/services/kyberSwap/index.ts +5 -3
  32. package/src/services/ondo/index.ts +142 -0
  33. package/src/services/oneInch/index.ts +5 -4
  34. package/src/services/toros/completeWithdrawal.ts +57 -40
  35. package/src/services/toros/easySwapper.ts +15 -1
  36. package/src/services/toros/initWithdrawal.ts +39 -31
  37. package/src/services/toros/swapData.ts +45 -131
  38. package/src/services/uniswap/V3Liquidity.ts +3 -24
  39. package/src/services/velodrome/liquidity.ts +3 -0
  40. package/src/test/aave.test.ts +99 -70
  41. package/src/test/aerodrome.test.ts +53 -24
  42. package/src/test/aerodromeCL.test.ts +64 -30
  43. package/src/test/arrakis.test.ts +23 -35
  44. package/src/test/balancer.test.ts +114 -106
  45. package/src/test/compoundV3.test.ts +45 -29
  46. package/src/test/constants.ts +56 -11
  47. package/src/test/cowswap.test.ts +33 -35
  48. package/src/test/dhedge.test.ts +45 -12
  49. package/src/test/flatmoney.test.ts +25 -39
  50. package/src/test/fluid.test.ts +33 -24
  51. package/src/test/hyperliquid.onchain.test.ts +131 -0
  52. package/src/test/kyberSwap.test.ts +37 -16
  53. package/src/test/lyra.test.ts +159 -150
  54. package/src/test/odos.test.ts +2 -2
  55. package/src/test/ondo.onchain.test.ts +132 -0
  56. package/src/test/oneInch.test.ts +36 -22
  57. package/src/test/pancakeCL.test.ts +72 -31
  58. package/src/test/pendle.test.ts +94 -54
  59. package/src/test/{pendleMint.test.ts → pendleMint.onchain.test.ts} +22 -8
  60. package/src/test/pool.test.ts +152 -95
  61. package/src/test/toros.onchain.test.ts +92 -0
  62. package/src/test/toros.test.ts +74 -20
  63. package/src/test/torosLimitOrder.test.ts +87 -42
  64. package/src/test/uniswap.test.ts +77 -128
  65. package/src/test/utils/testingHelper.ts +120 -0
  66. package/src/test/velodrome.test.ts +126 -92
  67. package/src/test/velodromeCL.test.ts +43 -31
  68. package/src/test/velodromeV2.test.ts +153 -95
  69. package/src/types.ts +22 -6
  70. package/src/utils/contract.ts +20 -0
  71. package/dist/services/futures/constants.d.ts +0 -1
  72. package/dist/services/futures/index.d.ts +0 -2
  73. package/dist/services/futures/margin.d.ts +0 -2
  74. package/dist/services/futures/trade.d.ts +0 -3
  75. package/dist/services/ramses/vesting.d.ts +0 -4
  76. package/dist/services/uniswap/V3Trade.d.ts +0 -3
  77. package/dist/test/utils/futures.d.ts +0 -2
  78. package/src/abi/IRamsesNonfungiblePositionManager.json +0 -486
  79. package/src/abi/ISynthetiXFuturesMarketV2.json +0 -531
  80. package/src/abi/ISynthetix.json +0 -139
  81. package/src/abi/IUniswapV3Quoter.json +0 -195
  82. package/src/abi/IUniswapV3Router.json +0 -221
  83. package/src/abi/IXRam.json +0 -99
  84. package/src/services/futures/constants.ts +0 -1
  85. package/src/services/futures/index.ts +0 -2
  86. package/src/services/futures/margin.ts +0 -10
  87. package/src/services/futures/trade.ts +0 -32
  88. package/src/services/ramses/vesting.ts +0 -24
  89. package/src/services/uniswap/V3Trade.ts +0 -46
  90. package/src/test/futures.test.ts +0 -51
  91. package/src/test/hyperliquid.test.ts +0 -107
  92. package/src/test/ramses.test.ts +0 -190
  93. package/src/test/ramsesCL.test.ts +0 -155
  94. package/src/test/synthetix.test.ts +0 -36
  95. package/src/test/utils/futures.ts +0 -14
package/README.md CHANGED
@@ -13,18 +13,39 @@
13
13
 
14
14
  ## Installation
15
15
 
16
- ### Node
17
-
18
16
  ```bash
17
+ # npm
19
18
  npm install @dhedge/v2-sdk
19
+
20
+ # yarn
21
+ yarn add @dhedge/v2-sdk
20
22
  ```
21
23
 
22
- ### Yarn
24
+ ## Quick Start
23
25
 
24
- ```bash
25
- yarn add @dhedge/v2-sdk
26
+ ```ts
27
+ import { Dhedge, Dapp, Network, ethers } from "@dhedge/v2-sdk";
28
+
29
+ // 1. Connect a wallet
30
+ const provider = new ethers.providers.JsonRpcProvider("YOUR_RPC_URL");
31
+ const wallet = new ethers.Wallet("YOUR_PRIVATE_KEY", provider);
32
+ const dhedge = new Dhedge(wallet, Network.POLYGON);
33
+
34
+ // 2. Load an existing vault and check its composition
35
+ const vault = await dhedge.loadPool("VAULT_ADDRESS");
36
+ const composition = await vault.getComposition();
37
+
38
+ // 3. Deposit USDC into the vault
39
+ await vault.approveDeposit("USDC_ADDRESS", ethers.constants.MaxUint256);
40
+ await vault.deposit("USDC_ADDRESS", "1000000"); // 1 USDC (6 decimals)
41
+
42
+ // 4. Trade USDC → WETH on KyberSwap (no API key needed)
43
+ await vault.approve(Dapp.KYBERSWAP, "USDC_ADDRESS", ethers.constants.MaxUint256);
44
+ await vault.trade(Dapp.KYBERSWAP, "USDC_ADDRESS", "WETH_ADDRESS", "1000000", 0.5);
26
45
  ```
27
46
 
47
+ > For aggregator-backed trades (1Inch, Odos), you'll need API keys — see [Initial Setup](#initial-setup).
48
+
28
49
  ## Usage
29
50
 
30
51
  ### Table of Contents
@@ -32,6 +53,14 @@ yarn add @dhedge/v2-sdk
32
53
  <ol>
33
54
  <li>
34
55
  <a href="#initial-setup">Initial setup</a>
56
+ <ul>
57
+ <li><a href="#supported-networks">Supported networks</a></li>
58
+ <li><a href="#looking-up-addresses">Looking up addresses</a></li>
59
+ <li><a href="#common-revert-reasons">Common revert reasons</a></li>
60
+ <li><a href="#reference-tests-as-live-examples">Reference: tests as live examples</a></li>
61
+ <li><a href="#execution-model">Execution model</a></li>
62
+ <li><a href="#simulation-and-tx-data">Simulation and tx data</a></li>
63
+ </ul>
35
64
  </li>
36
65
  <li>
37
66
  <a href="#general-vault-management">General Vault Management</a>
@@ -44,8 +73,11 @@ yarn add @dhedge/v2-sdk
44
73
  <li><a href="#6-approve-asset-for-deposit">Approve asset for deposit</a></li>
45
74
  <li><a href="#7-deposit-asset-into-a-vault">Deposit asset into a vault</a></li>
46
75
  <li><a href="#8-withdraw-from-a-vault">Withdraw from a vault</a></li>
47
- <li><a href="#9-approve-vault-asset-for-trading--staking)">Approve vault asset for trading & staking</a></li>
76
+ <li><a href="#9-approve-vault-asset-for-trading--staking">Approve vault asset for trading & staking</a></li>
48
77
  <li><a href="#10-trade-vault-assets">Trade vault assets</a></li>
78
+ <li><a href="#toros-mint-flow">Toros mint flow</a></li>
79
+ <li><a href="#toros-redeem-flow">Toros redeem flow</a></li>
80
+ <li><a href="#toros-limit-orders">Toros limit orders</a></li>
49
81
  </ul>
50
82
  </li>
51
83
  <li>
@@ -61,13 +93,28 @@ yarn add @dhedge/v2-sdk
61
93
  <a href="#uniswap-v3-style">Uniswap-v3 style protocols</a>
62
94
  </li>
63
95
  <li>
64
- <a href="#velodromev2--ramses">VelodromeV2 / Ramses / Aerodrome</a>
96
+ <a href="#velodromecl--aerodromecl--pancakecl">VelodromeCL / AerodromeCL / PancakeCL</a>
97
+ </li>
98
+ <li>
99
+ <a href="#velodromev2--aerodrome">VelodromeV2 / Aerodrome</a>
100
+ </li>
101
+ <li>
102
+ <a href="#pendle">Pendle</a>
65
103
  </li>
66
104
  </ul>
67
105
  </li>
68
106
  <li>
69
107
  <a href="#lendingborrowing-aave">Lending/Borrowing Aave</a>
70
108
  </li>
109
+ <li>
110
+ <a href="#lending-compoundv3--fluid">Lending CompoundV3 / Fluid</a>
111
+ </li>
112
+ <li>
113
+ <a href="#the-flat-money-protocol">The Flat Money Protocol</a>
114
+ </li>
115
+ <li>
116
+ <a href="#development">Development</a>
117
+ </li>
71
118
 
72
119
  </ol>
73
120
 
@@ -77,21 +124,24 @@ yarn add @dhedge/v2-sdk
77
124
 
78
125
  ---
79
126
 
80
- If you want to use aggregators such as 1Inch or Odos, copy `.env.example` to `.env` and configure the API keys you need.
127
+ If you want to use aggregators such as 1Inch or Odos, or Ondo GM token swaps, copy `.env.example` to `.env` and configure the API keys you need.
81
128
 
82
129
  ```
83
130
  ONEINCH_API_KEY=YOUR_API_KEY_FROM_1INCH
84
131
  ODOS_API_KEY=YOUR_ODOS_API_KEY
132
+ ONDO_API_KEY=YOUR_ONDO_API_KEY
85
133
  ```
86
134
 
87
135
  - `ONEINCH_API_KEY` is required for `Dapp.ONEINCH`
88
136
  - `ODOS_API_KEY` is required for `Dapp.ODOS`
89
137
  - `ODOS_API_KEY` can also be required by `completeTorosWithdrawal(...)` when a Toros withdrawal needs Odos-backed swap data
138
+ - `ONDO_API_KEY` is required for Ondo GM token swaps (`Dapp.ONDO`)
90
139
 
91
140
  To get started:
92
141
 
93
142
  - get a 1inch API key from the [1inch Developer Portal](https://docs.1inch.io/docs/aggregation-protocol/introduction)
94
143
  - get an Odos API key via the [Odos developer docs](https://docs.odos.xyz/build/api-docs), and make sure it is valid for the Odos endpoint configured by this SDK
144
+ - get an Ondo API key by contacting [onboarding@ondo.finance](mailto:onboarding@ondo.finance)
95
145
  - place the keys in a `.env` file at the project root before calling aggregator-backed methods
96
146
  - if a required key is missing, the SDK will fail before transaction submission
97
147
 
@@ -118,6 +168,53 @@ Important notes:
118
168
  - `loadPool(address)` assumes the address is a dHEDGE pool and resolves the manager contract automatically.
119
169
  - `loadPool(address, false)` can be used when you want to work with a non-dHEDGE contract that should execute directly from the signer.
120
170
 
171
+ #### Supported networks
172
+
173
+ The SDK supports six networks: **Polygon**, **Optimism**, **Arbitrum**, **Base**, **Ethereum**, **Hyperliquid**. The most broadly-deployed protocols across these networks are **1inch**, **KyberSwap**, **Aave V3**, **Toros**, and **Toros LimitOrder**. The authoritative per-(network, Dapp) registry is `routerAddress` in `src/config.ts` — a Dapp is supported on a network iff `routerAddress[network][dapp]` is set. CL position managers, gauges, FlatMoney, and limit orders use parallel maps in the same file (`nonfungiblePositionManagerAddress`, `stakingAddress`, `flatMoneyContractAddresses`, `limitOrderAddress`).
174
+
175
+ #### Looking up addresses
176
+
177
+ Most code examples use placeholders like `"USDC_TOKEN_ADDRESS"`, `MARKET_ADDRESS`, or `BALANCER_LP_TOKEN_ADDRESS`. The single source of truth for what's deployed and whitelisted on each network is the **dHEDGE [V2-Public config directory](https://github.com/dhedge/V2-Public/tree/master/config)**.
178
+
179
+ What each file gives you:
180
+
181
+ - **`dHEDGE Assets list.json`** — every registered asset/contract: `assetName`, `assetAddress`, `assetType`, oracle config. Filter by `assetName` (e.g. `WBTC`, `USDC Native`, `Toros BTCBULL3X`, `cWETHv3`, `Fluid USDC`, `PT-wstETH-25JUN2026`) to get its address.
182
+ - **`dHEDGE Governance Contract Guards.csv`** — **active** contract guards: which protocols vaults are currently allowed to call (Aave, Pendle, Velodrome routers, Toros LimitOrderManager, etc.).
183
+ - **`dHEDGE Deprecated Contract Guards.csv`** — **deprecated** contract guards: protocols that used to be allowed but no longer are. Useful when a previously-working address starts reverting.
184
+ - **`dHEDGE Governance Asset Guards.csv`** — per-asset-type rules (which asset types vaults can hold).
185
+
186
+ Per-network direct links (raw URLs are agent-friendly — fetch them directly):
187
+
188
+ | Network | Folder | Assets | Active guards | Deprecated | Asset guards |
189
+ | --- | --- | --- | --- | --- | --- |
190
+ | Polygon | [folder](https://github.com/dhedge/V2-Public/tree/master/config/polygonProd) | [json](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/polygonProd/dHEDGE%20Assets%20list%20-%20Polygon.json) | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/polygonProd/dHEDGE%20Governance%20Contract%20Guards%20-%20Polygon.csv) | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/polygonProd/dHEDGE%20Deprecated%20Contract%20Guards%20-%20Polygon.csv) | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/polygonProd/dHEDGE%20Governance%20Asset%20Guards%20-%20Polygon.csv) |
191
+ | Optimism | [folder](https://github.com/dhedge/V2-Public/tree/master/config/ovm) | [json](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/ovm/dHEDGE%20Assets%20list.json) | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/ovm/dHEDGE%20Governance%20Contract%20Guards.csv) | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/ovm/dHEDGE%20Deprecated%20Contract%20Guards.csv) | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/ovm/dHEDGE%20Governance%20Asset%20Guards.csv) |
192
+ | Arbitrum | [folder](https://github.com/dhedge/V2-Public/tree/master/config/arbitrum) | [json](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/arbitrum/dHEDGE%20Assets%20list.json) | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/arbitrum/dHEDGE%20Governance%20Contract%20Guards.csv) | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/arbitrum/dHEDGE%20Deprecated%20Contract%20Guards.csv) | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/arbitrum/dHEDGE%20Governance%20Asset%20Guards.csv) |
193
+ | Base | [folder](https://github.com/dhedge/V2-Public/tree/master/config/base) | [json](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/base/dHEDGE%20Assets%20list.json) | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/base/dHEDGE%20Governance%20Contract%20Guards.csv) | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/base/dHEDGE%20Deprecated%20Contract%20Guards.csv) | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/base/dHEDGE%20Governance%20Asset%20Guards.csv) |
194
+ | Ethereum | [folder](https://github.com/dhedge/V2-Public/tree/master/config/ethereum) | [json](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/ethereum/dHEDGE%20Assets%20list.json) | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/ethereum/dHEDGE%20Governance%20Contract%20Guards.csv) | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/ethereum/dHEDGE%20Deprecated%20Contract%20Guards.csv) | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/ethereum/dHEDGE%20Governance%20Asset%20Guards.csv) |
195
+ | Hyperliquid | [folder](https://github.com/dhedge/V2-Public/tree/master/config/hyperevm) | [json](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/hyperevm/dHEDGE%20Assets%20list.json) | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/hyperevm/dHEDGE%20Governance%20Contract%20Guards.csv) | n/a | [csv](https://raw.githubusercontent.com/dhedge/V2-Public/master/config/hyperevm/dHEDGE%20Governance%20Asset%20Guards.csv) |
196
+
197
+ Router addresses (for `pool.approve(dapp, ...)`) live in `src/config.ts` → `routerAddress[network][dapp]`.
198
+
199
+ #### Common revert reasons
200
+
201
+ Two canonical references in [dhedge/V2-Public](https://github.com/dhedge/V2-Public/tree/master/readmes/errorCodes):
202
+
203
+ - [`dHEDGEV2ErrorCodes.json`](https://raw.githubusercontent.com/dhedge/V2-Public/master/readmes/errorCodes/dHEDGEV2ErrorCodes.json) — every `dhN` string thrown by dHEDGE contracts (e.g. `dh22`, `dh4`), with a description and recommended action. Fetch the JSON and look up `result["dh22"]`.
204
+ - [`4byteErrorCodes.md`](https://raw.githubusercontent.com/dhedge/V2-Public/master/readmes/errorCodes/4byteErrorCodes.md) — protocol-specific 4-byte custom-error selectors (DYTM, etc.) mapped to their source contracts.
205
+
206
+ For 4-byte selectors not in the file above, decode them with `cast 4byte 0x<selector>` (Foundry).
207
+
208
+ #### Reference: tests as live examples
209
+
210
+ Each protocol section below has a matching test under `src/test/`. The tests are the most up-to-date reference for the SDK's **call patterns** — they show the actual sequence of SDK methods (approve → trade, addLiquidity → stake, etc.) for each integration.
211
+
212
+ When reading a test as a reference, focus on the SDK calls themselves and ignore the test-only scaffolding (manager impersonation, asset funding via `setUSDCAmount` / `setTokenAmount`, oracle staleness fixes, fork RPC plumbing) — those exist because tests run against a Hardhat fork. A real vault manager calling the SDK from production code already _is_ the manager, holds the assets, and uses live RPC, so the only transferable part is the SDK invocation sequence.
213
+
214
+ Naming convention:
215
+ - `src/test/<protocol>.test.ts` — runs against a Hardhat fork.
216
+ - `src/test/<protocol>.onchain.test.ts` — requires a live RPC (used when a fork can't reproduce the path: CowSwap solver settlement, Hyperliquid CoreWriter, Toros `completeWithdrawal` aggregator quotes).
217
+
121
218
  #### Execution model
122
219
 
123
220
  By default, manager actions are executed through `pool.poolLogic.execTransaction(...)`.
@@ -129,8 +226,8 @@ const tx = await pool.trade(
129
226
  Dapp.ONEINCH,
130
227
  "USDC_TOKEN_ADDRESS",
131
228
  "WETH_TOKEN_ADDRESS",
132
- "1000000",
133
- 0.5,
229
+ "1000000", // 1 USDC (6 decimals)
230
+ 0.5, // slippage %
134
231
  null,
135
232
  {
136
233
  estimateGas: true,
@@ -141,19 +238,22 @@ const tx = await pool.trade(
141
238
 
142
239
  #### Simulation and tx data
143
240
 
144
- Most manager methods accept `sdkOptions` as the last argument.
241
+ Most manager methods accept `sdkOptions` as the last argument. The return shape depends on which flag you set:
242
+
243
+ - **No flag (default)** — sends the transaction and returns the underlying ethers `ContractTransaction` / `TransactionResponse`.
244
+ - **`true`** — shorthand for `{ estimateGas: true }`.
245
+ - **`{ estimateGas: true }`** — simulates and returns `{ gas, gasEstimationError, to, txData, minAmountOut }`. `gas` is `null` when `gasEstimationError` is set.
246
+ - **`{ onlyGetTxData: true }`** — returns `{ to, txData, minAmountOut }` (no `gas` / `gasEstimationError`) without sending or simulating.
145
247
 
146
- - `true` is shorthand for `{ estimateGas: true }`
147
- - `{ estimateGas: true }` returns `{ gas, gasEstimationError, to, txData, minAmountOut }`
148
- - `{ onlyGetTxData: true }` returns `{ to, txData, minAmountOut }` without sending a transaction
248
+ > `minAmountOut` is only populated by `pool.trade(...)` for the swap aggregators that fetch a quote from an external API: `Dapp.ONEINCH`, `Dapp.ODOS`, `Dapp.KYBERSWAP`, and `Dapp.PENDLE`. For all other Dapps and for non-trade methods (`approve`, `lend`, `addLiquidity*`, etc.) it is `null` — either the slippage limit is encoded inside the calldata at execution time, or the operation has no concept of a min-out. `Dapp.COWSWAP` rejects `estimateGas` and `onlyGetTxData` entirely.
149
249
 
150
250
  ```ts
151
251
  const result = await pool.trade(
152
252
  Dapp.ONEINCH,
153
253
  "USDC_TOKEN_ADDRESS",
154
254
  "WETH_TOKEN_ADDRESS",
155
- "1000000",
156
- 0.5,
255
+ "1000000", // 1 USDC (6 decimals)
256
+ 0.5, // slippage %
157
257
  null,
158
258
  { estimateGas: true }
159
259
  )
@@ -260,7 +360,7 @@ const tx = await vault.approveDeposit("USDC_TOKEN_ADDRESS", ethers.constants.Max
260
360
  Deposit 1 USDC into a vault
261
361
 
262
362
  ```ts
263
- const usdcDepositAmount = "100000"
363
+ const usdcDepositAmount = "1000000" // 1 USDC (6 decimals)
264
364
  const tx = await vault.deposit("USDC_TOKEN_ADDRESS", usdcDepositAmount);
265
365
  ```
266
366
 
@@ -277,11 +377,11 @@ const tx = await vault.withdraw(poolTokensWithdrawAmount);
277
377
 
278
378
  Before the vault can trade or stake an asset on an external protocol, the vault must approve that protocol router or staking contract.
279
379
 
280
- Approve unlimited amount of USDC to trade on Sushiswap
380
+ Approve unlimited amount of USDC to trade on KyberSwap
281
381
 
282
382
  ```ts
283
383
  const tx = await vault.approve(
284
- Dapp.SUSHISWAP,
384
+ Dapp.KYBERSWAP,
285
385
  "USDC_TOKEN_ADDRESS",
286
386
  ethers.constants.MaxInt256
287
387
  )
@@ -289,20 +389,23 @@ const tx = await vault.approve(
289
389
 
290
390
  Approval model summary:
291
391
 
292
- - `approveDeposit(asset, amount)` approves from the user wallet to the vault
293
- - `approve(dapp, asset, amount)` approves from the vault to a protocol router
294
- - `approveStaking(dapp, asset, amount)` approves from the vault to a staking contract
295
- - `approveSpender(spender, asset, amount)` is available for custom integrations
392
+ | Method | From To | Spender resolution | When to use |
393
+ | --- | --- | --- | --- |
394
+ | `approveDeposit(asset, amount)` | user wallet → vault | the vault itself | Before `vault.deposit(asset, amount)` |
395
+ | `approve(dapp, asset, amount)` | vault protocol router | `routerAddress[network][dapp]` | Before `vault.trade`, `vault.lend`, `vault.borrow`, etc. |
396
+ | `approveStaking(dapp, asset, amount)` | vault → staking contract | `stakingAddress[network][dapp]` | Before `vault.stake(dapp, ...)` (V2-style farms) |
397
+ | `approveSpender(spender, asset, amount)` | vault → arbitrary spender | caller-supplied | Custom integrations, CL position managers, etc. |
398
+ | `approveSpenderNFT(spender, nftContract, tokenId)` | vault → arbitrary spender (ERC-721) | caller-supplied | Approving a single CL position NFT before `stakeInGauge` |
296
399
 
297
400
  #### 10. Trade vault assets
298
401
 
299
- Trade 1 USDC into DAI on Sushiswap (other options depend on network and configured API keys, and can include TOROS, QUICKSWAP, BALANCER, ONEINCH, and ODOS)
402
+ Trade 1 USDC into DAI on KyberSwap. Other `Dapp` values supported by `vault.trade(...)` depend on the network they include `TOROS`, `BALANCER`, `ONEINCH` (requires `ONEINCH_API_KEY`), `ODOS` (requires `ODOS_API_KEY`), `PENDLE`, and `COWSWAP`.
300
403
 
301
404
  ```ts
302
- const amountIn = "1000000"
405
+ const amountIn = "1000000" // 1 USDC (6 decimals)
303
406
  const slippage = 0.5
304
407
  const tx = await vault.trade(
305
- Dapp.SUSHISWAP,
408
+ Dapp.KYBERSWAP,
306
409
  "USDC_TOKEN_ADDRESS",
307
410
  "DAI_TOKEN_ADDRESS",
308
411
  amountIn,
@@ -310,6 +413,28 @@ const tx = await vault.trade(
310
413
  )
311
414
  ```
312
415
 
416
+ **CowSwap** behaves differently from the other aggregators:
417
+
418
+ - Approve the **CoW Vault Relayer** (`0xC92E8bdf79f0507f65a392b0ab4667716BFE0110`) via `approveSpender`, not via `approve(Dapp.COWSWAP, ...)`.
419
+ - A single `vault.trade(Dapp.COWSWAP, ...)` call internally sends **two transactions** (submit the signed order, then `setPreSignature` on GPv2Settlement). The order is filled off-chain by solvers.
420
+ - `estimateGas` and `onlyGetTxData` are not supported.
421
+
422
+ ```ts
423
+ await vault.approveSpender(
424
+ "0xC92E8bdf79f0507f65a392b0ab4667716BFE0110", // CoW Vault Relayer
425
+ "USDC_TOKEN_ADDRESS",
426
+ ethers.constants.MaxUint256
427
+ );
428
+
429
+ await vault.trade(
430
+ Dapp.COWSWAP,
431
+ "USDC_TOKEN_ADDRESS",
432
+ "WETH_TOKEN_ADDRESS",
433
+ "2000000",
434
+ 0.5
435
+ );
436
+ ```
437
+
313
438
  #### Toros mint flow
314
439
 
315
440
  Minting a Toros token uses `vault.trade(...)`, but it is not a generic token-to-token swap. The input asset must be a valid deposit asset for the underlying Toros vault.
@@ -325,7 +450,7 @@ const tx = await vault.trade(
325
450
  Dapp.TOROS,
326
451
  "USDC_TOKEN_ADDRESS",
327
452
  "TOROS_TOKEN_ADDRESS",
328
- "100000000",
453
+ "100000000", // 100 USDC (6 decimals)
329
454
  slippage
330
455
  )
331
456
  ```
@@ -357,7 +482,7 @@ const tx = await vault.trade(
357
482
  Dapp.TOROS,
358
483
  "TOROS_TOKEN_ADDRESS",
359
484
  "USDC_TOKEN_ADDRESS",
360
- "100000000",
485
+ "100000000", // amount of Toros vault tokens to redeem (Toros tokens are 18 decimals)
361
486
  slippage
362
487
  )
363
488
  ```
@@ -371,19 +496,69 @@ const tx = await vault.completeTorosWithdrawal(
371
496
  )
372
497
  ```
373
498
 
499
+ #### Toros limit orders
500
+
501
+ Toros vaults support stop-loss / take-profit orders managed by the on-chain `PoolLimitOrderManager`. From a dHEDGE vault, the manager (or trader) can register an order against a held Toros vault token; once the pricing-asset price crosses one of the thresholds the order becomes executable.
502
+
503
+ A vault may hold at most one active limit order per Toros vault token. Prices are passed in 18-decimal fixed point (D18). Pass `null`/`undefined` to disable a side — stop-loss defaults to `0` (off) and take-profit defaults to `MaxUint256` (off).
504
+
505
+ The `pricingAsset` must match the address of an asset registered in the dHEDGE asset handler. Pick the entry in `dHEDGE Assets list.json` whose `assetName` represents the Toros vault's price reference (e.g. `WBTC` for BTC vaults, `WETH` for ETH vaults) and use its `assetAddress`. See [Looking up addresses](#looking-up-addresses) for the per-network URLs.
506
+
507
+ 1. Approve the Toros vault token for the limit-order manager (one-time)
508
+
509
+ ```ts
510
+ import { ethers } from "ethers";
511
+
512
+ await vault.approveTorosLimitOrder(
513
+ "TOROS_VAULT_ADDRESS",
514
+ ethers.constants.MaxUint256
515
+ );
516
+ ```
517
+
518
+ 2. Create an order — stop-loss at \$50,000 and take-profit at \$100,000 priced in WBTC
519
+
520
+ ```ts
521
+ await vault.createTorosLimitOrder(
522
+ "TOROS_VAULT_ADDRESS",
523
+ ethers.utils.parseEther("0.001"), // amount of vault tokens (18 decimals)
524
+ ethers.utils.parseEther("50000"), // stopLossPriceD18
525
+ ethers.utils.parseEther("100000"), // takeProfitPriceD18
526
+ "WBTC_TOKEN_ADDRESS" // pricingAsset (assetAddress from dHEDGE Assets list.json)
527
+ );
528
+ ```
529
+
530
+ 3. Read the active order, modify it, or delete it
531
+
532
+ ```ts
533
+ const order = await vault.getTorosLimitOrder(vault.address, "TOROS_VAULT_ADDRESS");
534
+ const exists = await vault.hasActiveTorosLimitOrder(vault.address, "TOROS_VAULT_ADDRESS");
535
+
536
+ await vault.modifyTorosLimitOrder(
537
+ "TOROS_VAULT_ADDRESS",
538
+ order.amount,
539
+ order.stopLossPriceD18.mul(95).div(100), // tighten stop by 5%
540
+ order.takeProfitPriceD18,
541
+ order.pricingAsset
542
+ );
543
+
544
+ await vault.deleteTorosLimitOrder("TOROS_VAULT_ADDRESS");
545
+ ```
546
+
374
547
  ### Liquidity
375
548
 
376
549
  ---
377
550
 
378
551
  #### Uniswap-v2 style
379
552
 
553
+ > **Note:** This path is **legacy**. Sushiswap is the only V2-style DEX still configured (Polygon only) and is not actively used; new integrations should prefer KyberSwap / 1inch / Pendle / Velodrome. The methods below remain for backwards compatibility.
554
+
380
555
  For Uniswap-v2 like protocols, such as sushiswap, we use `addLiquidity`, `removeLiquidity`, `stake`, and `unstake`, and `harvestRewards`
381
556
 
382
557
  1. Add USDC/DAI into a Sushiswap liquidity pool
383
558
 
384
559
  ```ts
385
- const amountUsdc = "1000000"
386
- const amountDai = "997085"
560
+ const amountUsdc = "1000000" // 1 USDC (6 decimals)
561
+ const amountDai = "997085000000000000" // ~0.997 DAI (18 decimals)
387
562
  const tx = await pool.addLiquidity(
388
563
  Dapp.SUSHISWAP,
389
564
  "USDC_TOKEN_ADDRESS",
@@ -396,7 +571,7 @@ const tx = await pool.addLiquidity(
396
571
  2. Remove USDC/DAI worth of 1 Sushiswap LP from the liquidity pool
397
572
 
398
573
  ```ts
399
- const amountSlpUsdcDai = "1000000000000000000"
574
+ const amountSlpUsdcDai = "1000000000000000000" // 1 LP token (18 decimals)
400
575
  const tx = await pool.removeLiquidity(
401
576
  Dapp.SUSHISWAP,
402
577
  "USDC_TOKEN_ADDRESS",
@@ -405,7 +580,7 @@ const tx = await pool.removeLiquidity(
405
580
  )
406
581
  ```
407
582
 
408
- 3. Approve unlimited amound of SLP USDC-DAI token for staking on Sushiswap
583
+ 3. Approve unlimited amount of SLP USDC-DAI token for staking on Sushiswap
409
584
 
410
585
  ```ts
411
586
  const tx = await pool.approveStaking(
@@ -418,7 +593,7 @@ const tx = await pool.approveStaking(
418
593
  4. Stake 1 Sushiswap LP USDC/DAI token
419
594
 
420
595
  ```ts
421
- const amountSlpUsdcDai = "1000000000000000000"
596
+ const amountSlpUsdcDai = "1000000000000000000" // 1 LP token (18 decimals)
422
597
  const tx = await pool.stake(
423
598
  Dapp.SUSHISWAP,
424
599
  "SLP_USDC_DAI_TOKEN_ADDRESS",
@@ -429,7 +604,7 @@ const tx = await pool.stake(
429
604
  5. Unstake 1 Sushiswap LP USDC/DAI token
430
605
 
431
606
  ```ts
432
- const amountSlpUsdcDai = "1000000000000000000"
607
+ const amountSlpUsdcDai = "1000000000000000000" // 1 LP token (18 decimals)
433
608
  const tx = await pool.unstake(
434
609
  Dapp.SUSHISWAP,
435
610
  "SLP_USDC_DAI_TOKEN_ADDRESS",
@@ -455,7 +630,11 @@ For Balancer, we use `joinBalancerPool`, `exitBalancerPool`, and `harvestBalance
455
630
  ```ts
456
631
  const balancerPoolId = "0x03cd191f589d12b0582a99808cf19851e468e6b500010000000000000000000a"
457
632
  const assets = [WBTC_TOKEN_ADDRESS, USDC_TOKEN_ADDRESS, WETH_TOKEN_ADDRESS];
458
- const amounts = ["2000", "1000000", "200000000000000"];
633
+ const amounts = [
634
+ "2000", // 0.00002 WBTC (8 decimals)
635
+ "1000000", // 1 USDC (6 decimals)
636
+ "200000000000000" // 0.0002 WETH (18 decimals)
637
+ ];
459
638
  const tx = await pool.joinBalancerPool(balancerPoolId, assets, amounts)
460
639
  ```
461
640
 
@@ -493,14 +672,15 @@ await pool.approveUniswapV3Liquidity(
493
672
  ethers.constants.MaxInt256
494
673
  );
495
674
  const tx = await pool.addLiquidityUniswapV3(
496
- WETH_ADDRESS
675
+ Dapp.UNISWAPV3,
676
+ WETH_ADDRESS,
497
677
  USDC_ADDRESS,
498
678
  '430000000000000', // wethBalance
499
- '100000000', // usdcBalance
500
- 2000,
501
- 3000,
502
- null,
503
- null,
679
+ '100000000', // usdcBalance
680
+ 2000, // minPrice
681
+ 3000, // maxPrice
682
+ null, // minTick (use null when providing prices)
683
+ null, // maxTick (use null when providing prices)
504
684
  FeeAmount.MEDIUM,
505
685
  )
506
686
  ```
@@ -512,12 +692,11 @@ tokenId = await nonfungiblePositionManager.tokenOfOwnerByIndex(pool.address,0).t
512
692
  const tx = await pool.decreaseLiquidity(
513
693
  Dapp.UNISWAPV3,
514
694
  tokenId,
515
- 50 // precent
695
+ 50 // percent
516
696
  );
517
697
  ```
518
698
 
519
699
  Removing 100% will burn the NFT position.
520
- Burning a Ramses CL NFT position won't claim rewards, so `getRewards` needs to be called before.
521
700
 
522
701
  3. Increase liquidity in the existing WETH/USDC pool
523
702
 
@@ -536,22 +715,54 @@ const result = await pool.increaseLiquidity(
536
715
  const tx = await pool.claimFees(Dapp.UNISWAPV3, tokenId);
537
716
  ```
538
717
 
539
- 4. Claim rewards (e.g. for Ramses CL)
718
+ #### VelodromeCL / AerodromeCL / PancakeCL
719
+
720
+ Velodrome CL, Aerodrome CL, and Pancake CL use the same SDK methods as Uniswap V3 — `approveUniswapV3Liquidity`, `addLiquidityUniswapV3`, `increaseLiquidity`, `decreaseLiquidity`, `claimFees` — just pass the matching `Dapp` enum (`Dapp.VELODROMECL`, `Dapp.AERODROMECL`, or `Dapp.PANCAKECL`). Use `minTick` / `maxTick` (not `minPrice` / `maxPrice`) and pass the pool's tick spacing as the last argument instead of a Uniswap fee tier.
721
+
722
+ 1. Mint a position (USDC/WETH on Aerodrome CL)
723
+
724
+ ```ts
725
+ await pool.approveSpender(AERODROME_POSITION_MANAGER, USDC_ADDRESS, ethers.constants.MaxUint256);
726
+ await pool.approveSpender(AERODROME_POSITION_MANAGER, WETH_ADDRESS, ethers.constants.MaxUint256);
727
+
728
+ await pool.addLiquidityUniswapV3(
729
+ Dapp.AERODROMECL,
730
+ USDC_ADDRESS,
731
+ WETH_ADDRESS,
732
+ usdcAmount,
733
+ wethAmount,
734
+ null, // minPrice (use ticks instead)
735
+ null, // maxPrice
736
+ -2, // minTick
737
+ 4, // maxTick
738
+ 1 // tick spacing
739
+ );
740
+ ```
741
+
742
+ `AERODROME_POSITION_MANAGER` is the chain's CL `nonfungiblePositionManager`; look it up via `nonfungiblePositionManagerAddress[network][Dapp.AERODROMECL]` from `src/config.ts`.
743
+
744
+ 2. Stake the CL position in a gauge (then unstake later)
540
745
 
541
746
  ```ts
542
- const tx = await pool.getRewards(Dapp.RAMSESCL, tokenId, [RAM_ADDRESS]);
747
+ await pool.approveSpenderNFT(GAUGE_ADDRESS, AERODROME_POSITION_MANAGER, tokenId);
748
+ await pool.stakeInGauge(Dapp.AERODROMECL, GAUGE_ADDRESS, tokenId);
749
+
750
+ // later
751
+ await pool.unstakeFromGauge(GAUGE_ADDRESS, tokenId);
543
752
  ```
544
753
 
545
- #### VelodromeV2 / Ramses / Aerodrome
754
+ `claimFees(Dapp.AERODROMECL, tokenId)` collects swap fees on an unstaked position and gauge rewards (e.g. AERO/VELO) on a staked position.
755
+
756
+ #### VelodromeV2 / Aerodrome
546
757
 
547
- For VelodromeV2 / Ramses / Aerodrome , we use `addLiquidityV2`, `stakeInGauge`, `unstakeFromGauge`, `removeLiquidityV2`, and `claimFees`.
758
+ For VelodromeV2 / Aerodrome, we use `addLiquidityV2`, `stakeInGauge`, `unstakeFromGauge`, `removeLiquidityV2`, and `claimFees`.
548
759
 
549
- Add liquidity of 100 USDC and 0.00043 WETH to USDC/WETH Ramses pool
550
- (for Velodrome just use Dapp.VELODROMEV2, for Aerodrome Dapp.AERODROME). see example in the [arrakis test](https://github.com/dhedge/dhedge-v2-sdk/blob/master/src/test/ramses.test.ts), [velodromeV2 test](https://github.com/dhedge/dhedge-v2-sdk/blob/master/src/test/velodromeV2.test.ts) and [aerdodrome test](https://github.com/dhedge/dhedge-v2-sdk/blob/master/src/test/aerdodrome.test.ts)
760
+ Add liquidity of 100 USDC and 0.00043 WETH to a USDC/WETH pool
761
+ (for Velodrome use `Dapp.VELODROMEV2`, for Aerodrome use `Dapp.AERODROME`). See examples in the [velodromeV2 test](https://github.com/dhedge/dhedge-v2-sdk/blob/master/src/test/velodromeV2.test.ts) and [aerodrome test](https://github.com/dhedge/dhedge-v2-sdk/blob/master/src/test/aerodrome.test.ts).
551
762
 
552
763
  ```ts
553
764
  const tx = await pool.addLiquidityV2(
554
- Dapp.RAMSES,
765
+ Dapp.VELODROMEV2,
555
766
  USDC_ADDRESS,
556
767
  WETH_ADDRESS,
557
768
  '10000000',
@@ -560,6 +771,56 @@ const tx = await pool.addLiquidityV2(
560
771
  )
561
772
  ```
562
773
 
774
+ #### Pendle
775
+
776
+ For Pendle Principal Tokens (PT), we use `pool.trade(Dapp.PENDLE, ...)` to swap an underlying token into a PT, swap a PT back into its underlying, or — once the market has expired — redeem a PT to its underlying via the SY exchange rate. The SDK auto-detects expired markets via the Pendle API and routes the call through the matured-exit path.
777
+
778
+ > **Note:** Only a limited set of PTs is supported. Each PT (and its underlying / SY) must be whitelisted in the dHEDGE asset handler before a vault can hold or trade it. Check the live asset registry (see [Looking up addresses](#looking-up-addresses)) for the current list — attempts to trade an unwhitelisted PT will revert.
779
+
780
+ 1. Swap underlying (e.g. wstETH) into a PT in an active market
781
+
782
+ ```ts
783
+ const wstEthBalance = await vault.utils.getBalance(WSTETH_ADDRESS, vault.address);
784
+ await vault.approve(Dapp.PENDLE, WSTETH_ADDRESS, ethers.constants.MaxUint256);
785
+ await vault.trade(
786
+ Dapp.PENDLE,
787
+ WSTETH_ADDRESS,
788
+ PT_WSTETH_ADDRESS,
789
+ wstEthBalance,
790
+ 1 // slippage %
791
+ );
792
+ ```
793
+
794
+ 2. Swap a PT back into its underlying before maturity
795
+
796
+ ```ts
797
+ const ptBalance = await vault.utils.getBalance(PT_WSTETH_ADDRESS, vault.address);
798
+ await vault.approve(Dapp.PENDLE, PT_WSTETH_ADDRESS, ethers.constants.MaxUint256);
799
+ await vault.trade(
800
+ Dapp.PENDLE,
801
+ PT_WSTETH_ADDRESS,
802
+ WSTETH_ADDRESS,
803
+ ptBalance,
804
+ 1
805
+ );
806
+ ```
807
+
808
+ 3. Redeem a matured PT to its underlying
809
+
810
+ After the market's `expiry`, the SDK detects the inactive market and uses the `exitPostExpToToken` path; the amount received is `ptAmount * 1e18 / SY.exchangeRate()`.
811
+
812
+ ```ts
813
+ const ptBalance = await vault.utils.getBalance(MATURED_PT_ADDRESS, vault.address);
814
+ await vault.approve(Dapp.PENDLE, MATURED_PT_ADDRESS, ethers.constants.MaxUint256);
815
+ await vault.trade(
816
+ Dapp.PENDLE,
817
+ MATURED_PT_ADDRESS,
818
+ UNDERLYING_ADDRESS,
819
+ ptBalance,
820
+ 1
821
+ );
822
+ ```
823
+
563
824
  <br>
564
825
 
565
826
  ### Lending/Borrowing Aave
@@ -571,25 +832,61 @@ For Aave, we use `lend`, `withdrawDeposit`, `borrow` and `repay`
571
832
  ##### 1. Deposit 1 USDC into Aave lending pool
572
833
 
573
834
  ```ts
574
- const tx = await pool.lend(Dapp.AAVE, USDC_TOKEN_ADDRESS, "1000000")
835
+ const tx = await pool.lend(Dapp.AAVE, USDC_TOKEN_ADDRESS, "1000000") // 1 USDC (6 decimals)
575
836
  ```
576
837
 
577
838
  ##### 2. Withdraw 1 USDC from Aave lending pool
578
839
 
579
840
  ```ts
580
- const tx = await pool.withdrawDeposit(Dapp.AAVE, USDC_TOKEN_ADDRESS, "1000000")
841
+ const tx = await pool.withdrawDeposit(Dapp.AAVE, USDC_TOKEN_ADDRESS, "1000000") // 1 USDC (6 decimals)
581
842
  ```
582
843
 
583
844
  ##### 3. Borrow 0.0001 WETH from Aave lending pool
584
845
 
585
846
  ```ts
586
- const tx = await pool.borrow(Dapp.AAVE, WETH_TOKEN_ADDRESS, "100000000000000");
847
+ const tx = await pool.borrow(Dapp.AAVE, WETH_TOKEN_ADDRESS, "100000000000000"); // 0.0001 WETH (18 decimals)
587
848
  ```
588
849
 
589
850
  ##### 4. Repay 0.0001 WETH to Aave lending pool
590
851
 
591
852
  ```ts
592
- const tx = await pool.repay(Dapp.AAVE, WETH_TOKEN_ADDRESS, "100000000000000");
853
+ const tx = await pool.repay(Dapp.AAVE, WETH_TOKEN_ADDRESS, "100000000000000"); // 0.0001 WETH (18 decimals)
854
+ ```
855
+
856
+ <br>
857
+
858
+ ### Lending CompoundV3 / Fluid
859
+
860
+ CompoundV3 markets (cTokens, e.g. `cWETHv3`, `cUSDCv3`) and Fluid markets (fTokens, e.g. `fWETH`, `fUSDC`) share the same SDK surface — `lendCompoundV3` to supply and `withdrawCompoundV3` to withdraw, with the market address as the first argument. CompoundV3 additionally exposes `harvestCompoundV3Rewards` to claim COMP rewards (Fluid has no rewards harvest path).
861
+
862
+ Look up market addresses in `dHEDGE Assets list.json` (see [Looking up addresses](#looking-up-addresses)). Compound V3 markets are `assetType: "28"` with `contractGuard: "CompoundV3CometContractGuard"`; Fluid markets are `assetType: "34"` with `contractGuard: "FluidTokenContractGuard"`.
863
+
864
+ 1. Approve the asset for the market
865
+
866
+ ```ts
867
+ await pool.approveSpender(MARKET_ADDRESS, ASSET_ADDRESS, ethers.constants.MaxUint256);
868
+ ```
869
+
870
+ 2. Supply
871
+
872
+ ```ts
873
+ // CompoundV3
874
+ await pool.lendCompoundV3(CWETH_V3_ADDRESS, WETH_ADDRESS, "1000000000000000000"); // 1 WETH (18 decimals)
875
+
876
+ // Fluid uses the same method, just a Fluid market address
877
+ await pool.lendCompoundV3(FWETH_ADDRESS, WETH_ADDRESS, "1000000000000000000");
878
+ ```
879
+
880
+ 3. Withdraw
881
+
882
+ ```ts
883
+ await pool.withdrawCompoundV3(MARKET_ADDRESS, ASSET_ADDRESS, AMOUNT);
884
+ ```
885
+
886
+ 4. (CompoundV3 only) Harvest COMP rewards
887
+
888
+ ```ts
889
+ await pool.harvestCompoundV3Rewards(CWETH_V3_ADDRESS);
593
890
  ```
594
891
 
595
892
  <br>
@@ -625,3 +922,56 @@ const tx = await pool.repay(Dapp.AAVE, WETH_TOKEN_ADDRESS, "100000000000000");
625
922
  ```ts
626
923
  await pool.cancelOrderViaFlatMoney();
627
924
  ```
925
+
926
+ <br>
927
+
928
+ ## Development
929
+
930
+ ### Setup
931
+
932
+ ```sh
933
+ # install
934
+ yarn install
935
+
936
+ # copy and edit env
937
+ cp .env.example .env
938
+ ```
939
+
940
+ `.env` recognised keys:
941
+
942
+ | Variable | Purpose |
943
+ | --- | --- |
944
+ | `PRIVATE_KEY` | Wallet used by tests; needs to be the manager (or trader) of `TEST_POOL[network]` for fork tests. |
945
+ | `<NETWORK>_URL` | RPC URL used to fork or run on-chain tests. One per network: `POLYGON_URL`, `OPTIMISM_URL`, `ARBITRUM_URL`, `BASE_URL`, `ETHEREUM_URL`, `PLASMA_URL`, `HYPERLIQUID_URL`. |
946
+ | `ONEINCH_API_KEY` | Required only for `Dapp.ONEINCH` calls. |
947
+
948
+ ### Build & lint
949
+
950
+ ```sh
951
+ yarn build # tsdx build
952
+ yarn lint # tsdx lint
953
+ ```
954
+
955
+ ### Fork a network
956
+
957
+ Each fork command spins up a local Hardhat node on a fixed port (the test wallet helper looks these ports up automatically when `onFork: true`):
958
+
959
+ | Network | Command | Local port |
960
+ | --- | --- | :---: |
961
+ | Polygon | `yarn fork:polygon` | 8542 |
962
+ | Optimism | `yarn fork:optimism` | 8544 |
963
+ | Arbitrum | `yarn fork:arbitrum` | 8540 |
964
+ | Base | `yarn fork:base` | 8546 |
965
+ | Ethereum | `yarn fork:ethereum` | 8547 |
966
+ | Plasma | `yarn fork:plasma` | 8548 |
967
+ | Hyperliquid | `yarn fork:hyperliquid` | 8549 |
968
+
969
+ ### Run tests
970
+
971
+ ```sh
972
+ yarn test # run everything
973
+ npx jest src/test/aave.test.ts # one file
974
+ npx jest -t "trades USDC into WETH" # by test name
975
+ ```
976
+
977
+ Most test files declare a network with `testingHelper({ network: Network.X, testingRun, onFork: true })`. The helper picks `http://127.0.0.1:<port>` when `onFork: true` (the matching `yarn fork:<network>` must be running) and `process.env.<NETWORK>_URL` when `onFork: false` — used for "on-chain" tests like `*.onchain.test.ts` that can't run on a fork (e.g. CowSwap solver settlement, Hyperliquid CoreWriter, Toros completeWithdrawal aggregator quotes).