@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
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
 
@@ -118,6 +165,53 @@ Important notes:
118
165
  - `loadPool(address)` assumes the address is a dHEDGE pool and resolves the manager contract automatically.
119
166
  - `loadPool(address, false)` can be used when you want to work with a non-dHEDGE contract that should execute directly from the signer.
120
167
 
168
+ #### Supported networks
169
+
170
+ 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`).
171
+
172
+ #### Looking up addresses
173
+
174
+ 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)**.
175
+
176
+ What each file gives you:
177
+
178
+ - **`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.
179
+ - **`dHEDGE Governance Contract Guards.csv`** — **active** contract guards: which protocols vaults are currently allowed to call (Aave, Pendle, Velodrome routers, Toros LimitOrderManager, etc.).
180
+ - **`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.
181
+ - **`dHEDGE Governance Asset Guards.csv`** — per-asset-type rules (which asset types vaults can hold).
182
+
183
+ Per-network direct links (raw URLs are agent-friendly — fetch them directly):
184
+
185
+ | Network | Folder | Assets | Active guards | Deprecated | Asset guards |
186
+ | --- | --- | --- | --- | --- | --- |
187
+ | 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) |
188
+ | 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) |
189
+ | 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) |
190
+ | 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) |
191
+ | 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) |
192
+ | 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) |
193
+
194
+ Router addresses (for `pool.approve(dapp, ...)`) live in `src/config.ts` → `routerAddress[network][dapp]`.
195
+
196
+ #### Common revert reasons
197
+
198
+ Two canonical references in [dhedge/V2-Public](https://github.com/dhedge/V2-Public/tree/master/readmes/errorCodes):
199
+
200
+ - [`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"]`.
201
+ - [`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.
202
+
203
+ For 4-byte selectors not in the file above, decode them with `cast 4byte 0x<selector>` (Foundry).
204
+
205
+ #### Reference: tests as live examples
206
+
207
+ 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.
208
+
209
+ 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.
210
+
211
+ Naming convention:
212
+ - `src/test/<protocol>.test.ts` — runs against a Hardhat fork.
213
+ - `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).
214
+
121
215
  #### Execution model
122
216
 
123
217
  By default, manager actions are executed through `pool.poolLogic.execTransaction(...)`.
