curvance 4.0.4 → 4.1.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 (125) hide show
  1. package/README.md +595 -59
  2. package/dist/chains/arb-sepolia.json +44 -0
  3. package/dist/chains/arbitrum.d.ts.map +1 -1
  4. package/dist/chains/arbitrum.js +4 -2
  5. package/dist/chains/arbitrum.js.map +1 -1
  6. package/dist/chains/index.d.ts +4 -0
  7. package/dist/chains/index.d.ts.map +1 -1
  8. package/dist/chains/index.js +15 -0
  9. package/dist/chains/index.js.map +1 -1
  10. package/dist/chains/monad-mainnet.json +26 -1
  11. package/dist/chains/monad.d.ts.map +1 -1
  12. package/dist/chains/monad.js +4 -2
  13. package/dist/chains/monad.js.map +1 -1
  14. package/dist/chains/rpc.d.ts +57 -0
  15. package/dist/chains/rpc.d.ts.map +1 -0
  16. package/dist/chains/rpc.js +47 -0
  17. package/dist/chains/rpc.js.map +1 -0
  18. package/dist/classes/Api.d.ts +4 -3
  19. package/dist/classes/Api.d.ts.map +1 -1
  20. package/dist/classes/Api.js +7 -7
  21. package/dist/classes/Api.js.map +1 -1
  22. package/dist/classes/BorrowableCToken.d.ts +3 -2
  23. package/dist/classes/BorrowableCToken.d.ts.map +1 -1
  24. package/dist/classes/BorrowableCToken.js +6 -5
  25. package/dist/classes/BorrowableCToken.js.map +1 -1
  26. package/dist/classes/CToken.d.ts +11 -3
  27. package/dist/classes/CToken.d.ts.map +1 -1
  28. package/dist/classes/CToken.js +85 -79
  29. package/dist/classes/CToken.js.map +1 -1
  30. package/dist/classes/Calldata.d.ts +2 -2
  31. package/dist/classes/Calldata.d.ts.map +1 -1
  32. package/dist/classes/Calldata.js +2 -2
  33. package/dist/classes/Calldata.js.map +1 -1
  34. package/dist/classes/DexAggregators/IDexAgg.d.ts +2 -2
  35. package/dist/classes/DexAggregators/IDexAgg.d.ts.map +1 -1
  36. package/dist/classes/DexAggregators/Kuru.d.ts +2 -2
  37. package/dist/classes/DexAggregators/Kuru.d.ts.map +1 -1
  38. package/dist/classes/DexAggregators/Kuru.js +3 -4
  39. package/dist/classes/DexAggregators/Kuru.js.map +1 -1
  40. package/dist/classes/DexAggregators/KuruMainnet.d.ts +1 -0
  41. package/dist/classes/DexAggregators/KuruMainnet.d.ts.map +1 -0
  42. package/dist/classes/DexAggregators/KuruMainnet.js +228 -0
  43. package/dist/classes/DexAggregators/KuruMainnet.js.map +1 -0
  44. package/dist/classes/DexAggregators/KyberSwap.d.ts +2 -2
  45. package/dist/classes/DexAggregators/KyberSwap.d.ts.map +1 -1
  46. package/dist/classes/DexAggregators/KyberSwap.js +10 -8
  47. package/dist/classes/DexAggregators/KyberSwap.js.map +1 -1
  48. package/dist/classes/DexAggregators/MultiDexAgg.d.ts +2 -2
  49. package/dist/classes/DexAggregators/MultiDexAgg.d.ts.map +1 -1
  50. package/dist/classes/DexAggregators/MultiDexAgg.js +3 -3
  51. package/dist/classes/DexAggregators/MultiDexAgg.js.map +1 -1
  52. package/dist/classes/ERC20.d.ts +5 -3
  53. package/dist/classes/ERC20.d.ts.map +1 -1
  54. package/dist/classes/ERC20.js +20 -14
  55. package/dist/classes/ERC20.js.map +1 -1
  56. package/dist/classes/ERC4626.d.ts.map +1 -1
  57. package/dist/classes/ERC4626.js +3 -1
  58. package/dist/classes/ERC4626.js.map +1 -1
  59. package/dist/classes/Kuru.d.ts +59 -0
  60. package/dist/classes/Kuru.d.ts.map +1 -0
  61. package/dist/classes/Kuru.js +167 -0
  62. package/dist/classes/Kuru.js.map +1 -0
  63. package/dist/classes/KuruMainnet.d.ts +59 -0
  64. package/dist/classes/KuruMainnet.d.ts.map +1 -0
  65. package/dist/classes/KuruMainnet.js +167 -0
  66. package/dist/classes/KuruMainnet.js.map +1 -0
  67. package/dist/classes/Market.d.ts +13 -4
  68. package/dist/classes/Market.d.ts.map +1 -1
  69. package/dist/classes/Market.js +86 -28
  70. package/dist/classes/Market.js.map +1 -1
  71. package/dist/classes/NativeToken.d.ts +6 -3
  72. package/dist/classes/NativeToken.d.ts.map +1 -1
  73. package/dist/classes/NativeToken.js +11 -16
  74. package/dist/classes/NativeToken.js.map +1 -1
  75. package/dist/classes/OptimizerReader.d.ts +3 -3
  76. package/dist/classes/OptimizerReader.d.ts.map +1 -1
  77. package/dist/classes/OptimizerReader.js +1 -1
  78. package/dist/classes/OptimizerReader.js.map +1 -1
  79. package/dist/classes/OracleManager.d.ts +3 -3
  80. package/dist/classes/OracleManager.d.ts.map +1 -1
  81. package/dist/classes/OracleManager.js +1 -1
  82. package/dist/classes/OracleManager.js.map +1 -1
  83. package/dist/classes/PositionManager.d.ts +2 -2
  84. package/dist/classes/PositionManager.d.ts.map +1 -1
  85. package/dist/classes/PositionManager.js +4 -4
  86. package/dist/classes/PositionManager.js.map +1 -1
  87. package/dist/classes/ProtocolReader.d.ts +18 -4
  88. package/dist/classes/ProtocolReader.d.ts.map +1 -1
  89. package/dist/classes/ProtocolReader.js +177 -55
  90. package/dist/classes/ProtocolReader.js.map +1 -1
  91. package/dist/classes/Redstone.d.ts.map +1 -1
  92. package/dist/classes/Redstone.js +1 -2
  93. package/dist/classes/Redstone.js.map +1 -1
  94. package/dist/classes/Zapper.d.ts +4 -2
  95. package/dist/classes/Zapper.d.ts.map +1 -1
  96. package/dist/classes/Zapper.js +14 -13
  97. package/dist/classes/Zapper.js.map +1 -1
  98. package/dist/classes/index.d.ts +1 -1
  99. package/dist/classes/index.d.ts.map +1 -1
  100. package/dist/classes/index.js +6 -1
  101. package/dist/classes/index.js.map +1 -1
  102. package/dist/contracts/monad-mainnet.json +1 -1
  103. package/dist/helpers.d.ts +3 -1
  104. package/dist/helpers.d.ts.map +1 -1
  105. package/dist/helpers.js +34 -4
  106. package/dist/helpers.js.map +1 -1
  107. package/dist/integrations/snapshot.d.ts.map +1 -1
  108. package/dist/integrations/snapshot.js +4 -18
  109. package/dist/integrations/snapshot.js.map +1 -1
  110. package/dist/retry-provider.d.ts +81 -6
  111. package/dist/retry-provider.d.ts.map +1 -1
  112. package/dist/retry-provider.js +489 -35
  113. package/dist/retry-provider.js.map +1 -1
  114. package/dist/setup.d.ts +14 -3
  115. package/dist/setup.d.ts.map +1 -1
  116. package/dist/setup.js +56 -20
  117. package/dist/setup.js.map +1 -1
  118. package/dist/snapshot.d.ts +53 -0
  119. package/dist/snapshot.d.ts.map +1 -0
  120. package/dist/snapshot.js +103 -0
  121. package/dist/snapshot.js.map +1 -0
  122. package/dist/types.d.ts +2 -1
  123. package/dist/types.d.ts.map +1 -1
  124. package/dist/types.js.map +1 -1
  125. package/package.json +3 -1
package/README.md CHANGED
@@ -1,28 +1,8 @@
1
1
  <p style="text-align: center;width:100%">
2
- <img src="https://pbs.twimg.com/profile_banners/1445781144125857796/1752160592" alt="Curvance"/>
2
+ <img src="https://pbs.twimg.com/profile_banners/1445781144125857796/1773687595/1500x500" alt="Curvance"/>
3
3
  </p>
4
4
 
5
- Features
6
- - **Efficient RPC Usage:** Preloads all market data with minimal calls.
7
- - **Typed Contracts:** Uses ethers.js for safe, typed blockchain interactions.
8
- - **Price Feeds:** Integrates Redstone for on-chain price updates.
9
- - **Decimal Support:** Handles BigInt and floating-point math with decimal.js.
10
- - **Flexible Providers:** Works with multiple providers:
11
- - `ethers.Wallet` - For CLI/Local wallet connections
12
- - `ethers.JsonRpcSigner` - For browser interactions
13
- - `ethers.JsonRpcProvider` - For a user configured RPC
14
- - `null` - We setup a JsonRpcProvider if we configured one for you
15
- - **Property conversions:** For example getting a users asset balance can optionally be returned in USD or token amount
16
- - **Contract addresses:** Use this package to pull in curvance contracts & have the latest contract addresses (especially useful on testnet)
17
-
18
- Dependencies:
19
- - [Redstone](https://www.npmjs.com/package/@redstone-finance/sdk): Used to attach price updates in a multicall for some actions.
20
- - [Decimals](https://www.npmjs.com/package/decimal.js): Any floating point path being done with BigInt is done with Decimals.
21
- - [ethers.js](https://www.npmjs.com/package/ethers): All signers passed into the protocol are using ether.js typed signers.
22
-
23
- Notes:
24
- - All values are returned in either BigInt or [Decimals](https://www.npmjs.com/package/decimal.js)
25
- - We use [alchemy](https://dashboard.alchemy.com/apps) chain prefixing for exaple: `eth-mainnet` or `arb-sepolia` to represents chains
5
+ A TypeScript SDK for interacting with the Curvance protocol. Built on ethers v6 with a bulk-loaded cache model — `setupChain()` preloads all market data in 1–3 RPC calls, and all subsequent reads are synchronous from cache.
26
6
 
27
7
  ## ❯ Install
28
8
 
@@ -30,54 +10,610 @@ Notes:
30
10
  $ npm install --save curvance
31
11
  ```
32
12
 
33
- ## ❯ Usage
13
+ ## ❯ Supported Chains
34
14
 
35
- ### Grab general information
36
- This is very RPC efficient as it uses 1-3 RPC call's to setup all the data you need. This is the main way to use the SDK as ALL the data is pre-setup for you. All you need to do is traverse the markets.
37
- ```js
38
- const { markets, reader, faucet } = await setupChain("monad-testnet", wallet);
15
+ Chain identifiers use Alchemy-style prefixes:
16
+
17
+ | Chain | Identifier |
18
+ |---|---|
19
+ | Monad Mainnet | `monad-mainnet` |
20
+ | Arbitrum Sepolia | `arb-sepolia` |
21
+
22
+ ## ❯ Quick Start
23
+
24
+ ```ts
25
+ import { setupChain } from "curvance";
26
+ import { ethers } from "ethers";
27
+
28
+ const wallet = new ethers.Wallet(privateKey, provider);
29
+ const { markets, reader, dexAgg, global_milestone } = await setupChain("monad-mainnet", wallet);
39
30
  ```
40
31
 
41
- You can then explore the data pretty easily like so:
42
- ```js
43
- let count = 0;
44
- console.log(`Market summaries in USD:`);
45
- for(const market of markets) {
46
- console.log(`[${count}] tvl: ${market.tvl.toFixed(18)} | totalDebt: ${market.totalDebt.toFixed(18)} | totalCollateral: ${market.totalCollateral.toFixed(18)}`);
47
- for(const token of market.tokens) {
48
- console.log(`\tToken: ${token.symbol} | Price: ${token.getPrice()} | Amount: ${token.getTvl(false)}`);
32
+ `setupChain` signature:
33
+
34
+ ```ts
35
+ setupChain(
36
+ chain: ChainRpcPrefix,
37
+ provider: curvance_provider | null = null, // null → SDK constructs a JsonRpcProvider
38
+ approval_protection: boolean = false, // revoke-before-approve pattern
39
+ api_url: string = "https://api.curvance.com",
40
+ options: { feePolicy?: FeePolicy } = {}
41
+ ): Promise<{
42
+ markets: Market[],
43
+ reader: ProtocolReader,
44
+ dexAgg: IDexAgg,
45
+ global_milestone: MilestoneResponse | null
46
+ }>
47
+ ```
48
+
49
+ ### Explore markets
50
+
51
+ ```ts
52
+ for (const market of markets) {
53
+ console.log(`${market.name} | tvl: ${market.tvl} | debt: ${market.totalDebt}`);
54
+ for (const token of market.tokens) {
55
+ console.log(` ${token.symbol} | price: ${token.getPrice()} | apy: ${token.getApy(true)}%`);
49
56
  }
50
- count++;
51
57
  }
52
58
  ```
53
59
 
54
- ### Grab individaul classes
55
- ```js
56
- const test_1 = new ERC20(signer, `0x123`);
57
- await test_1.approve(someGuy, BigInt(50e18));
60
+ ## Providers
61
+
62
+ `curvance_provider` accepts any ethers v6 provider or signer. All providers are automatically wrapped with retry logic (exponential backoff for rate limits and 5xx errors).
63
+
64
+ | Type | Use case |
65
+ |---|---|
66
+ | `ethers.Wallet` | CLI / server-side with private key |
67
+ | `ethers.JsonRpcSigner` | Browser wallet (MetaMask, etc.) |
68
+ | `ethers.JsonRpcProvider` | Read-only or custom RPC |
69
+ | `null` | SDK constructs a provider from chain config |
70
+
71
+ `curvance_signer` = `JsonRpcSigner | Wallet` — required for write operations (deposit, borrow, etc.)
72
+
73
+ ## ❯ Markets
74
+
75
+ `Market` is the top-level container. Each market groups related collateral and borrow tokens and tracks the user's aggregate position.
76
+
77
+ ### Market properties
78
+
79
+ ```ts
80
+ market.name // market name
81
+ market.address // market contract address
82
+ market.tvl // total value locked (USD, Decimal)
83
+ market.totalDebt // total outstanding debt (USD, Decimal)
84
+ market.totalCollateral // total posted collateral (USD, Decimal)
85
+ market.cooldownLength // hold period between actions (20 min)
86
+ market.hasBorrowing() // whether this market supports borrowing
87
+ market.highestApy() // best supply APY across all tokens
88
+ market.ltv // LTV range {min, max} or single value
89
+ ```
90
+
91
+ ### User position (all in USD as `Decimal`)
92
+
93
+ ```ts
94
+ market.userDeposits // total deposits
95
+ market.userDebt // total outstanding debt
96
+ market.userMaxDebt // maximum allowable debt
97
+ market.userRemainingCredit // available borrow capacity (with 0.1% buffer)
98
+ market.userCollateral // posted collateral (in shares)
99
+ market.positionHealth // health factor — null means infinite (no debt)
100
+ market.userNet // deposits - debt
101
+ ```
102
+
103
+ ### Rate tracking
104
+
105
+ ```ts
106
+ // rateType: 'day' | 'week' | 'month' | 'year'
107
+ market.getUserDepositsChange('week') // projected earnings
108
+ market.getUserDebtChange('week') // projected interest cost
109
+ market.getUserNetChange('week') // net projected change
110
+ ```
111
+
112
+ ### Data refresh
113
+
114
+ ```ts
115
+ await market.reloadMarketData() // refresh rates, prices, utilization
116
+ await market.reloadUserData(account) // refresh user balances and position
117
+ ```
118
+
119
+ ## ❯ Tokens (CToken / BorrowableCToken)
120
+
121
+ Tokens within a market are either `CToken` (collateral/supply side) or `BorrowableCToken` (extends `CToken` with borrow/repay). Access them via `market.tokens` or `market.getBorrowableCTokens()`.
122
+
123
+ ### Token metadata
124
+
125
+ ```ts
126
+ token.symbol
127
+ token.name
128
+ token.decimals
129
+ token.asset // underlying ERC20 address
130
+ token.isBorrowable // whether this token can be borrowed
131
+ token.isVault // whether underlying is an ERC4626 vault
132
+ token.isNativeVault // native token vault (e.g. shMON)
133
+ token.canZap // supports zap deposits
134
+ token.canLeverage // supports leverage
135
+ token.maxLeverage // max allowed leverage (Decimal)
136
+ ```
137
+
138
+ ### Market state
139
+
140
+ ```ts
141
+ token.exchangeRate // current share-to-asset rate
142
+ token.totalAssets // total assets held (bigint)
143
+ token.totalSupply // total shares outstanding (bigint)
144
+ token.borrowPaused
145
+ token.collateralizationPaused
146
+ token.mintPaused
147
+ ```
148
+
149
+ ### Prices & conversions
150
+
151
+ ```ts
152
+ token.getPrice() // asset price (USD, Decimal)
153
+ token.getPrice(true) // share price
154
+ token.convertTokensToUsd(amount) // TokenInput → USD
155
+ token.convertUsdToTokens(usd) // USD → TokenInput
156
+ token.convertTokenInputToShares(amount) // user input → shares
157
+ token.virtualConvertToAssets(shares) // shares → assets (cached, no RPC)
158
+ token.virtualConvertToShares(assets) // assets → shares (cached, no RPC)
159
+ ```
160
+
161
+ ### User balances
162
+
163
+ ```ts
164
+ token.getUserShareBalance(inUSD) // cToken balance
165
+ token.getUserAssetBalance(inUSD) // underlying asset balance
166
+ token.getUserUnderlyingBalance(inUSD) // underlying token balance
167
+ token.getUserCollateral(inUSD) // posted collateral
168
+ token.getUserDebt(inUSD) // outstanding debt (borrow tokens)
169
+ ```
170
+
171
+ ### Market totals & caps
172
+
173
+ ```ts
174
+ token.getTvl(inUSD)
175
+ token.getTotalCollateral(inUSD)
176
+ token.getCollateralCap(inUSD) // remaining collateral capacity
177
+ token.getDebtCap(inUSD) // remaining debt capacity
178
+ token.getRemainingCollateral(formatted)
179
+ token.getRemainingDebt(formatted)
180
+ ```
181
+
182
+ ### Collateral parameters
183
+
184
+ ```ts
185
+ token.getCollRatio(inBPS) // collateralization ratio
186
+ token.getCollReqSoft(inBPS) // soft liquidation threshold
187
+ token.getCollReqHard(inBPS) // hard liquidation threshold
188
+ token.getLiqIncBase(inBPS) // liquidation incentive base
189
+ token.getLiqIncMin(inBPS) // liquidation incentive min
190
+ token.getLiqIncMax(inBPS) // liquidation incentive max
191
+ token.liquidationPrice // oracle liquidation price (null = infinite)
192
+ ```
193
+
194
+ ### APY & rates
195
+
196
+ ```ts
197
+ token.getApy(asPercentage) // supply APY
198
+ token.getTotalSupplyRate() // supply APY + incentives + native yield
199
+ token.getBorrowRate(inPercentage) // borrow APY
200
+ token.getTotalBorrowRate() // borrow APY minus incentive rewards
201
+
202
+ // BorrowableCToken only
203
+ token.getLiquidity(inUSD) // available liquidity to borrow
204
+ token.getUtilizationRate(inPercentage)
205
+ token.getPredictedBorrowRate(inPercentage)
206
+ token.getMaxBorrowable() // max amount given credit
207
+ ```
208
+
209
+ ### Position snapshot & preview
210
+
211
+ ```ts
212
+ token.getSnapshot(account) // position snapshot for an account
213
+ token.maxRedemption(inShares, bufferTime) // max redeemable amount
214
+ token.simulateDeposit(amount) // preview deposit without executing
215
+ token.simulateDepositAsCollateral(amount)
216
+ ```
217
+
218
+ ## ❯ Core Operations
219
+
220
+ All amounts are `Decimal` (human-readable token units) unless noted.
221
+
222
+ ### Approvals
223
+
224
+ ```ts
225
+ await token.approveUnderlying(amount, target) // approve underlying asset spend
226
+ await token.approve(amount, spender) // approve cToken itself
227
+ await token.getAllowance(contract, underlying) // check allowance
58
228
  ```
59
229
 
60
- Some of these classes use preloaded cache to prevent RPC calls for example
61
- ```js
62
- const test_1 = new ERC20(signer, `0x123`);
63
- console.log(test_1.name); // Attempts to use cache for name, so this returns undefined
64
- const name = await test_1.fetchName();
65
- console.log(name); // My Test Token
66
- console.log(test_1.name); // My Test Token
230
+ ### Deposit & Withdraw
231
+
232
+ ```ts
233
+ // Deposit as supplier (earns yield, cannot be used as collateral)
234
+ await token.deposit(amount, zap?, receiver?)
235
+
236
+ // Deposit as collateral (enables borrowing against it)
237
+ await token.depositAsCollateral(amount, zapInstructions?, receiver?)
238
+
239
+ // Withdraw
240
+ await token.redeem(amount) // by asset amount
241
+ await token.redeemShares(amount) // by share amount
242
+ await token.redeemCollateral(amount, receiver?, owner?)
243
+
244
+ // Manage posted collateral
245
+ await token.postCollateral(amount) // post unposted balance as collateral
246
+ await token.removeCollateral(amount, removeAll?)
67
247
  ```
68
248
 
69
- Note: Protocol reader will populate things like `test_1.name` for underlying assets from the first preload RPC call and wont need to be fetched.
249
+ ### Borrow & Repay (`BorrowableCToken` only)
70
250
 
251
+ ```ts
252
+ await borrowToken.borrow(amount, receiver?)
253
+ await borrowToken.repay(amount)
254
+
255
+ // Previews
256
+ const impact = await borrowToken.hypotheticalBorrowOf(amount) // on-chain health preview
257
+ await borrowToken.fetchDebt(inUSD)
258
+ await borrowToken.debtBalance(account)
259
+ ```
260
+
261
+ ### Interest rate model
262
+
263
+ ```ts
264
+ await borrowToken.fetchBorrowRate()
265
+ await borrowToken.fetchSupplyRate()
266
+ await borrowToken.fetchUtilizationRate()
267
+ await borrowToken.fetchPredictedBorrowRate()
268
+ await borrowToken.fetchUtilizationRateChange(assets, direction)
269
+ borrowToken.borrowChange(amount, rateType) // interest accrual over time period
270
+ ```
271
+
272
+ ## ❯ Plugins (Zappers & Position Managers)
273
+
274
+ Zapper and PositionManager contracts must be approved before first use.
275
+
276
+ ```ts
277
+ // Check and approve a plugin
278
+ const approved = await token.isPluginApproved('simple', 'zapper')
279
+ if (!approved) await token.approvePlugin('simple', 'zapper')
280
+
281
+ // Plugin types
282
+ // ZapperTypes: 'none' | 'native-vault' | 'vault' | 'simple' | 'native-simple'
283
+ // PositionManagerTypes: 'native-vault' | 'simple' | 'vault'
284
+
285
+ const zapper = token.getZapper('simple')
286
+ const positionManager = token.getPositionManager('simple')
287
+ ```
288
+
289
+ ## ❯ Zapping (Swap + Deposit)
290
+
291
+ Zap deposits allow depositing any token by swapping to the required underlying via the DEX aggregator.
292
+
293
+ ```ts
294
+ // Native token (MON) → deposit
295
+ await token.approvePlugin('native-simple', 'zapper')
296
+ await zapper.nativeZap(ctoken, amount, collateralize)
297
+
298
+ // Any ERC20 → swap → deposit
299
+ await token.approvePlugin('simple', 'zapper')
300
+ await token.approveUnderlying(amount)
301
+ await token.depositAsCollateral(amount, {
302
+ type: 'simple',
303
+ inputToken: inputTokenAddress,
304
+ slippage: new Decimal(0.01) // 1%
305
+ })
306
+ ```
307
+
308
+ Check approval status for a zap before executing:
309
+
310
+ ```ts
311
+ const approved = await token.isZapAssetApproved(instructions, amount)
312
+ if (!approved) await token.approveZapAsset(instructions, amount)
313
+ ```
314
+
315
+ ## ❯ Leverage & Deleverage
316
+
317
+ Leverage uses the PositionManager plugin to atomically borrow and swap into the collateral token.
318
+
319
+ ```ts
320
+ // One-step: deposit collateral + leverage
321
+ await collateralToken.approveUnderlying(amount)
322
+ await collateralToken.approvePlugin('simple', 'positionManager')
323
+ await collateralToken.depositAndLeverage(amount, borrowToken, targetLeverage, 'simple', slippage)
324
+
325
+ // Separate: deposit first, then leverage
326
+ await collateralToken.depositAsCollateral(amount)
327
+ await collateralToken.leverageUp(borrowToken, new Decimal(3), 'simple', new Decimal(0.005))
328
+
329
+ // Reduce leverage
330
+ await collateralToken.leverageDown(borrowToken, currentLeverage, targetLeverage, 'simple', slippage)
331
+
332
+ // Check current leverage
333
+ collateralToken.getLeverage() // Decimal | null (null if no debt)
334
+ ```
335
+
336
+ ### Leverage previews (via ProtocolReader)
337
+
338
+ ```ts
339
+ const preview = await reader.hypotheticalLeverageOf(account, depositCToken, borrowCToken, depositAmount)
340
+ // Returns: { currentLeverage, adjustMaxLeverage, maxLeverage, maxDebtBorrowable }
341
+ ```
342
+
343
+ ## ❯ Health & Position Previews
344
+
345
+ Preview position health before executing any action. Returns a `Decimal` percentage (0–1) or `null` (infinite / no debt).
346
+
347
+ ```ts
348
+ // Individual action previews
349
+ await market.previewPositionHealthDeposit(ctoken, amount)
350
+ await market.previewPositionHealthRedeem(ctoken, amount)
351
+ await market.previewPositionHealthBorrow(borrowToken, amount)
352
+ await market.previewPositionHealthRepay(borrowToken, amount)
353
+ await market.previewPositionHealthLeverageUp(depositCToken, depositAmount, borrowCToken, borrowAmount)
354
+ await market.previewPositionHealthLeverageDown(depositCToken, borrowCToken, newLeverage, currentLeverage)
355
+
356
+ // Generic preview
357
+ await market.previewPositionHealth(depositCToken, borrowCToken, isDeposit, collateralAmt, isRepay, debtAmt, bufferTime)
358
+
359
+ // Projected earnings/cost impact
360
+ await market.previewAssetImpact(user, collateralCToken, debtCToken, depositAmount, borrowAmount, rateType)
361
+ ```
362
+
363
+ ```ts
364
+ const health = await market.previewPositionHealthBorrow(borrowToken, new Decimal(1000))
365
+ if (health === null) {
366
+ // remains solvent with infinite health
367
+ } else if (health.lt(0.1)) {
368
+ console.warn("Would drop to 10% health — too risky")
369
+ }
370
+ ```
371
+
372
+ ## ❯ Cooldowns
373
+
374
+ Curvance enforces a 20-minute hold period between certain actions.
375
+
376
+ ```ts
377
+ market.cooldown // Date | null (current cooldown expiry)
378
+ await market.expiresAt(account) // fetch cooldown expiry from chain
379
+ await market.multiHoldExpiresAt(markets) // cooldown across multiple markets
380
+ ```
381
+
382
+ ## ❯ Format Utilities
383
+
384
+ Pure calculation helpers for building UI or simulating outcomes. All accept and return `Decimal`.
385
+
386
+ ### Leverage math
387
+
388
+ ```ts
389
+ import { leverage } from "curvance"
390
+
391
+ leverage.calculateBorrowAmount(depositUsd, leverageMultiplier)
392
+ leverage.calculateLeverageRatio(totalValue, debtAmount)
393
+ leverage.calculateDeleverageAmount(currentLeverage, targetLeverage, totalValue)
394
+ leverage.calculatePositionSize(tokenAmount, leverageMultiplier)
395
+ leverage.validateLeverageInput(input) // checks balance, min deposit, max leverage, liquidity
396
+ leverage.checkLeverageAmountBelowMinimum(input) // $10.10 minimum borrow
397
+ leverage.checkBorrowExceedsLiquidity(borrowAmount, availableLiquidity)
398
+ ```
399
+
400
+ ### Borrow math
401
+
402
+ ```ts
403
+ import { borrow } from "curvance"
404
+
405
+ borrow.calculateMaxBorrow(remainingCredit, remainingDebt, availableLiquidity)
406
+ borrow.calculateMaxRepay(userBalance, userDebt)
407
+ borrow.validateRepayRemainder(currentDebtUsd, repayAmountUsd) // enforces $10 minimum remainder
408
+ borrow.calculateDebtPreview(currentDebt, amount, isRepaying)
409
+ borrow.convertAmountByCurrencyView(amount, price, currencyView) // USD ↔ token view
410
+ ```
411
+
412
+ ### Collateral math
413
+
414
+ ```ts
415
+ import { collateral } from "curvance"
416
+
417
+ collateral.calculateExchangeRate(assetBalance, shareBalance)
418
+ collateral.calculateCollateralBreakdown(assetBalance, shares, exchangeRate)
419
+ collateral.calculateNewCollateral(currentCollateral, amount, action)
420
+ ```
421
+
422
+ ### Health display
423
+
424
+ ```ts
425
+ import { health } from "curvance"
426
+
427
+ health.getHealthStatus(percentageValue) // 'Danger' | 'Caution' | 'Healthy'
428
+ health.healthFactorToPercentage(rawFactor)
429
+ health.formatHealthFactorPercentage(value)
430
+ health.formatHealthFactor(value) // handles infinity
431
+ health.getLiquidityStatus(ratio) // 'green' | 'yellow' | 'red'
432
+ ```
433
+
434
+ ### Amount formatting
435
+
436
+ ```ts
437
+ import { amounts } from "curvance"
438
+
439
+ amounts.clampUsdDustAmount(value) // zero out sub-$0.01 amounts
440
+ amounts.normalizeAmountString(value, maxFractionDigits, roundingMode)
441
+ amounts.normalizeCurrencyAmounts({ amount, currencyView, tokenDecimals, price })
442
+ ```
443
+
444
+ ## ❯ Helpers & Utilities
445
+
446
+ ```ts
447
+ import {
448
+ getContractAddresses,
449
+ contractSetup,
450
+ handleTransactionWithOracles,
451
+ toDecimal, toBigInt,
452
+ getDepositApy, getBorrowCost,
453
+ getInterestYield, getNativeYield,
454
+ getMerklDepositIncentives, getMerklBorrowIncentives,
455
+ getRateSeconds,
456
+ WAD, WAD_DECIMAL, BPS, RAY,
457
+ UINT256_MAX, EMPTY_ADDRESS, NATIVE_ADDRESS,
458
+ DEFAULT_SLIPPAGE_BPS,
459
+ } from "curvance"
460
+ ```
461
+
462
+ | Helper | Description |
463
+ |---|---|
464
+ | `getContractAddresses(chain)` | All contract addresses for a chain |
465
+ | `contractSetup(provider, address, abi)` | Create a typed contract instance |
466
+ | `handleTransactionWithOracles(...)` | Wraps a tx in a Redstone multicall when a pull oracle price write is required |
467
+ | `toDecimal(value, decimals)` | `bigint` → `Decimal` |
468
+ | `toBigInt(value, decimals)` | `Decimal` → `bigint` |
469
+ | `getDepositApy(token, opportunities, apyOverrides)` | Total deposit yield (interest + Merkl + native) |
470
+ | `getBorrowCost(token, opportunities)` | Net borrow cost — may be negative when rewards exceed rate |
471
+ | `getInterestYield(token)` | Lending APY only |
472
+ | `getNativeYield(token, apyOverrides)` | Native yield component |
473
+ | `getMerklDepositIncentives(tokenAddress, opportunities)` | Merkl reward APR for deposits |
474
+ | `getMerklBorrowIncentives(tokenAddress, opportunities)` | Merkl reward APR for borrows |
475
+ | `getRateSeconds(rateType)` | Convert `'year' \| 'month' \| 'week' \| 'day'` → seconds |
476
+
477
+ ## ❯ Fee Policy
478
+
479
+ The SDK supports configurable fees applied at the DEX aggregator layer for swaps. Fees are denominated in BPS of the swap input and charged on leverage, deleverage, deposit+leverage, and zap operations.
480
+
481
+ ```ts
482
+ import { flatFeePolicy, NO_FEE_POLICY } from "curvance"
483
+
484
+ const feePolicy = flatFeePolicy({
485
+ bps: 10n, // 0.1% default fee
486
+ feeReceiver: "0xYourAddress",
487
+ chain: "monad-mainnet",
488
+ stableToStableBps: 2n, // optional lower fee for stable↔stable swaps
489
+ })
490
+
491
+ const { markets } = await setupChain("monad-mainnet", wallet, false, undefined, { feePolicy })
492
+ ```
493
+
494
+ The SDK automatically returns 0 bps for native ↔ wrapped-native swaps and same-token no-op zaps.
495
+
496
+ ```ts
497
+ // FeePolicy interface — implement your own
498
+ interface FeePolicy {
499
+ feeReceiver: address;
500
+ getFeeBps(ctx: FeePolicyContext): bigint;
501
+ }
502
+
503
+ // Context passed to getFeeBps
504
+ interface FeePolicyContext {
505
+ operation: 'leverage-up' | 'leverage-down' | 'deposit-and-leverage' | 'zap';
506
+ inputToken: address;
507
+ outputToken: address;
508
+ inputAmount: bigint;
509
+ currentLeverage: Decimal | null;
510
+ targetLeverage: Decimal | null;
511
+ }
512
+ ```
513
+
514
+ ## ❯ Integrations
515
+
516
+ ### Merkl rewards
517
+
518
+ ```ts
519
+ import { fetchMerklOpportunities, fetchMerklUserRewards, fetchMerklCampaignsBySymbol } from "curvance"
520
+
521
+ // All active opportunities (APR, token, type)
522
+ const opportunities = await fetchMerklOpportunities()
523
+
524
+ // Pending rewards for a user
525
+ const rewards = await fetchMerklUserRewards({ wallet: address, chainId: 143 })
526
+
527
+ // Campaigns for a specific token
528
+ const campaigns = await fetchMerklCampaignsBySymbol({ tokenSymbol: "USDC" })
529
+ ```
530
+
531
+ ### Portfolio snapshots
532
+
533
+ ```ts
534
+ import { takePortfolioSnapshot, snapshotMarket } from "curvance"
535
+
536
+ // Full portfolio across all markets
537
+ const snapshot = await takePortfolioSnapshot(account)
538
+ // Returns: { account, chain, timestamp, totalDepositsUSD, totalDebtUSD, netUSD, dailyEarnings, dailyCost, markets[] }
539
+
540
+ // Single market
541
+ const marketSnapshot = snapshotMarket(market)
542
+ ```
543
+
544
+ ## ❯ Optimizer
545
+
546
+ The `OptimizerReader` reads yield-rebalancing vaults that allocate across markets.
547
+
548
+ ```ts
549
+ import { OptimizerReader } from "curvance"
550
+
551
+ const optimizer = new OptimizerReader(provider)
552
+
553
+ await optimizer.getOptimizerMarketData(optimizerAddresses)
554
+ // Returns: { totalAssets, sharePrice, performanceFee, markets[] }
555
+
556
+ await optimizer.getOptimizerUserData(optimizerAddresses, account)
557
+ // Returns: user balance and redeemable amounts
558
+
559
+ await optimizer.optimalDeposit(optimizer, assets) // best market to deposit into
560
+ await optimizer.optimalWithdrawal(optimizer, assets) // best market to withdraw from
561
+ await optimizer.optimalRebalance(optimizer) // suggested reallocations: { cToken, assets }[]
562
+ ```
563
+
564
+ ## ❯ TypeScript Types
565
+
566
+ ```ts
567
+ // Primitives
568
+ type address = `0x${string}` // checksummed Ethereum address
569
+ type bytes = `0x${string}` // hex-encoded calldata
570
+ type Percentage = Decimal // 0–1, e.g. 0.7 = 70%
571
+ type USD = Decimal // human-readable USD (1.0 = $1)
572
+ type USD_WAD = bigint // USD in 1e18 WAD format
573
+ type TokenInput = Decimal // human-readable token amount
574
+ type TypeBPS = bigint // basis points (10000 = 100%)
575
+ type ChainRpcPrefix = "monad-mainnet" | "arb-sepolia"
576
+ type curvance_provider = JsonRpcSigner | Wallet | JsonRpcProvider
577
+ type curvance_signer = JsonRpcSigner | Wallet
578
+
579
+ // Market categorization
580
+ type MarketCategory = "stablecoin" | "staking" | "restaking" | "yield-stablecoin" | "blue-chip" | "native"
581
+ type CollateralSource = "Renzo" | "Upshift" | "Yuzu" | "Native" | "Circle" | "Fastlane" | "Apriori" | "Mu Digital" | "Kintsu" | "Reservoir"
582
+
583
+ // Operations
584
+ type ZapperTypes = 'none' | 'native-vault' | 'vault' | 'simple' | 'native-simple'
585
+ type PositionManagerTypes = 'native-vault' | 'simple' | 'vault'
586
+ type ChangeRate = 'year' | 'month' | 'week' | 'day'
587
+
588
+ // DEX
589
+ interface Quote {
590
+ to: address
591
+ calldata: bytes
592
+ min_out: bigint
593
+ out: bigint
594
+ }
595
+ ```
596
+
597
+ All numeric return values are `bigint` or `Decimal` — never plain JS `number`.
598
+
599
+ ## ❯ Constants
600
+
601
+ ```ts
602
+ WAD // 1_000_000_000_000_000_000n (1e18)
603
+ BPS // 10_000n
604
+ RAY // 1_000_000_000_000_000_000_000_000_000n (1e27)
605
+ WAD_SQUARED // 1e36n
606
+ WAD_DECIMAL // Decimal('1e18')
607
+ UINT256_MAX
608
+ EMPTY_ADDRESS // '0x0000000000000000000000000000000000000000'
609
+ NATIVE_ADDRESS // canonical native token address
610
+ DEFAULT_SLIPPAGE_BPS // 100n (1%)
611
+ ```
71
612
 
72
- ### Helpers
73
- - `getContractAddresses` - Grab the contracts addresses for a given chain
74
- - `AdaptorTypes` - Adaptor identifier enums
75
- - `WAD` - WAD amount
76
- - `WAD_DECIMAL` - WAD amount as Decimal.js type
77
- - `contractSetup` - Used to initialize contract & attach typescript interface
78
- - `handleTransactionWithOracles` - Depending on what adaptor is being used to execute the function we choose to run a multi-call that will write-price on-chain with the given function -- but only if the adaptor is the type of adaptor that requires this (pull oracle)
613
+ ## ❯ Dependencies
79
614
 
80
- ```js
81
- const contracts = getContractAddresses('monad-testnet');
82
- console.log(contracts.ProtocolReader);
83
- ```
615
+ | Package | Purpose |
616
+ |---|---|
617
+ | [ethers v6](https://www.npmjs.com/package/ethers) | Typed contract interactions, providers, and signer handling |
618
+ | [decimal.js](https://www.npmjs.com/package/decimal.js) | Arbitrary-precision math for all token amounts, prices, and rates |
619
+ | [@redstone-finance/sdk](https://www.npmjs.com/package/@redstone-finance/sdk) | Price feed writes bundled into multicalls for pull-oracle adaptors |