@@ -129,8 +223,8 @@ const tx = await pool.trade(
129
223
  Dapp.ONEINCH,
130
224
  "USDC_TOKEN_ADDRESS",
131
225
  "WETH_TOKEN_ADDRESS",
132
- "1000000",
133
- 0.5,
226
+ "1000000", // 1 USDC (6 decimals)
227
+ 0.5, // slippage %
134
228
  null,
135
229
  {
136
230
  estimateGas: true,
@@ -141,19 +235,22 @@ const tx = await pool.trade(
141
235
 
142
236
  #### Simulation and tx data
143
237
 
144
- Most manager methods accept `sdkOptions` as the last argument.
238
+ Most manager methods accept `sdkOptions` as the last argument. The return shape depends on which flag you set:
239
+
240
+ - **No flag (default)** — sends the transaction and returns the underlying ethers `ContractTransaction` / `TransactionResponse`.
241
+ - **`true`** — shorthand for `{ estimateGas: true }`.
242
+ - **`{ estimateGas: true }`** — simulates and returns `{ gas, gasEstimationError, to, txData, minAmountOut }`. `gas` is `null` when `gasEstimationError` is set.
243
+ - **`{ onlyGetTxData: true }`** — returns `{ to, txData, minAmountOut }` (no `gas` / `gasEstimationError`) without sending or simulating.
145
244
 
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
245
+ > `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
246
 
150
247
  ```ts
151
248
  const result = await pool.trade(
152
249
  Dapp.ONEINCH,
153
250
  "USDC_TOKEN_ADDRESS",
154
251
  "WETH_TOKEN_ADDRESS",
155
- "1000000",
156
- 0.5,
252
+ "1000000", // 1 USDC (6 decimals)
253
+ 0.5, // slippage %
157
254
  null,
158
255
  { estimateGas: true }
159
256
  )
@@ -260,7 +357,7 @@ const tx = await vault.approveDeposit("USDC_TOKEN_ADDRESS", ethers.constants.Max
260
357
  Deposit 1 USDC into a vault
261
358
 
262
359
  ```ts
263
- const usdcDepositAmount = "100000"
360
+ const usdcDepositAmount = "1000000" // 1 USDC (6 decimals)
264
361
  const tx = await vault.deposit("USDC_TOKEN_ADDRESS", usdcDepositAmount);
265
362
  ```
266
363
 
@@ -277,11 +374,11 @@ const tx = await vault.withdraw(poolTokensWithdrawAmount);
277
374
 
278
375
  Before the vault can trade or stake an asset on an external protocol, the vault must approve that protocol router or staking contract.
279
376
 
280
- Approve unlimited amount of USDC to trade on Sushiswap
377
+ Approve unlimited amount of USDC to trade on KyberSwap
281
378
 
282
379
  ```ts
283
380
  const tx = await vault.approve(
284
- Dapp.SUSHISWAP,
381
+ Dapp.KYBERSWAP,
285
382
  "USDC_TOKEN_ADDRESS",
286
383
  ethers.constants.MaxInt256
287
384
  )
@@ -289,20 +386,23 @@ const tx = await vault.approve(
289
386
 
290
387
  Approval model summary:
291
388
 
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
389
+ | Method | From To | Spender resolution | When to use |
390
+ | --- | --- | --- | --- |
391
+ | `approveDeposit(asset, amount)` | user wallet → vault | the vault itself | Before `vault.deposit(asset, amount)` |
392
+ | `approve(dapp, asset, amount)` | vault protocol router | `routerAddress[network][dapp]` | Before `vault.trade`, `vault.lend`, `vault.borrow`, etc. |
393
+ | `approveStaking(dapp, asset, amount)` | vault → staking contract | `stakingAddress[network][dapp]` | Before `vault.stake(dapp, ...)` (V2-style farms) |
394
+ | `approveSpender(spender, asset, amount)` | vault → arbitrary spender | caller-supplied | Custom integrations, CL position managers, etc. |
395
+ | `approveSpenderNFT(spender, nftContract, tokenId)` | vault → arbitrary spender (ERC-721) | caller-supplied | Approving a single CL position NFT before `stakeInGauge` |
296
396
 
297
397
  #### 10. Trade vault assets
298
398
 
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)
399
+ 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
400
 
301
401
  ```ts
302
- const amountIn = "1000000"
402
+ const amountIn = "1000000" // 1 USDC (6 decimals)
303
403
  const slippage = 0.5
304
404
  const tx = await vault.trade(
305
- Dapp.SUSHISWAP,
405
+ Dapp.KYBERSWAP,
306
406
  "USDC_TOKEN_ADDRESS",
307
407
  "DAI_TOKEN_ADDRESS",
308
408
  amountIn,
@@ -310,6 +410,28 @@ const tx = await vault.trade(
310
410
  )
311
411
  ```
312
412
 
413
+ **CowSwap** behaves differently from the other aggregators:
414
+
415
+ - Approve the **CoW Vault Relayer** (`0xC92E8bdf79f0507f65a392b0ab4667716BFE0110`) via `approveSpender`, not via `approve(Dapp.COWSWAP, ...)`.
416
+ - 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.
417
+ - `estimateGas` and `onlyGetTxData` are not supported.
418
+
419
+ ```ts
420
+ await vault.approveSpender(
421
+ "0xC92E8bdf79f0507f65a392b0ab4667716BFE0110", // CoW Vault Relayer
422
+ "USDC_TOKEN_ADDRESS",
423
+ ethers.constants.MaxUint256
424
+ );
425
+
426
+ await vault.trade(
427
+ Dapp.COWSWAP,
428
+ "USDC_TOKEN_ADDRESS",
429
+ "WETH_TOKEN_ADDRESS",
430
+ "2000000",
431
+ 0.5
432
+ );
433
+ ```
434
+
313
435
  #### Toros mint flow
314
436
 
315
437
  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 +447,7 @@ const tx = await vault.trade(
325
447
  Dapp.TOROS,
326
448
  "USDC_TOKEN_ADDRESS",
327
449
  "TOROS_TOKEN_ADDRESS",
328
- "100000000",
450
+ "100000000", // 100 USDC (6 decimals)
329
451
  slippage
330
452
  )
331
453
  ```
@@ -357,7 +479,7 @@ const tx = await vault.trade(
357
479
  Dapp.TOROS,
358
480
  "TOROS_TOKEN_ADDRESS",
359
481
  "USDC_TOKEN_ADDRESS",
360
- "100000000",
482
+ "100000000", // amount of Toros vault tokens to redeem (Toros tokens are 18 decimals)
361
483
  slippage
362
484
  )
363
485
  ```
@@ -371,19 +493,69 @@ const tx = await vault.completeTorosWithdrawal(
371
493
  )
372
494
  ```
373
495
 
496
+ #### Toros limit orders
497
+
498
+ 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.
499
+
500
+ 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).
501
+
502
+ 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.
503
+
504
+ 1. Approve the Toros vault token for the limit-order manager (one-time)
505
+
506
+ ```ts
507
+ import { ethers } from "ethers";
508
+
509
+ await vault.approveTorosLimitOrder(
510
+ "TOROS_VAULT_ADDRESS",
511
+ ethers.constants.MaxUint256
512
+ );
513
+ ```
514
+
515
+ 2. Create an order — stop-loss at \$50,000 and take-profit at \$100,000 priced in WBTC
516
+
517
+ ```ts
518
+ await vault.createTorosLimitOrder(
519
+ "TOROS_VAULT_ADDRESS",
520
+ ethers.utils.parseEther("0.001"), // amount of vault tokens (18 decimals)
521
+ ethers.utils.parseEther("50000"), // stopLossPriceD18
522
+ ethers.utils.parseEther("100000"), // takeProfitPriceD18
523
+ "WBTC_TOKEN_ADDRESS" // pricingAsset (assetAddress from dHEDGE Assets list.json)
524
+ );
525
+ ```
526
+
527
+ 3. Read the active order, modify it, or delete it
528
+
529
+ ```ts
530
+ const order = await vault.getTorosLimitOrder(vault.address, "TOROS_VAULT_ADDRESS");
531
+ const exists = await vault.hasActiveTorosLimitOrder(vault.address, "TOROS_VAULT_ADDRESS");
532
+
533
+ await vault.modifyTorosLimitOrder(
534
+ "TOROS_VAULT_ADDRESS",
535
+ order.amount,
536
+ order.stopLossPriceD18.mul(95).div(100), // tighten stop by 5%
537
+ order.takeProfitPriceD18,
538
+ order.pricingAsset
539
+ );
540
+
541
+ await vault.deleteTorosLimitOrder("TOROS_VAULT_ADDRESS");
542
+ ```
543
+
374
544
  ### Liquidity
375
545
 
376
546
  ---
377
547
 
378
548
  #### Uniswap-v2 style
379
549
 
550
+ > **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.
551
+
380
552
  For Uniswap-v2 like protocols, such as sushiswap, we use `addLiquidity`, `removeLiquidity`, `stake`, and `unstake`, and `harvestRewards`
381
553
 
382
554
  1. Add USDC/DAI into a Sushiswap liquidity pool
383
555
 
384
556
  ```ts
385
- const amountUsdc = "1000000"
386
- const amountDai = "997085"
557
+ const amountUsdc = "1000000" // 1 USDC (6 decimals)
558
+ const amountDai = "997085000000000000" // ~0.997 DAI (18 decimals)
387
559
  const tx = await pool.addLiquidity(
388
560
  Dapp.SUSHISWAP,
389
561
  "USDC_TOKEN_ADDRESS",
@@ -396,7 +568,7 @@ const tx = await pool.addLiquidity(
396
568
  2. Remove USDC/DAI worth of 1 Sushiswap LP from the liquidity pool
397
569
 
398
570
  ```ts
399
- const amountSlpUsdcDai = "1000000000000000000"
571
+ const amountSlpUsdcDai = "1000000000000000000" // 1 LP token (18 decimals)
400
572
  const tx = await pool.removeLiquidity(
401
573
  Dapp.SUSHISWAP,
402
574
  "USDC_TOKEN_ADDRESS",
@@ -405,7 +577,7 @@ const tx = await pool.removeLiquidity(
405
577
  )
406
578
  ```
407
579
 
408
- 3. Approve unlimited amound of SLP USDC-DAI token for staking on Sushiswap
580
+ 3. Approve unlimited amount of SLP USDC-DAI token for staking on Sushiswap
409
581
 
410
582
  ```ts
411
583
  const tx = await pool.approveStaking(
@@ -418,7 +590,7 @@ const tx = await pool.approveStaking(
418
590
  4. Stake 1 Sushiswap LP USDC/DAI token
419
591
 
420
592
  ```ts
421
- const amountSlpUsdcDai = "1000000000000000000"
593
+ const amountSlpUsdcDai = "1000000000000000000" // 1 LP token (18 decimals)
422
594
  const tx = await pool.stake(
423
595
  Dapp.SUSHISWAP,
424
596
  "SLP_USDC_DAI_TOKEN_ADDRESS",
@@ -429,7 +601,7 @@ const tx = await pool.stake(
429
601
  5. Unstake 1 Sushiswap LP USDC/DAI token
430
602
 
431
603
  ```ts
432
- const amountSlpUsdcDai = "1000000000000000000"
604
+ const amountSlpUsdcDai = "1000000000000000000" // 1 LP token (18 decimals)
433
605
  const tx = await pool.unstake(
434
606
  Dapp.SUSHISWAP,
435
607
  "SLP_USDC_DAI_TOKEN_ADDRESS",
@@ -455,7 +627,11 @@ For Balancer, we use `joinBalancerPool`, `exitBalancerPool`, and `harvestBalance
455
627
  ```ts
456
628
  const balancerPoolId = "0x03cd191f589d12b0582a99808cf19851e468e6b500010000000000000000000a"
457
629
  const assets = [WBTC_TOKEN_ADDRESS, USDC_TOKEN_ADDRESS, WETH_TOKEN_ADDRESS];
458
- const amounts = ["2000", "1000000", "200000000000000"];
630
+ const amounts = [
631
+ "2000", // 0.00002 WBTC (8 decimals)
632
+ "1000000", // 1 USDC (6 decimals)
633
+ "200000000000000" // 0.0002 WETH (18 decimals)
634
+ ];
459
635
  const tx = await pool.joinBalancerPool(balancerPoolId, assets, amounts)
460
636
  ```
461
637
 
@@ -493,14 +669,15 @@ await pool.approveUniswapV3Liquidity(
493
669
  ethers.constants.MaxInt256
494
670
  );
495
671
  const tx = await pool.addLiquidityUniswapV3(
496
- WETH_ADDRESS
672
+ Dapp.UNISWAPV3,
673
+ WETH_ADDRESS,
497
674
  USDC_ADDRESS,
498
675
  '430000000000000', // wethBalance
499
- '100000000', // usdcBalance
500
- 2000,
501
- 3000,
502
- null,
503
- null,
676
+ '100000000', // usdcBalance
677
+ 2000, // minPrice
678
+ 3000, // maxPrice
679
+ null, // minTick (use null when providing prices)
680
+ null, // maxTick (use null when providing prices)
504
681
  FeeAmount.MEDIUM,
505
682
  )
506
683
  ```
@@ -512,12 +689,11 @@ tokenId = await nonfungiblePositionManager.tokenOfOwnerByIndex(pool.address,0).t
512
689
  const tx = await pool.decreaseLiquidity(
513
690
  Dapp.UNISWAPV3,
514
691
  tokenId,
515
- 50 // precent
692
+ 50 // percent
516
693
  );
517
694
  ```
518
695
 
519
696
  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
697
 
522
698
  3. Increase liquidity in the existing WETH/USDC pool
523
699
 
@@ -536,22 +712,54 @@ const result = await pool.increaseLiquidity(
536
712
  const tx = await pool.claimFees(Dapp.UNISWAPV3, tokenId);
537
713
  ```
538
714
 
539
- 4. Claim rewards (e.g. for Ramses CL)
715
+ #### VelodromeCL / AerodromeCL / PancakeCL
716
+
717
+ 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.
718
+
719
+ 1. Mint a position (USDC/WETH on Aerodrome CL)
720
+
721
+ ```ts
722
+ await pool.approveSpender(AERODROME_POSITION_MANAGER, USDC_ADDRESS, ethers.constants.MaxUint256);
723
+ await pool.approveSpender(AERODROME_POSITION_MANAGER, WETH_ADDRESS, ethers.constants.MaxUint256);
724
+
725
+ await pool.addLiquidityUniswapV3(
726
+ Dapp.AERODROMECL,
727
+ USDC_ADDRESS,
728
+ WETH_ADDRESS,
729
+ usdcAmount,
730
+ wethAmount,
731
+ null, // minPrice (use ticks instead)
732
+ null, // maxPrice
733
+ -2, // minTick
734
+ 4, // maxTick
735
+ 1 // tick spacing
736
+ );
737
+ ```
738
+
739
+ `AERODROME_POSITION_MANAGER` is the chain's CL `nonfungiblePositionManager`; look it up via `nonfungiblePositionManagerAddress[network][Dapp.AERODROMECL]` from `src/config.ts`.
740
+
741
+ 2. Stake the CL position in a gauge (then unstake later)
540
742
 
541
743
  ```ts
542
- const tx = await pool.getRewards(Dapp.RAMSESCL, tokenId, [RAM_ADDRESS]);
744
+ await pool.approveSpenderNFT(GAUGE_ADDRESS, AERODROME_POSITION_MANAGER, tokenId);
745
+ await pool.stakeInGauge(Dapp.AERODROMECL, GAUGE_ADDRESS, tokenId);
746
+
747
+ // later
748
+ await pool.unstakeFromGauge(GAUGE_ADDRESS, tokenId);
543
749
  ```
544
750
 
545
- #### VelodromeV2 / Ramses / Aerodrome
751
+ `claimFees(Dapp.AERODROMECL, tokenId)` collects swap fees on an unstaked position and gauge rewards (e.g. AERO/VELO) on a staked position.
752
+
753
+ #### VelodromeV2 / Aerodrome
546
754
 
547
- For VelodromeV2 / Ramses / Aerodrome , we use `addLiquidityV2`, `stakeInGauge`, `unstakeFromGauge`, `removeLiquidityV2`, and `claimFees`.
755
+ For VelodromeV2 / Aerodrome, we use `addLiquidityV2`, `stakeInGauge`, `unstakeFromGauge`, `removeLiquidityV2`, and `claimFees`.
548
756
 
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)
757
+ Add liquidity of 100 USDC and 0.00043 WETH to a USDC/WETH pool
758
+ (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
759
 
552
760
  ```ts
553
761
  const tx = await pool.addLiquidityV2(
554
- Dapp.RAMSES,
762
+ Dapp.VELODROMEV2,
555
763
  USDC_ADDRESS,
556
764
  WETH_ADDRESS,
557
765
  '10000000',
@@ -560,6 +768,56 @@ const tx = await pool.addLiquidityV2(
560
768
  )
561
769
  ```
562
770
 
771
+ #### Pendle
772
+
773
+ 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.
774
+
775
+ > **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.
776
+
777
+ 1. Swap underlying (e.g. wstETH) into a PT in an active market
778
+
779
+ ```ts
780
+ const wstEthBalance = await vault.utils.getBalance(WSTETH_ADDRESS, vault.address);
781
+ await vault.approve(Dapp.PENDLE, WSTETH_ADDRESS, ethers.constants.MaxUint256);
782
+ await vault.trade(
783
+ Dapp.PENDLE,
784
+ WSTETH_ADDRESS,
785
+ PT_WSTETH_ADDRESS,
786
+ wstEthBalance,
787
+ 1 // slippage %
788
+ );
789
+ ```
790
+
791
+ 2. Swap a PT back into its underlying before maturity
792
+
793
+ ```ts
794
+ const ptBalance = await vault.utils.getBalance(PT_WSTETH_ADDRESS, vault.address);
795
+ await vault.approve(Dapp.PENDLE, PT_WSTETH_ADDRESS, ethers.constants.MaxUint256);
796
+ await vault.trade(
797
+ Dapp.PENDLE,
798
+ PT_WSTETH_ADDRESS,
799
+ WSTETH_ADDRESS,
800
+ ptBalance,
801
+ 1
802
+ );
803
+ ```
804
+
805
+ 3. Redeem a matured PT to its underlying
806
+
807
+ After the market's `expiry`, the SDK detects the inactive market and uses the `exitPostExpToToken` path; the amount received is `ptAmount * 1e18 / SY.exchangeRate()`.
808
+
809
+ ```ts
810
+ const ptBalance = await vault.utils.getBalance(MATURED_PT_ADDRESS, vault.address);
811
+ await vault.approve(Dapp.PENDLE, MATURED_PT_ADDRESS, ethers.constants.MaxUint256);
812
+ await vault.trade(
813
+ Dapp.PENDLE,
814
+ MATURED_PT_ADDRESS,
815
+ UNDERLYING_ADDRESS,
816
+ ptBalance,
817
+ 1
818
+ );
819
+ ```
820
+
563
821
  <br>
564
822
 
565
823
  ### Lending/Borrowing Aave
@@ -571,25 +829,61 @@ For Aave, we use `lend`, `withdrawDeposit`, `borrow` and `repay`
571
829
  ##### 1. Deposit 1 USDC into Aave lending pool
572
830
 
573
831
  ```ts
574
- const tx = await pool.lend(Dapp.AAVE, USDC_TOKEN_ADDRESS, "1000000")
832
+ const tx = await pool.lend(Dapp.AAVE, USDC_TOKEN_ADDRESS, "1000000") // 1 USDC (6 decimals)
575
833
  ```
576
834
 
577
835
  ##### 2. Withdraw 1 USDC from Aave lending pool
578
836
 
579
837
  ```ts
580
- const tx = await pool.withdrawDeposit(Dapp.AAVE, USDC_TOKEN_ADDRESS, "1000000")
838
+ const tx = await pool.withdrawDeposit(Dapp.AAVE, USDC_TOKEN_ADDRESS, "1000000") // 1 USDC (6 decimals)
581
839
  ```
582
840
 
583
841
  ##### 3. Borrow 0.0001 WETH from Aave lending pool
584
842
 
585
843
  ```ts
586
- const tx = await pool.borrow(Dapp.AAVE, WETH_TOKEN_ADDRESS, "100000000000000");
844
+ const tx = await pool.borrow(Dapp.AAVE, WETH_TOKEN_ADDRESS, "100000000000000"); // 0.0001 WETH (18 decimals)
587
845
  ```
588
846
 
589
847
  ##### 4. Repay 0.0001 WETH to Aave lending pool
590
848
 
591
849
  ```ts
592
- const tx = await pool.repay(Dapp.AAVE, WETH_TOKEN_ADDRESS, "100000000000000");
850
+ const tx = await pool.repay(Dapp.AAVE, WETH_TOKEN_ADDRESS, "100000000000000"); // 0.0001 WETH (18 decimals)
851
+ ```
852
+
853
+ <br>
854
+
855
+ ### Lending CompoundV3 / Fluid
856
+
857
+ 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).
858
+
859
+ 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"`.
860
+
861
+ 1. Approve the asset for the market
862
+
863
+ ```ts
864
+ await pool.approveSpender(MARKET_ADDRESS, ASSET_ADDRESS, ethers.constants.MaxUint256);
865
+ ```
866
+
867
+ 2. Supply
868
+
869
+ ```ts
870
+ // CompoundV3
871
+ await pool.lendCompoundV3(CWETH_V3_ADDRESS, WETH_ADDRESS, "1000000000000000000"); // 1 WETH (18 decimals)
872
+
873
+ // Fluid uses the same method, just a Fluid market address
874
+ await pool.lendCompoundV3(FWETH_ADDRESS, WETH_ADDRESS, "1000000000000000000");
875
+ ```
876
+
877
+ 3. Withdraw
878
+
879
+ ```ts
880
+ await pool.withdrawCompoundV3(MARKET_ADDRESS, ASSET_ADDRESS, AMOUNT);
881
+ ```
882
+
883
+ 4. (CompoundV3 only) Harvest COMP rewards
884
+
885
+ ```ts
886
+ await pool.harvestCompoundV3Rewards(CWETH_V3_ADDRESS);
593
887
  ```
594
888
 
595
889
  <br>
@@ -625,3 +919,56 @@ const tx = await pool.repay(Dapp.AAVE, WETH_TOKEN_ADDRESS, "100000000000000");
625
919
  ```ts
626
920
  await pool.cancelOrderViaFlatMoney();
627
921
  ```
922
+
923
+ <br>
924
+
925
+ ## Development
926
+
927
+ ### Setup
928
+
929
+ ```sh
930
+ # install
931
+ yarn install
932
+
933
+ # copy and edit env
934
+ cp .env.example .env
935
+ ```
936
+
937
+ `.env` recognised keys:
938
+
939
+ | Variable | Purpose |
940
+ | --- | --- |
941
+ | `PRIVATE_KEY` | Wallet used by tests; needs to be the manager (or trader) of `TEST_POOL[network]` for fork tests. |
942
+ | `<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`. |
943
+ | `ONEINCH_API_KEY` | Required only for `Dapp.ONEINCH` calls. |
944
+
945
+ ### Build & lint
946
+
947
+ ```sh
948
+ yarn build # tsdx build
949
+ yarn lint # tsdx lint
950
+ ```
951
+
952
+ ### Fork a network
953
+
954
+ 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`):
955
+
956
+ | Network | Command | Local port |
957
+ | --- | --- | :---: |
958
+ | Polygon | `yarn fork:polygon` | 8542 |
959
+ | Optimism | `yarn fork:optimism` | 8544 |
960
+ | Arbitrum | `yarn fork:arbitrum` | 8540 |
961
+ | Base | `yarn fork:base` | 8546 |
962
+ | Ethereum | `yarn fork:ethereum` | 8547 |
963
+ | Plasma | `yarn fork:plasma` | 8548 |
964
+ | Hyperliquid | `yarn fork:hyperliquid` | 8549 |
965
+
966
+ ### Run tests
967
+
968
+ ```sh
969
+ yarn test # run everything
970
+ npx jest src/test/aave.test.ts # one file
971
+ npx jest -t "trades USDC into WETH" # by test name
972
+ ```
973
+
974
+ 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).