From 2ea726bd331beb45d0cebcd4f914231aaf9326db Mon Sep 17 00:00:00 2001 From: fedorovdg Date: Fri, 21 Nov 2025 16:04:12 +0400 Subject: [PATCH 1/7] feat: add router adapter --- README.md | 247 ++++-- src/constants/aliases.ts | 15 + src/external-api.ts | 8 +- src/interfaces.ts | 19 + src/lendMarkets/LendMarketTemplate.ts | 566 +++++++------ src/lendMarkets/interfaces/leverageZapV2.ts | 73 ++ src/lendMarkets/modules/index.ts | 1 + src/lendMarkets/modules/leverageZapV2.ts | 849 ++++++++++++++++++++ src/llamalend.ts | 1 + src/utils.ts | 13 +- 10 files changed, 1450 insertions(+), 342 deletions(-) create mode 100644 src/lendMarkets/interfaces/leverageZapV2.ts create mode 100644 src/lendMarkets/modules/index.ts create mode 100644 src/lendMarkets/modules/leverageZapV2.ts diff --git a/README.md b/README.md index ecd0094..dd50dc0 100644 --- a/README.md +++ b/README.md @@ -948,34 +948,93 @@ await lendMarket.forceUpdateUserState(txHash, "0x123..."); ``` ### Leverage (createLoan, borrowMore, repay) for lendMarket + +LendMarket leverage operations support different routers and require quote information from external routing services. + +#### Breaking Changes + +**1. New metrics methods - get all metrics in one call** +- Instead of `createLoanBands`, `createLoanPrices`, `createLoanHealth`, `createLoanPriceImpact`: `createLoanExpectedMetrics()` - returns priceImpact, bands, prices, health +- Instead of `borrowMoreBands`, `borrowMorePrices`, `borrowMoreHealth`, `borrowMorePriceImpact`: + `borrowMoreExpectedMetrics()` – returns priceImpact, bands, prices, health +- Instead of `repayBands`, `repayPrices`, `repayHealth`, `repayPriceImpact`: + `repayExpectedMetrics()` – returns priceImpact, bands, prices, health + + +**ILeverageMetrics** - metrics object returned by new methods: + +```ts +interface ILeverageMetrics { + priceImpact: number; + bands: [number, number]; + prices: string[]; + health: string; +} +``` + +**2. Final methods now require `router` and `calldata` parameters** +- **Before**: `createLoan(userCollateral, userBorrowed, debt, range)` +- **Now**: `createLoan(userCollateral, userBorrowed, debt, range, router, calldata)` +- **Before**: `borrowMore(userCollateral, userBorrowed, debt, slippage)` +- **Now**: `borrowMore(userCollateral, userBorrowed, debt, router, calldata)` +- **Before**: `repay(stateCollateral, userCollateral, userBorrowed, slippage)` +- **Now**: `repay(stateCollateral, userCollateral, userBorrowed, router, calldata)` + +where `router` - address of router, `calldata` - calldata byte code + +#### Understanding Callback Functions and Quotes + +**GetExpectedFn** - callback function for fetching quotes from router (e.g., Odos, Curve): + +```ts +type GetExpectedFn = ( + fromToken: string, // Address of token to swap from + toToken: string, // Address of token to swap to + amountIn: bigint, // Amount of tokens to swap (in wei) + blacklist: string | string[], // Contract/pool addresses to exclude +) => Promise; +``` + +**IQuote** - quote object with swap information: + +```ts +interface IQuote { + outAmount: string; // Amount of tokens to receive + priceImpact: number; // Price impac +} +``` + +#### Which quotes are needed for different operations + +| Action type | Quote inputAmount | +|--------------|------------------| +| createLoan | debt + userBorrowed | +| borrowMore | debt + userBorrowed | +| repay | stateCollateral + userCollateral | + +**For createLoan and borrowMore:** +- Get quote for swapping `(debt + userBorrowed)` borrowed tokens → collateral tokens +- Example: For debt=2000 and userBorrowed=1000, get quote for swapping 3000 borrowed tokens to collateral + +!!! Be careful: here debt means the amount of debt added during the operation, not the new total debt the user will have after it. + +**For repay:** +- Get quote for swapping `(stateCollateral + userCollateral)` collateral tokens → borrowed tokens +- Example: For stateCollateral=2 and userCollateral=1, get quote for swapping 3 collateral tokens to borrowed tokens + + +#### Leverage operations with routers + ```ts (async () => { - await llamalend.init('JsonRpc', {}, {}, API_KEY_1INCH); + await llamalend.init('JsonRpc', {}); await llamalend.lendMarkets.fetchMarkets(); const lendMarket = llamalend.getLendMarket('one-way-market-0'); - console.log(lendMarket.collateral_token, lendMarket.borrowed_token); - // { - // address: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1', - // decimals: 18, - // name: 'Wrapped Ether', - // symbol: 'WETH' - // } - // - // { - // address: '0x498bf2b1e120fed3ad3d42ea2165e9b73f99c1e5', - // decimals: 18, - // name: 'curve.finance USD Stablecoin', - // symbol: 'crvUSD' - // } - console.log(await lendMarket.wallet.balances()); - // { - // collateral: '100.0', - // borrowed: '2000000.0', - // vaultShares: '0.0', - // gauge: '0' - // } - + + const router = '0x.....'; // router name + const calldata = '0x...'; // calldata from router for executing swap + const quote = // quote from your router (debt + userBorrowed) // - Create Loan - @@ -994,10 +1053,11 @@ await lendMarket.forceUpdateUserState(txHash, "0x123..."); let userBorrowed = 1000; let debt = 2000; const range = 10; - const slippage = 0.5; // % await lendMarket.leverage.maxLeverage(range); // 7.4728229145282742179 - await lendMarket.leverage.createLoanMaxRecv(userCollateral, userBorrowed, range); + + // Get maximum possible debt for given parameters + await lendMarket.leverage.createLoanMaxRecv(userCollateral, userBorrowed, range, getExpected); // { // maxDebt: '26089.494406081862861214', // maxTotalCollateral: '9.539182089833411347', @@ -1007,7 +1067,11 @@ await lendMarket.forceUpdateUserState(txHash, "0x123..."); // maxLeverage: '7.25291100528992828612', // avgPrice: '3172.3757757003568790858' // } - await lendMarket.leverage.createLoanExpectedCollateral(userCollateral, userBorrowed, debt, slippage); + + // Get quote for swapping (debt + userBorrowed)!!! + + // Get expected collateral amount + await lendMarket.leverage.createLoanExpectedCollateral(userCollateral, userBorrowed, debt, quote); // { // totalCollateral: '1.946422996710829', // userCollateral: '1.0', @@ -1016,18 +1080,23 @@ await lendMarket.forceUpdateUserState(txHash, "0x123..."); // leverage: '1.4796358613861877' // avgPrice: '3169.8299919022623523421' // } - await lendMarket.leverage.createLoanPriceImpact(userBorrowed, debt); - // 0.08944411854377342 % - await lendMarket.leverage.createLoanMaxRange(userCollateral, userBorrowed, debt); - // 50 - await lendMarket.leverage.createLoanBands(userCollateral, userBorrowed, debt, range); - // [ 76, 67 ] - await lendMarket.leverage.createLoanPrices(userCollateral, userBorrowed, debt, range); - // [ '1027.977701011670136614', '1187.061409925215211173' ] - await lendMarket.leverage.createLoanHealth(userCollateral, userBorrowed, debt, range); - // 195.8994783042570637 - await lendMarket.leverage.createLoanHealth(userCollateral, userBorrowed, debt, range, false); - // 3.2780908310686365 + + // NEW: Get all metrics in one call + const metrics = await lendMarket.leverage.createLoanExpectedMetrics( + userCollateral, + userBorrowed, + debt, + range, + quote, + true // healthIsFull - true for full health, false for not full + ); + // { + // priceImpact: 0.08944411854377342, // % + // bands: [76, 67], + // prices: ['1027.977701011670136614', '1187.061409925215211173'], + // health: '195.8994783042570637' // % + // + await lendMarket.leverage.createLoanIsApproved(userCollateral, userBorrowed); // false await lendMarket.leverage.createLoanApprove(userCollateral, userBorrowed); @@ -1035,12 +1104,10 @@ await lendMarket.forceUpdateUserState(txHash, "0x123..."); // '0xd5491d9f1e9d8ac84b03867494e35b25efad151c597d2fa4211d7bf5d540c98e', // '0x93565f37ec5be902a824714a30bddc25cf9cd9ed39b4c0e8de61fab44af5bc8c' // ] - await lendMarket.leverage.createLoanRouteImage(userBorrowed, debt); - // 'data:image/svg+xml;base64,PHN2ZyBpZD0ic2Fua2V5UGFyZW50U3ZnIiB4bWxucz...' - // You must call lendMarket.leverage.createLoanExpectedCollateral() with the same args before - await lendMarket.leverage.createLoan(userCollateral, userBorrowed, debt, range); + // Create loan, passing router address and calldata from router + await lendMarket.leverage.createLoan(userCollateral, userBorrowed, debt, range, router, calldata); // 0xeb1b7a92bcb02598f00dc8bbfe8fa3a554e7a2b1ca764e0ee45e2bf583edf731 await lendMarket.wallet.balances(); @@ -1083,7 +1150,9 @@ await lendMarket.forceUpdateUserState(txHash, "0x123..."); userCollateral = 2; userBorrowed = 2000; debt = 10000; - await lendMarket.leverage.borrowMoreMaxRecv(userCollateral, userBorrowed); + + // Get maximum possible debt for given parameters + await lendMarket.leverage.borrowMoreMaxRecv(userCollateral, userBorrowed, getExpected); // { // maxDebt: '76182.8497941193262889', // maxTotalCollateral: '26.639775583730298462', @@ -1092,7 +1161,10 @@ await lendMarket.forceUpdateUserState(txHash, "0x123..."); // collateralFromMaxDebt: '22.962457277119938834', // avgPrice: '3172.55402418338331369083' // } - await lendMarket.leverage.borrowMoreExpectedCollateral(userCollateral, userBorrowed, debt, slippage); + + // Get quote for swapping (debt + userBorrowed) + + await lendMarket.leverage.borrowMoreExpectedCollateral(userCollateral, userBorrowed, debt, quote); // { // totalCollateral: '5.783452104143246413', // userCollateral: '2.0', @@ -1100,25 +1172,29 @@ await lendMarket.forceUpdateUserState(txHash, "0x123..."); // collateralFromDebt: '3.152876753452705342' // avgPrice: '3171.70659749038129067231' // } - await lendMarket.leverage.borrowMorePriceImpact(userBorrowed, debt); - // 0.010784277354269765 % - await lendMarket.leverage.borrowMoreBands(userCollateral, userBorrowed, debt); - // [ 47, 38 ] - await lendMarket.leverage.borrowMorePrices(userCollateral, userBorrowed, debt); - // [ '1560.282474721398939216', '1801.742501325928269008' ] - await lendMarket.leverage.borrowMoreHealth(userCollateral, userBorrowed, debt, true); - // 91.6798951784708552 - await lendMarket.leverage.borrowMoreHealth(userCollateral, userBorrowed, debt, false); - // 3.7614279042995641 + + // NEW: Get all metrics in one call + const metricsBM = await lendMarket.leverage.borrowMoreExpectedMetrics( + userCollateral, + userBorrowed, + debt, + quote, + true // healthIsFull - true for full health, false for not full + ); + // { + // priceImpact: 0.010784277354269765, // % + // bands: [47, 38], + // prices: ['1560.282474721398939216', '1801.742501325928269008'], + // health: '91.6798951784708552' // % + // } + await lendMarket.leverage.borrowMoreIsApproved(userCollateral, userBorrowed); // true await lendMarket.leverage.borrowMoreApprove(userCollateral, userBorrowed); // [] - await lendMarket.leverage.borrowMoreRouteImage(userBorrowed, debt); - // 'data:image/svg+xml;base64,PHN2ZyBpZD0ic2Fua2V5UGFyZW50U3ZnIiB4bWxucz...' - // You must call lendMarket.leverage.borrowMoreExpectedCollateral() with the same args before - await lendMarket.leverage.borrowMore(userCollateral, userBorrowed, debt, slippage); + // Execute borrowMore, passing router address and calldata from router + await lendMarket.leverage.borrowMore(userCollateral, userBorrowed, debt, router, calldata); // 0x6357dd6ea7250d7adb2344cd9295f8255fd8fbbe85f00120fbcd1ebf139e057c await lendMarket.wallet.balances(); @@ -1164,7 +1240,10 @@ await lendMarket.forceUpdateUserState(txHash, "0x123..."); const stateCollateral = 2; userCollateral = 1; userBorrowed = 1500; - await lendMarket.leverage.repayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed, slippage); + + // Get quote for swapping (stateCollateral + userCollateral) + + await lendMarket.leverage.repayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed, quote); // { // totalBorrowed: '10998.882838599741571472', // borrowedFromStateCollateral: '6332.588559066494374648', @@ -1173,29 +1252,33 @@ await lendMarket.forceUpdateUserState(txHash, "0x123..."); // avgPrice: '3166.29427953324743125312' // } - await lendMarket.leverage.repayPriceImpact(stateCollateral, userCollateral); - // 0.013150142802201724 % - await lendMarket.leverage.repayIsFull(stateCollateral, userCollateral, userBorrowed); + await lendMarket.leverage.repayIsFull(stateCollateral, userCollateral, userBorrowed, quote); // false - await lendMarket.leverage.repayIsAvailable(stateCollateral, userCollateral, userBorrowed); + await lendMarket.leverage.repayIsAvailable(stateCollateral, userCollateral, userBorrowed, quote); // true - await lendMarket.leverage.repayBands(stateCollateral, userCollateral, userBorrowed); - // [ 199, 190 ] - await lendMarket.leverage.repayPrices(stateCollateral, userCollateral, userBorrowed); - // [ '175.130965754280721633', '202.233191367561902757' ] - await lendMarket.leverage.repayHealth(stateCollateral, userCollateral, userBorrowed, true); - // 1699.6097751079226865 - await lendMarket.leverage.repayHealth(stateCollateral, userCollateral, userBorrowed, false); - // 3.4560086962806991 + + // NEW: Get all metrics in one call + const metricsRepay = await lendMarket.leverage.repayExpectedMetrics( + stateCollateral, + userCollateral, + userBorrowed, + true, // healthIsFull - true for full health, false for not full + quote + ); + // { + // priceImpact: 0.013150142802201724, // % + // bands: [199, 190], + // prices: ['175.130965754280721633', '202.233191367561902757'], + // health: '1699.6097751079226865' // % + // } + await lendMarket.leverage.repayIsApproved(userCollateral, userBorrowed); // false await lendMarket.leverage.repayApprove(userCollateral, userBorrowed); // ['0xd8a8d3b3f67395e1a4f4d4f95b041edcaf1c9f7bab5eb8a8a767467678295498'] - await lendMarket.leverage.repayRouteImage(stateCollateral, userCollateral); - // 'data:image/svg+xml;base64,PHN2ZyBpZD0ic2Fua2V5UGFyZW50U3ZnIiB4bWxucz...' - // You must call lendMarket.leverage.repayExpectedBorrowed() with the same args before - await lendMarket.leverage.repay(stateCollateral, userCollateral, userBorrowed, slippage); + // Execute repay, passing router address and calldata from router + await lendMarket.leverage.repay(stateCollateral, userCollateral, userBorrowed, router, calldata); // 0xe48a97fef1c54180a2c7d104d210a95ac1a516fdd22109682179f1582da23a82 await lendMarket.wallet.balances(); @@ -1225,7 +1308,7 @@ await lendMarket.forceUpdateUserState(txHash, "0x123..."); ### Leverage createLoan all ranges methods for lendMarket ```ts - await llamalend.init('JsonRpc', {}, {}, API_KEY_1INCH); + await llamalend.init('JsonRpc', {}); await llamalend.lendMarkets.fetchMarkets(); const lendMarket = llamalend.getLendMarket('one-way-market-0'); @@ -1233,7 +1316,9 @@ await lendMarket.forceUpdateUserState(txHash, "0x123..."); const userCollateral = 1; const userBorrowed = 1000; const debt = 2000; - await lendMarket.leverage.createLoanMaxRecvAllRanges(userCollateral, userBorrowed); + + // Get maximum values for all possible ranges + await lendMarket.leverage.createLoanMaxRecvAllRanges(userCollateral, userBorrowed, getExpected); // { // '4': { // maxDebt: '37916.338071504823875251', @@ -1283,8 +1368,10 @@ await lendMarket.forceUpdateUserState(txHash, "0x123..."); // maxLeverage: '2.94916151440614435181', // avgPrice: '3167.28167656266072703689' // } - - await lendMarket.leverage.createLoanBandsAllRanges(userCollateral, userBorrowed, debt); + + // Get quote for specific debt + userBorrowed + // and provide getExpected callback + await lendMarket.leverage.createLoanBandsAllRanges(userCollateral, userBorrowed, debt, getExpected, quote); // { // '4': [ 73, 70 ], // '5': [ 73, 69 ], @@ -1296,7 +1383,7 @@ await lendMarket.forceUpdateUserState(txHash, "0x123..."); // '50': [ 97, 48 ] // } - await lendMarket.leverage.createLoanPricesAllRanges(userCollateral, userBorrowed, debt); + await lendMarket.leverage.createLoanPricesAllRanges(userCollateral, userBorrowed, debt, getExpected, quote); // { // '4': [ '1073.323292757532604807', '1136.910693647788699808' ], // '5': [ '1073.323292757532604807', '1153.387660222394333133' ], diff --git a/src/constants/aliases.ts b/src/constants/aliases.ts index c2bce93..9cffb7e 100644 --- a/src/constants/aliases.ts +++ b/src/constants/aliases.ts @@ -9,6 +9,7 @@ export const ALIASES_ETHEREUM = lowerCaseValues({ "gauge_factory": "0xabC000d88f23Bb45525E447528DBF656A9D55bf5", // "leverage_zap": "0x3294514B78Df4Bb90132567fcf8E5e99f390B687", // 1inch "leverage_zap": "0xC5898606BdB494a994578453B92e7910a90aA873", // odos + "leverage_zap_v2": "0x0000000000000000000000000000000000000000", "leverage_markets_start_id": "9", "crvUSD": "0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E", "st_crvUSD": "0x0655977FEb2f289A4aB78af67BAB0d17aAb84367", @@ -20,6 +21,7 @@ export const ALIASES_POLYGON = lowerCaseValues({ "gauge_controller": "0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB", "gauge_factory": "0xabC000d88f23Bb45525E447528DBF656A9D55bf5", "leverage_zap": "0x0000000000000000000000000000000000000000", + "leverage_zap_v2": "0x0000000000000000000000000000000000000000", }); export const ALIASES_FANTOM = lowerCaseValues({ @@ -28,6 +30,7 @@ export const ALIASES_FANTOM = lowerCaseValues({ "gauge_controller": "0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB", "gauge_factory": "0xabC000d88f23Bb45525E447528DBF656A9D55bf5", "leverage_zap": "0x0000000000000000000000000000000000000000", + "leverage_zap_v2": "0x0000000000000000000000000000000000000000", }); export const ALIASES_AVALANCHE = lowerCaseValues({ @@ -36,6 +39,7 @@ export const ALIASES_AVALANCHE = lowerCaseValues({ "gauge_controller": "0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB", "gauge_factory": "0xabC000d88f23Bb45525E447528DBF656A9D55bf5", "leverage_zap": "0x0000000000000000000000000000000000000000", + "leverage_zap_v2": "0x0000000000000000000000000000000000000000", }); export const ALIASES_ARBITRUM = lowerCaseValues({ @@ -45,6 +49,7 @@ export const ALIASES_ARBITRUM = lowerCaseValues({ "gauge_factory": "0xabC000d88f23Bb45525E447528DBF656A9D55bf5", // "leverage_zap": "0x61C404B60ee9c5fB09F70F9A645DD38fE5b3A956", // 1inch "leverage_zap": "0xb7b240CFa985306563A301bC417Bc9715059a117", // odos + "leverage_zap_v2": "0x5b07Db9a85992c877b9fBeA6DCC4F79292577640", "leverage_markets_start_id": "9", }); @@ -55,6 +60,7 @@ export const ALIASES_OPTIMISM = lowerCaseValues({ "gauge_factory_old": "0xabC000d88f23Bb45525E447528DBF656A9D55bf5", "gauge_factory": "0x871fBD4E01012e2E8457346059e8C189d664DbA4", "leverage_zap": "0x273e44B9a1841857d9360e8792bB59f9e1FfE9Da", + "leverage_zap_v2": "0x0000000000000000000000000000000000000000", "leverage_markets_start_id": "0", "gas_oracle": '0xc0d3C0d3C0d3c0D3C0D3C0d3C0d3C0D3C0D3000f', "gas_oracle_blob": '0x420000000000000000000000000000000000000f', @@ -66,6 +72,7 @@ export const ALIASES_XDAI = lowerCaseValues({ "gauge_controller": "0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB", "gauge_factory": "0xabC000d88f23Bb45525E447528DBF656A9D55bf5", "leverage_zap": "0x0000000000000000000000000000000000000000", + "leverage_zap_v2": "0x0000000000000000000000000000000000000000", }); export const ALIASES_MOONBEAM = lowerCaseValues({ @@ -74,6 +81,7 @@ export const ALIASES_MOONBEAM = lowerCaseValues({ "gauge_controller": "0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB", "gauge_factory": "0xabC000d88f23Bb45525E447528DBF656A9D55bf5", "leverage_zap": "0x0000000000000000000000000000000000000000", + "leverage_zap_v2": "0x0000000000000000000000000000000000000000", }); export const ALIASES_AURORA = lowerCaseValues({ @@ -89,6 +97,7 @@ export const ALIASES_KAVA = lowerCaseValues({ "gauge_controller": "0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB", "gauge_factory": "0xabC000d88f23Bb45525E447528DBF656A9D55bf5", "leverage_zap": "0x0000000000000000000000000000000000000000", + "leverage_zap_v2": "0x0000000000000000000000000000000000000000", }); export const ALIASES_CELO = lowerCaseValues({ @@ -97,6 +106,7 @@ export const ALIASES_CELO = lowerCaseValues({ "gauge_controller": "0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB", "gauge_factory": "0xabC000d88f23Bb45525E447528DBF656A9D55bf5", "leverage_zap": "0x0000000000000000000000000000000000000000", + "leverage_zap_v2": "0x0000000000000000000000000000000000000000", }); export const ALIASES_ZKSYNC = lowerCaseValues({ @@ -105,6 +115,7 @@ export const ALIASES_ZKSYNC = lowerCaseValues({ "gauge_controller": "0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB", // <--- TODO CHANGE "gauge_factory": "0x0000000000000000000000000000000000000000", "leverage_zap": "0x0000000000000000000000000000000000000000", + "leverage_zap_v2": "0x0000000000000000000000000000000000000000", }); export const ALIASES_BASE = lowerCaseValues({ @@ -113,6 +124,7 @@ export const ALIASES_BASE = lowerCaseValues({ "gauge_controller": "0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB", // <--- TODO CHANGE "gauge_factory": "0xabC000d88f23Bb45525E447528DBF656A9D55bf5", "leverage_zap": "0x0000000000000000000000000000000000000000", + "leverage_zap_v2": "0x0000000000000000000000000000000000000000", }); export const ALIASES_BSC = lowerCaseValues({ @@ -121,6 +133,7 @@ export const ALIASES_BSC = lowerCaseValues({ "gauge_controller": "0x0000000000000000000000000000000000000000", // <--- TODO CHANGE "gauge_factory": "0xabC000d88f23Bb45525E447528DBF656A9D55bf5", "leverage_zap": "0x0000000000000000000000000000000000000000", + "leverage_zap_v2": "0x0000000000000000000000000000000000000000", }); export const ALIASES_FRAXTAL = lowerCaseValues({ @@ -130,6 +143,7 @@ export const ALIASES_FRAXTAL = lowerCaseValues({ "gauge_factory_old": "0xeF672bD94913CB6f1d2812a6e18c1fFdEd8eFf5c", "gauge_factory": "0x0b8d6b6cefc7aa1c2852442e518443b1b22e1c52", "leverage_zap": "0x37c5ab57AF7100Bdc9B668d766e193CCbF6614FD", // odos + "leverage_zap_v2": "0x0000000000000000000000000000000000000000", "leverage_markets_start_id": "0", }); @@ -139,5 +153,6 @@ export const ALIASES_SONIC = lowerCaseValues({ "gauge_controller": "0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB", "gauge_factory": "0xf3A431008396df8A8b2DF492C913706BDB0874ef", "leverage_zap": "0x5552b631e2aD801fAa129Aacf4B701071cC9D1f7", // odos + "leverage_zap_v2": "0x0000000000000000000000000000000000000000", "leverage_markets_start_id": "0", }); \ No newline at end of file diff --git a/src/external-api.ts b/src/external-api.ts index 4c9aad6..4fdd414 100644 --- a/src/external-api.ts +++ b/src/external-api.ts @@ -118,9 +118,9 @@ export const _getUserCollateral = memoize( ) export const _getUserCollateralForce = async ( - network: INetworkName, - controller: string, - user: string, + network: INetworkName, + controller: string, + user: string, newTx: string ): Promise => { await fetch(`https://prices.curve.finance/v1/lending/collateral_events/${network}/${controller}/${user}?new_tx=${newTx}`); @@ -234,4 +234,4 @@ async function fetchJson(url: string): Promise { async function fetchData(url: string) { const {data} = await fetchJson(url); return data; -} +} \ No newline at end of file diff --git a/src/interfaces.ts b/src/interfaces.ts index ba9081f..6a2a429 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -230,3 +230,22 @@ export interface ILlamma { export interface IResponseApi { data: any } + +export interface IQuote { + outAmount: string, + priceImpact: number +} + +export interface ILeverageMetrics { + priceImpact: number, + bands: [number, number], + prices: string[], + health: string, +} + +export type GetExpectedFn = ( + fromToken: string, + toToken: string, + amountIn: bigint, + blacklist: string | string[], +) => Promise; \ No newline at end of file diff --git a/src/lendMarkets/LendMarketTemplate.ts b/src/lendMarkets/LendMarketTemplate.ts index 005eaff..718c039 100644 --- a/src/lendMarkets/LendMarketTemplate.ts +++ b/src/lendMarkets/LendMarketTemplate.ts @@ -27,6 +27,8 @@ import {IDict, TGas, TAmount, IReward, IQuoteOdos, IOneWayMarket, IPartialFrac} import { _getExpectedOdos, _getQuoteOdos, _assembleTxOdos, _getUserCollateral, _getUserCollateralForce, _getMarketsData } from "../external-api.js"; import ERC20Abi from '../constants/abis/ERC20.json' with {type: 'json'}; import {cacheKey, cacheStats} from "../cache/index.js"; +import {ILeverageZapV2} from "./interfaces/leverageZapV2.js"; +import {LeverageZapV2Module} from "./modules/leverageZapV2.js"; const DAY = 86400; @@ -237,6 +239,7 @@ export class LendMarketTemplate { repay: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, slippage?: number) => Promise, } }; + leverageZapV2: ILeverageZapV2; constructor(id: string, marketData: IOneWayMarket, llamalend: Llamalend) { this.llamalend = llamalend; @@ -387,6 +390,51 @@ export class LendMarketTemplate { }, } + const leverageZapV2 = new LeverageZapV2Module(this); + + this.leverageZapV2 = { + hasLeverage: leverageZapV2.hasLeverage.bind(leverageZapV2), + + maxLeverage: leverageZapV2.maxLeverage.bind(leverageZapV2), + + createLoanMaxRecv: leverageZapV2.leverageCreateLoanMaxRecv.bind(leverageZapV2), + createLoanMaxRecvAllRanges: leverageZapV2.leverageCreateLoanMaxRecvAllRanges.bind(leverageZapV2), + createLoanExpectedCollateral: leverageZapV2.leverageCreateLoanExpectedCollateral.bind(leverageZapV2), + createLoanMaxRange: leverageZapV2.leverageCreateLoanMaxRange.bind(leverageZapV2), + createLoanBandsAllRanges: leverageZapV2.leverageCreateLoanBandsAllRanges.bind(leverageZapV2), + createLoanPricesAllRanges: leverageZapV2.leverageCreateLoanPricesAllRanges.bind(leverageZapV2), + createLoanIsApproved: leverageZapV2.leverageCreateLoanIsApproved.bind(leverageZapV2), + createLoanApprove: leverageZapV2.leverageCreateLoanApprove.bind(leverageZapV2), + createLoanExpectedMetrics: leverageZapV2.leverageCreateLoanExpectedMetrics.bind(leverageZapV2), + createLoan: leverageZapV2.leverageCreateLoan.bind(leverageZapV2), + + borrowMoreMaxRecv: leverageZapV2.leverageBorrowMoreMaxRecv.bind(leverageZapV2), + borrowMoreExpectedCollateral: leverageZapV2.leverageBorrowMoreExpectedCollateral.bind(leverageZapV2), + borrowMoreIsApproved: leverageZapV2.leverageCreateLoanIsApproved.bind(leverageZapV2), + borrowMoreApprove: leverageZapV2.leverageCreateLoanApprove.bind(leverageZapV2), + borrowMoreExpectedMetrics: leverageZapV2.leverageBorrowMoreExpectedMetrics.bind(leverageZapV2), + borrowMore: leverageZapV2.leverageBorrowMore.bind(leverageZapV2), + + repayExpectedBorrowed: leverageZapV2.leverageRepayExpectedBorrowed.bind(leverageZapV2), + repayIsFull: leverageZapV2.leverageRepayIsFull.bind(leverageZapV2), + repayIsAvailable: leverageZapV2.leverageRepayIsAvailable.bind(leverageZapV2), + repayExpectedMetrics: leverageZapV2.leverageRepayExpectedMetrics.bind(leverageZapV2), + repayIsApproved: leverageZapV2.leverageRepayIsApproved.bind(leverageZapV2), + repayApprove: leverageZapV2.leverageRepayApprove.bind(leverageZapV2), + repay: leverageZapV2.leverageRepay.bind(leverageZapV2), + + estimateGas: { + createLoanApprove: leverageZapV2.leverageCreateLoanApproveEstimateGas.bind(leverageZapV2), + createLoan: leverageZapV2.leverageCreateLoanEstimateGas.bind(leverageZapV2), + + borrowMoreApprove: leverageZapV2.leverageCreateLoanApproveEstimateGas.bind(leverageZapV2), + borrowMore: leverageZapV2.leverageBorrowMoreEstimateGas.bind(leverageZapV2), + + repayApprove: leverageZapV2.leverageRepayApproveEstimateGas.bind(leverageZapV2), + repay: leverageZapV2.leverageRepayEstimateGas.bind(leverageZapV2), + }, + } + } private _getMarketId = (): number => Number(this.id.split("-").slice(-1)[0]); @@ -773,45 +821,45 @@ export class LendMarketTemplate { } private vaultRewardTokens = memoize(async (): Promise<{token: string, symbol: string, decimals: number}[]> => { - if (this.addresses.gauge === this.llamalend.constants.ZERO_ADDRESS) return [] + if (this.addresses.gauge === this.llamalend.constants.ZERO_ADDRESS) return [] - // if (useApi) { - // const rewards = await _getRewardsFromApi(); - // if (!rewards[this.addresses.gauge]) return []; - // rewards[this.addresses.gauge].forEach((r) => _setContracts(r.tokenAddress, ERC20Abi)); - // return rewards[this.addresses.gauge].map((r) => ({ token: r.tokenAddress, symbol: r.symbol, decimals: Number(r.decimals) })); - // } - - const gaugeContract = this.llamalend.contracts[this.addresses.gauge].contract; - const gaugeMulticallContract = this.llamalend.contracts[this.addresses.gauge].multicallContract; - const rewardCount = Number(this.llamalend.formatUnits(await gaugeContract.reward_count(this.llamalend.constantOptions), 0)); + // if (useApi) { + // const rewards = await _getRewardsFromApi(); + // if (!rewards[this.addresses.gauge]) return []; + // rewards[this.addresses.gauge].forEach((r) => _setContracts(r.tokenAddress, ERC20Abi)); + // return rewards[this.addresses.gauge].map((r) => ({ token: r.tokenAddress, symbol: r.symbol, decimals: Number(r.decimals) })); + // } - const tokenCalls = []; - for (let i = 0; i < rewardCount; i++) { - tokenCalls.push(gaugeMulticallContract.reward_tokens(i)); - } - const tokens = (await this.llamalend.multicallProvider.all(tokenCalls) as string[]) - .filter((addr) => addr !== this.llamalend.constants.ZERO_ADDRESS) - .map((addr) => addr.toLowerCase()) - .filter((addr) => this.llamalend.chainId === 1 || addr !== this.llamalend.constants.COINS.crv); + const gaugeContract = this.llamalend.contracts[this.addresses.gauge].contract; + const gaugeMulticallContract = this.llamalend.contracts[this.addresses.gauge].multicallContract; + const rewardCount = Number(this.llamalend.formatUnits(await gaugeContract.reward_count(this.llamalend.constantOptions), 0)); - const tokenInfoCalls = []; - for (const token of tokens) { - this.llamalend.setContract(token, ERC20Abi); - const tokenMulticallContract = this.llamalend.contracts[token].multicallContract; - tokenInfoCalls.push(tokenMulticallContract.symbol(), tokenMulticallContract.decimals()); - } - const tokenInfo = await this.llamalend.multicallProvider.all(tokenInfoCalls); - for (let i = 0; i < tokens.length; i++) { - this.llamalend.constants.DECIMALS[tokens[i]] = Number(tokenInfo[(i * 2) + 1]); - } + const tokenCalls = []; + for (let i = 0; i < rewardCount; i++) { + tokenCalls.push(gaugeMulticallContract.reward_tokens(i)); + } + const tokens = (await this.llamalend.multicallProvider.all(tokenCalls) as string[]) + .filter((addr) => addr !== this.llamalend.constants.ZERO_ADDRESS) + .map((addr) => addr.toLowerCase()) + .filter((addr) => this.llamalend.chainId === 1 || addr !== this.llamalend.constants.COINS.crv); + + const tokenInfoCalls = []; + for (const token of tokens) { + this.llamalend.setContract(token, ERC20Abi); + const tokenMulticallContract = this.llamalend.contracts[token].multicallContract; + tokenInfoCalls.push(tokenMulticallContract.symbol(), tokenMulticallContract.decimals()); + } + const tokenInfo = await this.llamalend.multicallProvider.all(tokenInfoCalls); + for (let i = 0; i < tokens.length; i++) { + this.llamalend.constants.DECIMALS[tokens[i]] = Number(tokenInfo[(i * 2) + 1]); + } - return tokens.map((token, i) => ({ token, symbol: tokenInfo[i * 2] as string, decimals: Number(tokenInfo[(i * 2) + 1]) })); - }, - { - promise: true, - maxAge: 30 * 60 * 1000, // 30m - }); + return tokens.map((token, i) => ({ token, symbol: tokenInfo[i * 2] as string, decimals: Number(tokenInfo[(i * 2) + 1]) })); + }, + { + promise: true, + maxAge: 30 * 60 * 1000, // 30m + }); private vaultRewardsApr = async (useApi = true): Promise => { if(useApi) { @@ -923,30 +971,30 @@ export class LendMarketTemplate { base_price: string, A: string, }> => { - const llammaContract = this.llamalend.contracts[this.addresses.amm].multicallContract; - const controllerContract = this.llamalend.contracts[this.addresses.controller].multicallContract; - - const calls = [ - llammaContract.fee(), - llammaContract.admin_fee(), - controllerContract.liquidation_discount(), - controllerContract.loan_discount(), - llammaContract.get_base_price(), - llammaContract.A(), - ] - - const [_fee, _admin_fee, _liquidation_discount, _loan_discount, _base_price, _A]: bigint[] = await this.llamalend.multicallProvider.all(calls) as bigint[]; - const A = formatUnits(_A, 0) - const base_price = formatUnits(_base_price) - const [fee, admin_fee, liquidation_discount, loan_discount] = [_fee, _admin_fee, _liquidation_discount, _loan_discount] - .map((_x) => formatUnits(_x * BigInt(100))); - - return { fee, admin_fee, liquidation_discount, loan_discount, base_price, A } - }, - { - promise: true, - maxAge: 5 * 60 * 1000, // 5m - }); + const llammaContract = this.llamalend.contracts[this.addresses.amm].multicallContract; + const controllerContract = this.llamalend.contracts[this.addresses.controller].multicallContract; + + const calls = [ + llammaContract.fee(), + llammaContract.admin_fee(), + controllerContract.liquidation_discount(), + controllerContract.loan_discount(), + llammaContract.get_base_price(), + llammaContract.A(), + ] + + const [_fee, _admin_fee, _liquidation_discount, _loan_discount, _base_price, _A]: bigint[] = await this.llamalend.multicallProvider.all(calls) as bigint[]; + const A = formatUnits(_A, 0) + const base_price = formatUnits(_base_price) + const [fee, admin_fee, liquidation_discount, loan_discount] = [_fee, _admin_fee, _liquidation_discount, _loan_discount] + .map((_x) => formatUnits(_x * BigInt(100))); + + return { fee, admin_fee, liquidation_discount, loan_discount, base_price, A } + }, + { + promise: true, + maxAge: 5 * 60 * 1000, // 5m + }); private _getRate = async (isGetter = true): Promise => { let _rate; @@ -1040,23 +1088,23 @@ export class LendMarketTemplate { } private statsBandsInfo = memoize(async (): Promise<{ activeBand: number, maxBand: number, minBand: number, liquidationBand: number | null }> => { - const ammContract = this.llamalend.contracts[this.addresses.amm].multicallContract; - const calls = [ - ammContract.active_band_with_skip(), - ammContract.max_band(), - ammContract.min_band(), - ] - - const [activeBand, maxBand, minBand] = (await this.llamalend.multicallProvider.all(calls) as bigint[]).map((_b) => Number(_b)); - const { borrowed, collateral } = await this.statsBandBalances(activeBand); - let liquidationBand = null; - if (Number(borrowed) > 0 && Number(collateral) > 0) liquidationBand = activeBand; - return { activeBand, maxBand, minBand, liquidationBand } - }, - { - promise: true, - maxAge: 60 * 1000, // 1m - }); + const ammContract = this.llamalend.contracts[this.addresses.amm].multicallContract; + const calls = [ + ammContract.active_band_with_skip(), + ammContract.max_band(), + ammContract.min_band(), + ] + + const [activeBand, maxBand, minBand] = (await this.llamalend.multicallProvider.all(calls) as bigint[]).map((_b) => Number(_b)); + const { borrowed, collateral } = await this.statsBandBalances(activeBand); + let liquidationBand = null; + if (Number(borrowed) > 0 && Number(collateral) > 0) liquidationBand = activeBand; + return { activeBand, maxBand, minBand, liquidationBand } + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); private async statsBandBalances(n: number): Promise<{ borrowed: string, collateral: string }> { const ammContract = this.llamalend.contracts[this.addresses.amm].multicallContract; @@ -1207,31 +1255,31 @@ export class LendMarketTemplate { // ---------------- PRICES ---------------- public A = memoize(async(): Promise => { - const _A = await this.llamalend.contracts[this.addresses.amm].contract.A(this.llamalend.constantOptions) as bigint; - return formatUnits(_A, 0); - }, - { - promise: true, - maxAge: 86400 * 1000, // 1d - }); + const _A = await this.llamalend.contracts[this.addresses.amm].contract.A(this.llamalend.constantOptions) as bigint; + return formatUnits(_A, 0); + }, + { + promise: true, + maxAge: 86400 * 1000, // 1d + }); public basePrice = memoize(async(): Promise => { - const _price = await this.llamalend.contracts[this.addresses.amm].contract.get_base_price(this.llamalend.constantOptions) as bigint; - return formatUnits(_price); - }, - { - promise: true, - maxAge: 86400 * 1000, // 1d - }); + const _price = await this.llamalend.contracts[this.addresses.amm].contract.get_base_price(this.llamalend.constantOptions) as bigint; + return formatUnits(_price); + }, + { + promise: true, + maxAge: 86400 * 1000, // 1d + }); public oraclePrice = memoize(async (): Promise => { - const _price = await this.llamalend.contracts[this.addresses.amm].contract.price_oracle(this.llamalend.constantOptions) as bigint; - return formatUnits(_price); - }, - { - promise: true, - maxAge: 60 * 1000, // 1m - }); + const _price = await this.llamalend.contracts[this.addresses.amm].contract.price_oracle(this.llamalend.constantOptions) as bigint; + return formatUnits(_price); + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); public async oraclePriceBand(): Promise { const oraclePriceBN = BN(await this.oraclePrice()); @@ -1300,16 +1348,16 @@ export class LendMarketTemplate { } public _userState = memoize(async (address = ""): Promise<{ _collateral: bigint, _borrowed: bigint, _debt: bigint, _N: bigint }> => { - address = _getAddress.call(this.llamalend, address); - const contract = this.llamalend.contracts[this.addresses.controller].contract; - const [_collateral, _borrowed, _debt, _N] = await contract.user_state(address, this.llamalend.constantOptions) as bigint[]; + address = _getAddress.call(this.llamalend, address); + const contract = this.llamalend.contracts[this.addresses.controller].contract; + const [_collateral, _borrowed, _debt, _N] = await contract.user_state(address, this.llamalend.constantOptions) as bigint[]; - return { _collateral, _borrowed, _debt, _N } - }, - { - promise: true, - maxAge: 10 * 1000, // 10s - }); + return { _collateral, _borrowed, _debt, _N } + }, + { + promise: true, + maxAge: 10 * 1000, // 10s + }); public async userState(address = ""): Promise<{ collateral: string, borrowed: string, debt: string, N: string }> { const { _collateral, _borrowed, _debt, _N } = await this._userState(address); @@ -1404,7 +1452,7 @@ export class LendMarketTemplate { // ---------------- CREATE LOAN ---------------- - private _checkRange(range: number): void { + public _checkRange(range: number): void { if (range < this.minBands) throw Error(`range must be >= ${this.minBands}`); if (range > this.maxBands) throw Error(`range must be <= ${this.maxBands}`); } @@ -1418,25 +1466,25 @@ export class LendMarketTemplate { } public createLoanMaxRecvAllRanges = memoize(async (collateral: number | string): Promise<{ [index: number]: string }> => { - const _collateral = parseUnits(collateral, this.collateral_token.decimals); + const _collateral = parseUnits(collateral, this.collateral_token.decimals); - const calls = []; - for (let N = this.minBands; N <= this.maxBands; N++) { - calls.push(this.llamalend.contracts[this.addresses.controller].multicallContract.max_borrowable(_collateral, N, 0)); - } - const _amounts = await this.llamalend.multicallProvider.all(calls) as bigint[]; + const calls = []; + for (let N = this.minBands; N <= this.maxBands; N++) { + calls.push(this.llamalend.contracts[this.addresses.controller].multicallContract.max_borrowable(_collateral, N, 0)); + } + const _amounts = await this.llamalend.multicallProvider.all(calls) as bigint[]; - const res: { [index: number]: string } = {}; - for (let N = this.minBands; N <= this.maxBands; N++) { - res[N] = formatUnits(_amounts[N - this.minBands], this.borrowed_token.decimals); - } + const res: { [index: number]: string } = {}; + for (let N = this.minBands; N <= this.maxBands; N++) { + res[N] = formatUnits(_amounts[N - this.minBands], this.borrowed_token.decimals); + } - return res; - }, - { - promise: true, - maxAge: 5 * 60 * 1000, // 5m - }); + return res; + }, + { + promise: true, + maxAge: 5 * 60 * 1000, // 5m + }); public async getMaxRange(collateral: number | string, debt: number | string): Promise { const maxRecv = await this.createLoanMaxRecvAllRanges(collateral); @@ -1460,7 +1508,7 @@ export class LendMarketTemplate { return await this.llamalend.multicallProvider.all(calls) as bigint[]; } - private async _getPrices(_n2: bigint, _n1: bigint): Promise { + public async _getPrices(_n2: bigint, _n1: bigint): Promise { const contract = this.llamalend.contracts[this.addresses.amm].multicallContract; return (await this.llamalend.multicallProvider.all([ contract.p_oracle_down(_n2), @@ -1468,7 +1516,7 @@ export class LendMarketTemplate { ]) as bigint[]).map((_p) => formatUnits(_p)); } - private async _calcPrices(_n2: bigint, _n1: bigint): Promise<[string, string]> { + public async _calcPrices(_n2: bigint, _n1: bigint): Promise<[string, string]> { return [await this.calcTickPrice(Number(_n2) + 1), await this.calcTickPrice(Number(_n1))]; } @@ -2078,13 +2126,13 @@ export class LendMarketTemplate { public async calcPartialFrac(amount: TAmount, address = ""): Promise { address = _getAddress.call(this.llamalend, address); const tokensToLiquidate = await this.tokensToLiquidate(address); - + const amountBN = BN(amount); const tokensToLiquidateBN = BN(tokensToLiquidate); - + if (amountBN.gt(tokensToLiquidateBN)) throw Error("Amount cannot be greater than total tokens to liquidate"); if (amountBN.lte(0)) throw Error("Amount must be greater than 0"); - + // Calculate frac = amount / tokensToLiquidate * 10**18 // 100% = 10**18 const fracDecimalBN = amountBN.div(tokensToLiquidateBN); @@ -2136,33 +2184,33 @@ export class LendMarketTemplate { if (slippage > 100) throw Error("Slippage must be <= 100"); if (Number(currentDebt) === 0) throw Error(`Loan for ${address} does not exist`); if (Number(borrowed) === 0) throw Error(`User ${address} is not in liquidation mode`); - + const frac = partialFrac.frac; const fracBN = BN(partialFrac.fracDecimal); - + const borrowedBN = BN(borrowed); const expectedBorrowedBN = borrowedBN.times(fracBN); const minAmountBN = expectedBorrowedBN.times(100 - slippage).div(100); const _minAmount = fromBN(minAmountBN); - + const contract = this.llamalend.contracts[this.addresses.controller].contract; const gas = (await contract.liquidate_extended.estimateGas( - address, - _minAmount, - frac, + address, + _minAmount, + frac, this.llamalend.constants.ZERO_ADDRESS, [], this.llamalend.constantOptions )); - + if (estimateGas) return smartNumber(gas); await this.llamalend.updateFeeData(); const gasLimit = _mulBy1_3(DIGas(gas)); return (await contract.liquidate_extended( - address, - _minAmount, - frac, + address, + _minAmount, + frac, this.llamalend.constants.ZERO_ADDRESS, [], { ...this.llamalend.options, gasLimit } @@ -2331,78 +2379,78 @@ export class LendMarketTemplate { maxLeverage: string, avgPrice: string, }>> => { - this._checkLeverageZap(); - const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); - const contract = this.llamalend.contracts[this.llamalend.constants.ALIASES.leverage_zap].multicallContract; - - const oraclePriceBand = await this.oraclePriceBand(); - const pAvgApproxBN = BN(await this.calcTickPrice(oraclePriceBand)); // upper tick of oracle price band - let pAvgBN: BigNumber | null = null; - const arrLength = this.maxBands - this.minBands + 1; - let maxLeverageCollateralBN: BigNumber[] = new Array(arrLength).fill(BN(0)); - let _maxLeverageCollateral: bigint[] = new Array(arrLength).fill(BigInt(0)); - let maxBorrowablePrevBN: BigNumber[] = new Array(arrLength).fill(BN(0)); - let maxBorrowableBN: BigNumber[] = new Array(arrLength).fill(BN(0)); - let _maxBorrowable: bigint[] = new Array(arrLength).fill(BigInt(0)); + this._checkLeverageZap(); + const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); + const contract = this.llamalend.contracts[this.llamalend.constants.ALIASES.leverage_zap].multicallContract; + + const oraclePriceBand = await this.oraclePriceBand(); + const pAvgApproxBN = BN(await this.calcTickPrice(oraclePriceBand)); // upper tick of oracle price band + let pAvgBN: BigNumber | null = null; + const arrLength = this.maxBands - this.minBands + 1; + let maxLeverageCollateralBN: BigNumber[] = new Array(arrLength).fill(BN(0)); + let _maxLeverageCollateral: bigint[] = new Array(arrLength).fill(BigInt(0)); + let maxBorrowablePrevBN: BigNumber[] = new Array(arrLength).fill(BN(0)); + let maxBorrowableBN: BigNumber[] = new Array(arrLength).fill(BN(0)); + let _maxBorrowable: bigint[] = new Array(arrLength).fill(BigInt(0)); + + for (let i = 0; i < 5; i++) { + const pBN = pAvgBN ?? pAvgApproxBN; + maxBorrowablePrevBN = maxBorrowableBN; + const _userEffectiveCollateral: bigint = _userCollateral + fromBN(BN(userBorrowed).div(pBN), this.collateral_token.decimals); + const calls = []; + for (let N = this.minBands; N <= this.maxBands; N++) { + const j = N - this.minBands; + calls.push(contract.max_borrowable(this.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral[j], N, fromBN(pBN))); + } + _maxBorrowable = (await this.llamalend.multicallProvider.all(calls) as bigint[]).map((_mb) => _mb * BigInt(998) / BigInt(1000)); + maxBorrowableBN = _maxBorrowable.map((_mb) => toBN(_mb, this.borrowed_token.decimals)); - for (let i = 0; i < 5; i++) { - const pBN = pAvgBN ?? pAvgApproxBN; - maxBorrowablePrevBN = maxBorrowableBN; - const _userEffectiveCollateral: bigint = _userCollateral + fromBN(BN(userBorrowed).div(pBN), this.collateral_token.decimals); - const calls = []; - for (let N = this.minBands; N <= this.maxBands; N++) { - const j = N - this.minBands; - calls.push(contract.max_borrowable(this.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral[j], N, fromBN(pBN))); - } - _maxBorrowable = (await this.llamalend.multicallProvider.all(calls) as bigint[]).map((_mb) => _mb * BigInt(998) / BigInt(1000)); - maxBorrowableBN = _maxBorrowable.map((_mb) => toBN(_mb, this.borrowed_token.decimals)); + const deltaBN = maxBorrowableBN.map((mb, l) => mb.minus(maxBorrowablePrevBN[l]).abs().div(mb)); + if (BigNumber.max(...deltaBN).lt(0.0005)) { + maxBorrowableBN = maxBorrowablePrevBN; + break; + } - const deltaBN = maxBorrowableBN.map((mb, l) => mb.minus(maxBorrowablePrevBN[l]).abs().div(mb)); - if (BigNumber.max(...deltaBN).lt(0.0005)) { - maxBorrowableBN = maxBorrowablePrevBN; - break; - } + if (pAvgBN === null){ + const _y = BigInt(await _getExpectedOdos.call(this.llamalend, this.addresses.borrowed_token, this.addresses.collateral_token, _maxBorrowable[0], this.addresses.amm)); + const yBN = toBN(_y, this.collateral_token.decimals); + pAvgBN = maxBorrowableBN[0].div(yBN); + } - if (pAvgBN === null){ - const _y = BigInt(await _getExpectedOdos.call(this.llamalend, this.addresses.borrowed_token, this.addresses.collateral_token, _maxBorrowable[0], this.addresses.amm)); - const yBN = toBN(_y, this.collateral_token.decimals); - pAvgBN = maxBorrowableBN[0].div(yBN); + maxLeverageCollateralBN = maxBorrowableBN.map((mb) => mb.div(pAvgBN as BigNumber)); + _maxLeverageCollateral = maxLeverageCollateralBN.map((mlc) => fromBN(mlc, this.collateral_token.decimals)); } - maxLeverageCollateralBN = maxBorrowableBN.map((mb) => mb.div(pAvgBN as BigNumber)); - _maxLeverageCollateral = maxLeverageCollateralBN.map((mlc) => fromBN(mlc, this.collateral_token.decimals)); - } - - const userEffectiveCollateralBN = BN(userCollateral).plus(BN(userBorrowed).div(pAvgBN as BigNumber)); + const userEffectiveCollateralBN = BN(userCollateral).plus(BN(userBorrowed).div(pAvgBN as BigNumber)); - const res: IDict<{ - maxDebt: string, - maxTotalCollateral: string, - userCollateral: string, - collateralFromUserBorrowed: string, - collateralFromMaxDebt: string, - maxLeverage: string, - avgPrice: string, - }> = {}; - for (let N = this.minBands; N <= this.maxBands; N++) { - const j = N - this.minBands; - res[N] = { - maxDebt: formatNumber(maxBorrowableBN[j].toString(), this.borrowed_token.decimals), - maxTotalCollateral: formatNumber(maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).toString(), this.collateral_token.decimals), - userCollateral: formatNumber(userCollateral, this.collateral_token.decimals), - collateralFromUserBorrowed: formatNumber(BN(userBorrowed).div(pAvgBN as BigNumber).toString(), this.collateral_token.decimals), - collateralFromMaxDebt: formatNumber(maxLeverageCollateralBN[j].toString(), this.collateral_token.decimals), - maxLeverage: maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).div(userEffectiveCollateralBN).toString(), - avgPrice: (pAvgBN as BigNumber).toString(), - }; - } + const res: IDict<{ + maxDebt: string, + maxTotalCollateral: string, + userCollateral: string, + collateralFromUserBorrowed: string, + collateralFromMaxDebt: string, + maxLeverage: string, + avgPrice: string, + }> = {}; + for (let N = this.minBands; N <= this.maxBands; N++) { + const j = N - this.minBands; + res[N] = { + maxDebt: formatNumber(maxBorrowableBN[j].toString(), this.borrowed_token.decimals), + maxTotalCollateral: formatNumber(maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).toString(), this.collateral_token.decimals), + userCollateral: formatNumber(userCollateral, this.collateral_token.decimals), + collateralFromUserBorrowed: formatNumber(BN(userBorrowed).div(pAvgBN as BigNumber).toString(), this.collateral_token.decimals), + collateralFromMaxDebt: formatNumber(maxLeverageCollateralBN[j].toString(), this.collateral_token.decimals), + maxLeverage: maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).div(userEffectiveCollateralBN).toString(), + avgPrice: (pAvgBN as BigNumber).toString(), + }; + } - return res; - }, - { - promise: true, - maxAge: 60 * 1000, // 1m - }); + return res; + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); private _setSwapDataToCache = async (inputCoinAddress: string, outputCoinAddress: string, _amount: bigint, slippage: number) => { let swapData = await _getQuoteOdos.call(this.llamalend, inputCoinAddress, outputCoinAddress, _amount, this.addresses.amm, true, slippage); @@ -2483,36 +2531,36 @@ export class LendMarketTemplate { } private _leverageCalcN1 = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, user?: string): Promise => { - if (range > 0) this._checkRange(range); - let _stateDebt = BigInt(0); - if (user) { - const { _debt, _borrowed, _N } = await this._userState(user); - if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); - _stateDebt = _debt; - if (range < 0) range = Number(this.llamalend.formatUnits(_N, 0)); - } - const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt, user); - const _debt = _stateDebt + parseUnits(debt, this.borrowed_token.decimals); - return await this.llamalend.contracts[this.addresses.controller].contract.calculate_debt_n1(_futureStateCollateral, _debt, range, this.llamalend.constantOptions); - }, - { - promise: true, - maxAge: 60 * 1000, // 1m - }); + if (range > 0) this._checkRange(range); + let _stateDebt = BigInt(0); + if (user) { + const { _debt, _borrowed, _N } = await this._userState(user); + if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); + _stateDebt = _debt; + if (range < 0) range = Number(this.llamalend.formatUnits(_N, 0)); + } + const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt, user); + const _debt = _stateDebt + parseUnits(debt, this.borrowed_token.decimals); + return await this.llamalend.contracts[this.addresses.controller].contract.calculate_debt_n1(_futureStateCollateral, _debt, range, this.llamalend.constantOptions); + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); private _leverageCalcN1AllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, maxN: number): Promise => { - const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt); - const _debt = parseUnits(debt, this.borrowed_token.decimals); - const calls = []; - for (let N = this.minBands; N <= maxN; N++) { - calls.push(this.llamalend.contracts[this.addresses.controller].multicallContract.calculate_debt_n1(_futureStateCollateral, _debt, N)); - } - return await this.llamalend.multicallProvider.all(calls) as bigint[]; - }, - { - promise: true, - maxAge: 60 * 1000, // 1m - }); + const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt); + const _debt = parseUnits(debt, this.borrowed_token.decimals); + const calls = []; + for (let N = this.minBands; N <= maxN; N++) { + calls.push(this.llamalend.contracts[this.addresses.controller].multicallContract.calculate_debt_n1(_futureStateCollateral, _debt, N)); + } + return await this.llamalend.multicallProvider.all(calls) as bigint[]; + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); private async _leverageBands(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, user?: string): Promise<[bigint, bigint]> { const _n1 = await this._leverageCalcN1(userCollateral, userBorrowed, debt, range, user); @@ -2783,7 +2831,7 @@ export class LendMarketTemplate { const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); await this._setSwapDataToCache(this.addresses.borrowed_token, this.addresses.collateral_token, _dDebt + _userBorrowed, slippage); const { _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, avgPrice } = - await this._leverageExpectedCollateral(userCollateral, userBorrowed, dDebt, address); + await this._leverageExpectedCollateral(userCollateral, userBorrowed, dDebt, address); return { totalCollateral: formatUnits(_totalCollateral, this.collateral_token.decimals), userCollateral: formatUnits(_userCollateral, this.collateral_token.decimals), @@ -2960,30 +3008,30 @@ export class LendMarketTemplate { } private _leverageRepayBands = memoize( async (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address: string): Promise<[bigint, bigint]> => { - address = _getAddress.call(this.llamalend, address); - if (!(await this.leverageRepayIsAvailable(stateCollateral, userCollateral, userBorrowed, address))) return [parseUnits(0, 0), parseUnits(0, 0)]; - - const _stateRepayCollateral = parseUnits(stateCollateral, this.collateral_token.decimals); - const { _collateral: _stateCollateral, _debt: _stateDebt, _N } = await this._userState(address); - if (_stateDebt == BigInt(0)) throw Error(`Loan for ${address} does not exist`); - if (_stateCollateral < _stateRepayCollateral) throw Error(`Can't use more collateral than user's position has (${_stateRepayCollateral}) > ${_stateCollateral})`); - - let _n1 = parseUnits(0, 0); - let _n2 = parseUnits(0, 0); - const { _totalBorrowed: _repayExpected } = this._leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed); - try { - _n1 = await this.llamalend.contracts[this.addresses.controller].contract.calculate_debt_n1(_stateCollateral - _stateRepayCollateral, _stateDebt - _repayExpected, _N); - _n2 = _n1 + (_N - BigInt(1)); - } catch { - console.log("Full repayment"); - } + address = _getAddress.call(this.llamalend, address); + if (!(await this.leverageRepayIsAvailable(stateCollateral, userCollateral, userBorrowed, address))) return [parseUnits(0, 0), parseUnits(0, 0)]; + + const _stateRepayCollateral = parseUnits(stateCollateral, this.collateral_token.decimals); + const { _collateral: _stateCollateral, _debt: _stateDebt, _N } = await this._userState(address); + if (_stateDebt == BigInt(0)) throw Error(`Loan for ${address} does not exist`); + if (_stateCollateral < _stateRepayCollateral) throw Error(`Can't use more collateral than user's position has (${_stateRepayCollateral}) > ${_stateCollateral})`); + + let _n1 = parseUnits(0, 0); + let _n2 = parseUnits(0, 0); + const { _totalBorrowed: _repayExpected } = this._leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed); + try { + _n1 = await this.llamalend.contracts[this.addresses.controller].contract.calculate_debt_n1(_stateCollateral - _stateRepayCollateral, _stateDebt - _repayExpected, _N); + _n2 = _n1 + (_N - BigInt(1)); + } catch { + console.log("Full repayment"); + } - return [_n2, _n1]; - }, - { - promise: true, - maxAge: 5 * 60 * 1000, // 5m - }); + return [_n2, _n1]; + }, + { + promise: true, + maxAge: 5 * 60 * 1000, // 5m + }); private async leverageRepayBands(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise<[number, number]> { this._checkLeverageZap(); @@ -3189,7 +3237,7 @@ export class LendMarketTemplate { public async forceUpdateUserState(newTx: string, userAddress?: string): Promise { const address = userAddress || this.llamalend.signerAddress; if (!address) throw Error("Need to connect wallet or pass address into args"); - + await _getUserCollateralForce( this.llamalend.constants.NETWORK_NAME, this.addresses.controller, @@ -3197,4 +3245,8 @@ export class LendMarketTemplate { newTx ); } -} + + public getLlamalend(): Llamalend { + return this.llamalend; + } +} \ No newline at end of file diff --git a/src/lendMarkets/interfaces/leverageZapV2.ts b/src/lendMarkets/interfaces/leverageZapV2.ts new file mode 100644 index 0000000..8111e8a --- /dev/null +++ b/src/lendMarkets/interfaces/leverageZapV2.ts @@ -0,0 +1,73 @@ +import {GetExpectedFn, IDict, ILeverageMetrics, IQuote, TAmount, TGas} from "../../interfaces.js"; + +export interface ILeverageZapV2 { + hasLeverage: () => boolean, + + maxLeverage: (N: number) => Promise, + + createLoanMaxRecv: (userCollateral: TAmount, userBorrowed: TAmount, range: number, getExpected: GetExpectedFn) => + Promise<{ + maxDebt: string, + maxTotalCollateral: string, + userCollateral: string, + collateralFromUserBorrowed: string, + collateralFromMaxDebt: string, + maxLeverage: string, + avgPrice: string, + }>, + createLoanMaxRecvAllRanges: (userCollateral: TAmount, userBorrowed: TAmount, getExpected: GetExpectedFn) => + Promise>, + createLoanExpectedCollateral: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, quote: IQuote) => + Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, leverage: string, avgPrice: string }>, + createLoanExpectedMetrics: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, quote: IQuote, healthIsFull?: boolean) => Promise, + createLoanMaxRange: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, getExpected: GetExpectedFn) => Promise, + createLoanBandsAllRanges: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, getExpected: GetExpectedFn, quote: IQuote) => Promise>, + createLoanPricesAllRanges: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, getExpected: GetExpectedFn, quote: IQuote) => Promise>, + createLoanIsApproved: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + createLoanApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + createLoan: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, router: string, calldata: string) => Promise, + + borrowMoreMaxRecv: (userCollateral: TAmount, userBorrowed: TAmount, getExpected: GetExpectedFn, address?: string) => + Promise<{ + maxDebt: string, + maxTotalCollateral: string, + userCollateral: string, + collateralFromUserBorrowed: string, + collateralFromMaxDebt: string, + avgPrice: string, + }>, + borrowMoreExpectedCollateral: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, quote: IQuote, address?: string) => + Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, avgPrice: string }>, + borrowMoreExpectedMetrics: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, quote: IQuote, healthIsFull?: boolean, address?: string) => Promise, + borrowMoreIsApproved: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + borrowMoreApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + borrowMore: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, router: string, calldata: string) => Promise, + + repayExpectedBorrowed: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote) => + Promise<{ totalBorrowed: string, borrowedFromStateCollateral: string, borrowedFromUserCollateral: string, userBorrowed: string, avgPrice: string }>, + repayIsFull: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote, address?: string) => Promise, + repayIsAvailable: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote, address?: string) => Promise, + repayExpectedMetrics: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, healthIsFull: boolean, quote: IQuote, address: string) => Promise, + repayIsApproved: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + repayApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + repay: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, router: string, calldata: string) => Promise, + + estimateGas: { + createLoanApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + createLoan: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, router: string, calldata: string) => Promise, + + borrowMoreApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + borrowMore: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, router: string, calldata: string) => Promise, + + repayApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + repay: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, router: string, calldata: string) => Promise, + } +} diff --git a/src/lendMarkets/modules/index.ts b/src/lendMarkets/modules/index.ts new file mode 100644 index 0000000..496aeea --- /dev/null +++ b/src/lendMarkets/modules/index.ts @@ -0,0 +1 @@ +export { LeverageZapV2Module } from './leverageZapV2.js'; \ No newline at end of file diff --git a/src/lendMarkets/modules/leverageZapV2.ts b/src/lendMarkets/modules/leverageZapV2.ts new file mode 100644 index 0000000..d5ce710 --- /dev/null +++ b/src/lendMarkets/modules/leverageZapV2.ts @@ -0,0 +1,849 @@ +import memoize from "memoizee"; +import type { TAmount, TGas, IDict, IQuote, ILeverageMetrics, GetExpectedFn } from "../../interfaces.js"; +import type { LendMarketTemplate } from "../LendMarketTemplate.js"; +import { + _getAddress, + parseUnits, + BN, + toBN, + fromBN, + ensureAllowance, + hasAllowance, + ensureAllowanceEstimateGas, + formatUnits, + smartNumber, + formatNumber, + _mulBy1_3, + DIGas, + buildCalldataForLeverageZapV2, +} from "../../utils.js"; +import {Llamalend} from "../../llamalend.js"; +import BigNumber from "bignumber.js"; + +/** + * LeverageZapV2 module for LendMarketTemplate + */ +export class LeverageZapV2Module { + private market: LendMarketTemplate; + private llamalend: Llamalend; + + constructor(market: LendMarketTemplate) { + this.market = market; + this.llamalend = market.getLlamalend(); + } + + private _getMarketId = (): number => Number(this.market.id.split("-").slice(-1)[0]); + + // ============ CREATE LOAN METHODS ============ + + public hasLeverage = (): boolean => { + return this.llamalend.constants.ALIASES.leverage_zap_v2 !== this.llamalend.constants.ZERO_ADDRESS && + this._getMarketId() >= Number(this.llamalend.constants.ALIASES["leverage_markets_start_id"]); + } + + public _checkLeverageZap(): void { + if (!this.hasLeverage()) { + throw Error("This market does not support leverage"); + } + } + + public async _get_k_effective_BN(N: number): Promise { + // d_k_effective: uint256 = (1 - loan_discount) * sqrt((A-1)/A) / N + // k_effective = d_k_effective * sum_{0..N-1}(((A-1) / A)**k) + const { loan_discount, A } = await this.market.stats.parameters(); + const A_BN = BN(A); + const A_ratio_BN = A_BN.minus(1).div(A_BN); + + const d_k_effective_BN = BN(100).minus(loan_discount).div(100).times(A_ratio_BN.sqrt()).div(N); + let S = BN(0); + for (let n = 0; n < N; n++) { + S = S.plus(A_ratio_BN.pow(n)) + } + + return d_k_effective_BN.times(S); + } + + public async maxLeverage(N: number): Promise { + // max_leverage = 1 / (k_effective - 1) + const k_effective_BN = await this._get_k_effective_BN(N); + + return BN(1).div(BN(1).minus(k_effective_BN)).toString() + } + + public async leverageCreateLoanMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, range: number, getExpected: GetExpectedFn): + Promise<{ + maxDebt: string, + maxTotalCollateral: string, + userCollateral: string, + collateralFromUserBorrowed: string, + collateralFromMaxDebt: string, + maxLeverage: string, + avgPrice: string, + }> { + // max_borrowable = userCollateral / (1 / (k_effective * max_p_base) - 1 / p_avg) + this._checkLeverageZap(); + if (range > 0) this.market._checkRange(range); + const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals); + const _userBorrowed = parseUnits(userBorrowed, this.market.borrowed_token.decimals); + + const oraclePriceBand = await this.market.oraclePriceBand(); + let pAvgBN = BN(await this.market.calcTickPrice(oraclePriceBand)); // upper tick of oracle price band + let maxBorrowablePrevBN = BN(0); + let maxBorrowableBN = BN(0); + let _userEffectiveCollateral = BigInt(0); + let _maxLeverageCollateral = BigInt(0); + + const contract = this.llamalend.contracts[this.llamalend.constants.ALIASES.leverage_zap_v2].contract; + for (let i = 0; i < 5; i++) { + maxBorrowablePrevBN = maxBorrowableBN; + _userEffectiveCollateral = _userCollateral + fromBN(BN(userBorrowed).div(pAvgBN), this.market.collateral_token.decimals); + let _maxBorrowable = await contract.max_borrowable(this.market.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral, range, fromBN(pAvgBN)); + _maxBorrowable = _maxBorrowable * BigInt(998) / BigInt(1000) + if (_maxBorrowable === BigInt(0)) break; + maxBorrowableBN = toBN(_maxBorrowable, this.market.borrowed_token.decimals); + + if (maxBorrowableBN.minus(maxBorrowablePrevBN).abs().div(maxBorrowablePrevBN).lt(0.0005)) { + maxBorrowableBN = maxBorrowablePrevBN; + break; + } + + // additionalCollateral = (userBorrowed / p) + leverageCollateral + const _maxAdditionalCollateral = BigInt((await getExpected( + this.market.addresses.borrowed_token, + this.market.addresses.collateral_token, + _maxBorrowable + _userBorrowed, + this.market.addresses.amm + )).outAmount); + + pAvgBN = maxBorrowableBN.plus(userBorrowed).div(toBN(_maxAdditionalCollateral, this.market.collateral_token.decimals)); + _maxLeverageCollateral = _maxAdditionalCollateral - fromBN(BN(userBorrowed).div(pAvgBN), this.market.collateral_token.decimals); + } + + const userEffectiveCollateralBN = maxBorrowableBN.gt(0) ? toBN(_userEffectiveCollateral, this.market.collateral_token.decimals) : BN(0); + const maxLeverageCollateralBN = toBN(_maxLeverageCollateral, this.market.collateral_token.decimals); + + return { + maxDebt: formatNumber(maxBorrowableBN.toString(), this.market.borrowed_token.decimals), + maxTotalCollateral: formatNumber(maxLeverageCollateralBN.plus(userEffectiveCollateralBN).toString(), this.market.collateral_token.decimals), + userCollateral: formatNumber(userCollateral, this.market.collateral_token.decimals), + collateralFromUserBorrowed: formatNumber(BN(userBorrowed).div(pAvgBN).toString(), this.market.collateral_token.decimals), + collateralFromMaxDebt: formatNumber(maxLeverageCollateralBN.toString(), this.market.collateral_token.decimals), + maxLeverage: maxLeverageCollateralBN.plus(userEffectiveCollateralBN).div(userEffectiveCollateralBN).toString(), + avgPrice: pAvgBN.toString(), + }; + } + + public leverageCreateLoanMaxRecvAllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, getExpected: GetExpectedFn): + Promise> => { + this._checkLeverageZap(); + const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals); + const contract = this.llamalend.contracts[this.llamalend.constants.ALIASES.leverage_zap_v2].multicallContract; + + const oraclePriceBand = await this.market.oraclePriceBand(); + const pAvgApproxBN = BN(await this.market.calcTickPrice(oraclePriceBand)); // upper tick of oracle price band + let pAvgBN: BigNumber | null = null; + const arrLength = this.market.maxBands - this.market.minBands + 1; + let maxLeverageCollateralBN: BigNumber[] = new Array(arrLength).fill(BN(0)); + let _maxLeverageCollateral: bigint[] = new Array(arrLength).fill(BigInt(0)); + let maxBorrowablePrevBN: BigNumber[] = new Array(arrLength).fill(BN(0)); + let maxBorrowableBN: BigNumber[] = new Array(arrLength).fill(BN(0)); + let _maxBorrowable: bigint[] = new Array(arrLength).fill(BigInt(0)); + + for (let i = 0; i < 5; i++) { + const pBN = pAvgBN ?? pAvgApproxBN; + maxBorrowablePrevBN = maxBorrowableBN; + const _userEffectiveCollateral: bigint = _userCollateral + fromBN(BN(userBorrowed).div(pBN), this.market.collateral_token.decimals); + const calls = []; + for (let N = this.market.minBands; N <= this.market.maxBands; N++) { + const j = N - this.market.minBands; + calls.push(contract.max_borrowable(this.market.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral[j], N, fromBN(pBN))); + } + _maxBorrowable = (await this.llamalend.multicallProvider.all(calls) as bigint[]).map((_mb) => _mb * BigInt(998) / BigInt(1000)); + maxBorrowableBN = _maxBorrowable.map((_mb) => toBN(_mb, this.market.borrowed_token.decimals)); + + const deltaBN = maxBorrowableBN.map((mb, l) => mb.minus(maxBorrowablePrevBN[l]).abs().div(mb)); + if (BigNumber.max(...deltaBN).lt(0.0005)) { + maxBorrowableBN = maxBorrowablePrevBN; + break; + } + + if (pAvgBN === null){ + const _y = BigInt((await getExpected( + this.market.addresses.borrowed_token, + this.market.addresses.collateral_token, + _maxBorrowable[0], + this.market.addresses.amm + )).outAmount); + const yBN = toBN(_y, this.market.collateral_token.decimals); + pAvgBN = maxBorrowableBN[0].div(yBN); + } + + maxLeverageCollateralBN = maxBorrowableBN.map((mb) => mb.div(pAvgBN as BigNumber)); + _maxLeverageCollateral = maxLeverageCollateralBN.map((mlc) => fromBN(mlc, this.market.collateral_token.decimals)); + } + + const userEffectiveCollateralBN = BN(userCollateral).plus(BN(userBorrowed).div(pAvgBN as BigNumber)); + + const res: IDict<{ + maxDebt: string, + maxTotalCollateral: string, + userCollateral: string, + collateralFromUserBorrowed: string, + collateralFromMaxDebt: string, + maxLeverage: string, + avgPrice: string, + }> = {}; + for (let N = this.market.minBands; N <= this.market.maxBands; N++) { + const j = N - this.market.minBands; + res[N] = { + maxDebt: formatNumber(maxBorrowableBN[j].toString(), this.market.borrowed_token.decimals), + maxTotalCollateral: formatNumber(maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).toString(), this.market.collateral_token.decimals), + userCollateral: formatNumber(userCollateral, this.market.collateral_token.decimals), + collateralFromUserBorrowed: formatNumber(BN(userBorrowed).div(pAvgBN as BigNumber).toString(), this.market.collateral_token.decimals), + collateralFromMaxDebt: formatNumber(maxLeverageCollateralBN[j].toString(), this.market.collateral_token.decimals), + maxLeverage: maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).div(userEffectiveCollateralBN).toString(), + avgPrice: (pAvgBN as BigNumber).toString(), + }; + } + + return res; + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); + + private _leverageExpectedCollateral = async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, quote: IQuote, user?: string): + Promise<{ _futureStateCollateral: bigint, _totalCollateral: bigint, _userCollateral: bigint, + _collateralFromUserBorrowed: bigint, _collateralFromDebt: bigint, avgPrice: string }> => { + const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals); + const _debt = parseUnits(debt, this.market.borrowed_token.decimals); + const _userBorrowed = parseUnits(userBorrowed, this.market.borrowed_token.decimals); + // additionalCollateral = (userBorrowed / p) + leverageCollateral + const _additionalCollateral = BigInt(quote.outAmount); + const _collateralFromDebt = _debt * BigInt(10**18) / (_debt + _userBorrowed) * _additionalCollateral / BigInt(10**18); + const _collateralFromUserBorrowed = _additionalCollateral - _collateralFromDebt; + let _stateCollateral = BigInt(0); + if (user) { + const { _collateral, _borrowed } = await this.market._userState(user); + if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); + _stateCollateral = _collateral; + } + const _totalCollateral = _userCollateral + _additionalCollateral; + const _futureStateCollateral = _stateCollateral + _totalCollateral; + const avgPrice = toBN(_debt + _userBorrowed, this.market.borrowed_token.decimals).div(toBN(_additionalCollateral, this.market.collateral_token.decimals)).toString(); + + return { _futureStateCollateral, _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, avgPrice }; + }; + + public async leverageCreateLoanExpectedCollateral(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, quote: IQuote): + Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, leverage: string, avgPrice: string }> { + this._checkLeverageZap(); + + const { _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, avgPrice } = + await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt, quote); + return { + totalCollateral: formatUnits(_totalCollateral, this.market.collateral_token.decimals), + userCollateral: formatUnits(_userCollateral, this.market.collateral_token.decimals), + collateralFromUserBorrowed: formatUnits(_collateralFromUserBorrowed, this.market.collateral_token.decimals), + collateralFromDebt: formatUnits(_collateralFromDebt, this.market.collateral_token.decimals), + leverage: toBN(_collateralFromDebt + _userCollateral + _collateralFromUserBorrowed, this.market.collateral_token.decimals) + .div(toBN(_userCollateral + _collateralFromUserBorrowed, this.market.collateral_token.decimals)).toString(), + avgPrice, + } + } + + public async leverageCreateLoanExpectedMetrics(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, quote: IQuote, healthIsFull = true): Promise { + this._checkLeverageZap(); + + const [_n2, _n1] = await this._leverageBands(userCollateral, userBorrowed, debt, range, quote); + + const prices = await this.market._getPrices(_n2, _n1); + const health = await this._leverageHealth(userCollateral, userBorrowed, debt, range, quote, healthIsFull); + + return { + priceImpact: quote.priceImpact, + bands: [Number(_n2), Number(_n1)], + prices, + health, + } + } + + public async leverageCreateLoanMaxRange(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, getExpected: GetExpectedFn): Promise { + this._checkLeverageZap(); + const maxRecv = await this.leverageCreateLoanMaxRecvAllRanges(userCollateral, userBorrowed, getExpected); + for (let N = this.market.minBands; N <= this.market.maxBands; N++) { + if (BN(debt).gt(maxRecv[N].maxDebt)) return N - 1; + } + + return this.market.maxBands; + } + + private _leverageCalcN1 = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, quote: IQuote, user?: string): Promise => { + if (range > 0) this.market._checkRange(range); + let _stateDebt = BigInt(0); + if (user) { + const { _debt, _borrowed, _N } = await this.market._userState(user); + if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); + _stateDebt = _debt; + if (range < 0) range = Number(this.llamalend.formatUnits(_N, 0)); + } + const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt, quote, user); + const _debt = _stateDebt + parseUnits(debt, this.market.borrowed_token.decimals); + return await this.llamalend.contracts[this.market.addresses.controller].contract.calculate_debt_n1(_futureStateCollateral, _debt, range, this.llamalend.constantOptions); + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); + + private _leverageCalcN1AllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, maxN: number, quote: IQuote): Promise => { + const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt, quote); + const _debt = parseUnits(debt, this.market.borrowed_token.decimals); + const calls = []; + for (let N = this.market.minBands; N <= maxN; N++) { + calls.push(this.llamalend.contracts[this.market.addresses.controller].multicallContract.calculate_debt_n1(_futureStateCollateral, _debt, N)); + } + return await this.llamalend.multicallProvider.all(calls) as bigint[]; + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); + + private async _leverageBands(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, quote: IQuote, user?: string): Promise<[bigint, bigint]> { + const _n1 = await this._leverageCalcN1(userCollateral, userBorrowed, debt, range, quote, user); + if (range < 0) { + const { N } = await this.market.userState(user); + range = Number(N); + } + const _n2 = _n1 + BigInt(range - 1); + + return [_n2, _n1]; + } + + private async _leverageCreateLoanBandsAllRanges(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, getExpected: GetExpectedFn, quote: IQuote): Promise> { + const maxN = await this.leverageCreateLoanMaxRange(userCollateral, userBorrowed, debt, getExpected); + const _n1_arr = await this._leverageCalcN1AllRanges(userCollateral, userBorrowed, debt, maxN, quote); + const _n2_arr: bigint[] = []; + for (let N = this.market.minBands; N <= maxN; N++) { + _n2_arr.push(_n1_arr[N - this.market.minBands] + BigInt(N - 1)); + } + + const _bands: IDict<[bigint, bigint]> = {}; + for (let N = this.market.minBands; N <= maxN; N++) { + _bands[N] = [_n2_arr[N - this.market.minBands], _n1_arr[N - this.market.minBands]]; + } + + return _bands; + } + + public async leverageCreateLoanBandsAllRanges(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, getExpected: GetExpectedFn, quote: IQuote): Promise> { + this._checkLeverageZap(); + const _bands = await this._leverageCreateLoanBandsAllRanges(userCollateral, userBorrowed, debt, getExpected, quote); + + const bands: { [index: number]: [number, number] | null } = {}; + for (let N = this.market.minBands; N <= this.market.maxBands; N++) { + if (_bands[N]) { + bands[N] = _bands[N].map(Number) as [number, number]; + } else { + bands[N] = null + } + } + + return bands; + } + + public async leverageCreateLoanPricesAllRanges(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, getExpected: GetExpectedFn, quote: IQuote): Promise> { + this._checkLeverageZap(); + const _bands = await this._leverageCreateLoanBandsAllRanges(userCollateral, userBorrowed, debt, getExpected, quote); + + const prices: { [index: number]: [string, string] | null } = {}; + for (let N = this.market.minBands; N <= this.market.maxBands; N++) { + if (_bands[N]) { + prices[N] = await this.market._calcPrices(..._bands[N]); + } else { + prices[N] = null + } + } + + return prices; + } + + private async _leverageHealth( + userCollateral: TAmount, + userBorrowed: TAmount, + dDebt: TAmount, + range: number, + quote: IQuote, + full: boolean, + user = this.llamalend.constants.ZERO_ADDRESS + ): Promise { + if (range > 0) this.market._checkRange(range); + const { _totalCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, dDebt, quote, user); + const { _borrowed, _N } = await this.market._userState(user); + if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); + if (range < 0) range = Number(this.llamalend.formatUnits(_N, 0)); + const _dDebt = parseUnits(dDebt, this.market.borrowed_token.decimals); + + const contract = this.llamalend.contracts[this.market.addresses.controller].contract; + let _health = await contract.health_calculator(user, _totalCollateral, _dDebt, full, range, this.llamalend.constantOptions) as bigint; + _health = _health * BigInt(100); + + return formatUnits(_health); + } + + public async leverageCreateLoanIsApproved(userCollateral: TAmount, userBorrowed: TAmount): Promise { + this._checkLeverageZap(); + const collateralAllowance = await hasAllowance.call(this.llamalend, + [this.market.collateral_token.address], [userCollateral], this.llamalend.signerAddress, this.market.addresses.controller); + const borrowedAllowance = await hasAllowance.call(this.llamalend, + [this.market.borrowed_token.address], [userBorrowed], this.llamalend.signerAddress, this.llamalend.constants.ALIASES.leverage_zap_v2); + + return collateralAllowance && borrowedAllowance + } + + public async leverageCreateLoanApproveEstimateGas (userCollateral: TAmount, userBorrowed: TAmount): Promise { + this._checkLeverageZap(); + const collateralGas = await ensureAllowanceEstimateGas.call(this.llamalend, + [this.market.collateral_token.address], [userCollateral], this.market.addresses.controller); + const borrowedGas = await ensureAllowanceEstimateGas.call(this.llamalend, + [this.market.borrowed_token.address], [userBorrowed], this.llamalend.constants.ALIASES.leverage_zap_v2); + + if(Array.isArray(collateralGas) && Array.isArray(borrowedGas)) { + return [collateralGas[0] + borrowedGas[0], collateralGas[1] + borrowedGas[1]] + } else { + return (collateralGas as number) + (borrowedGas as number) + } + } + + public async leverageCreateLoanApprove(userCollateral: TAmount, userBorrowed: TAmount): Promise { + this._checkLeverageZap(); + const collateralApproveTx = await ensureAllowance.call(this.llamalend, + [this.market.collateral_token.address], [userCollateral], this.market.addresses.controller); + const borrowedApproveTx = await ensureAllowance.call(this.llamalend, + [this.market.borrowed_token.address], [userBorrowed], this.llamalend.constants.ALIASES.leverage_zap_v2); + + return [...collateralApproveTx, ...borrowedApproveTx] + } + + private async _leverageCreateLoan( + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + range: number, + router: string, + calldata: string, + estimateGas: boolean + ): Promise { + if (await this.market.userLoanExists()) throw Error("Loan already created"); + this.market._checkRange(range); + + const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals); + const _userBorrowed = parseUnits(userBorrowed, this.market.borrowed_token.decimals); + const _debt = parseUnits(debt, this.market.borrowed_token.decimals); + + + const zapCalldata = buildCalldataForLeverageZapV2(router, calldata) + const contract = this.llamalend.contracts[this.market.addresses.controller].contract; + const gas = await contract.create_loan_extended.estimateGas( + _userCollateral, + _debt, + range, + this.llamalend.constants.ALIASES.leverage_zap_v2, + [0, parseUnits(this._getMarketId(), 0), _userBorrowed], + zapCalldata, + { ...this.llamalend.constantOptions } + ); + if (estimateGas) return smartNumber(gas); + + await this.llamalend.updateFeeData(); + const gasLimit = _mulBy1_3(DIGas(gas)); + return (await contract.create_loan_extended( + _userCollateral, + _debt, + range, + this.llamalend.constants.ALIASES.leverage_zap_v2, + [0, parseUnits(this._getMarketId(), 0), _userBorrowed], + zapCalldata, + { ...this.llamalend.options, gasLimit } + )).hash + } + + public async leverageCreateLoanEstimateGas(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, router: string, calldata: string): Promise { + this._checkLeverageZap(); + if (!(await this.leverageCreateLoanIsApproved(userCollateral, userBorrowed))) throw Error("Approval is needed for gas estimation"); + return await this._leverageCreateLoan(userCollateral, userBorrowed, debt, range, router, calldata, true) as number; + } + + public async leverageCreateLoan(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, router: string, calldata: string): Promise { + this._checkLeverageZap(); + await this.leverageCreateLoanApprove(userCollateral, userBorrowed); + return await this._leverageCreateLoan(userCollateral, userBorrowed, debt, range, router, calldata, false) as string; + } + + // ---------------- LEVERAGE BORROW MORE ---------------- + + public async leverageBorrowMoreMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, getExpected: GetExpectedFn, address = ""): + Promise<{ + maxDebt: string, + maxTotalCollateral: string, + userCollateral: string, + collateralFromUserBorrowed: string, + collateralFromMaxDebt: string, + avgPrice: string, + }> { + // max_borrowable = userCollateral / (1 / (k_effective * max_p_base) - 1 / p_avg) + this._checkLeverageZap(); + address = _getAddress.call(this.llamalend, address); + const { _collateral: _stateCollateral, _borrowed: _stateBorrowed, _debt: _stateDebt, _N } = await this.market._userState(address); + if (_stateBorrowed > BigInt(0)) throw Error(`User ${address} is already in liquidation mode`); + const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals); + const controllerContract = this.llamalend.contracts[this.market.addresses.controller].contract; + const _borrowedFromStateCollateral = await controllerContract.max_borrowable(_stateCollateral, _N, _stateDebt, this.llamalend.constantOptions) - _stateDebt; + const _userBorrowed = _borrowedFromStateCollateral + parseUnits(userBorrowed, this.market.borrowed_token.decimals); + userBorrowed = formatUnits(_userBorrowed, this.market.borrowed_token.decimals); + + const oraclePriceBand = await this.market.oraclePriceBand(); + let pAvgBN = BN(await this.market.calcTickPrice(oraclePriceBand)); // upper tick of oracle price band + let maxBorrowablePrevBN = BN(0); + let maxBorrowableBN = BN(0); + let _userEffectiveCollateral = BigInt(0); + let _maxLeverageCollateral = BigInt(0); + + const contract = this.llamalend.contracts[this.llamalend.constants.ALIASES.leverage_zap_v2].contract; + for (let i = 0; i < 5; i++) { + maxBorrowablePrevBN = maxBorrowableBN; + _userEffectiveCollateral = _userCollateral + fromBN(BN(userBorrowed).div(pAvgBN), this.market.collateral_token.decimals); + let _maxBorrowable = await contract.max_borrowable(this.market.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral, _N, fromBN(pAvgBN)); + _maxBorrowable = _maxBorrowable * BigInt(998) / BigInt(1000); + if (_maxBorrowable === BigInt(0)) break; + maxBorrowableBN = toBN(_maxBorrowable, this.market.borrowed_token.decimals); + + if (maxBorrowableBN.minus(maxBorrowablePrevBN).abs().div(maxBorrowablePrevBN).lt(0.0005)) { + maxBorrowableBN = maxBorrowablePrevBN; + break; + } + + // additionalCollateral = (userBorrowed / p) + leverageCollateral + const _maxAdditionalCollateral = BigInt((await getExpected( + this.market.addresses.borrowed_token, this.market.addresses.collateral_token, _maxBorrowable + _userBorrowed, this.market.addresses.amm)).outAmount); + pAvgBN = maxBorrowableBN.plus(userBorrowed).div(toBN(_maxAdditionalCollateral, this.market.collateral_token.decimals)); + _maxLeverageCollateral = _maxAdditionalCollateral - fromBN(BN(userBorrowed).div(pAvgBN), this.market.collateral_token.decimals); + } + + if (maxBorrowableBN.eq(0)) _userEffectiveCollateral = BigInt(0); + const _maxTotalCollateral = _userEffectiveCollateral + _maxLeverageCollateral + let _maxBorrowable = await controllerContract.max_borrowable(_stateCollateral + _maxTotalCollateral, _N, _stateDebt, this.llamalend.constantOptions) - _stateDebt; + _maxBorrowable = _maxBorrowable * BigInt(998) / BigInt(1000); + + return { + maxDebt: formatUnits(_maxBorrowable, this.market.borrowed_token.decimals), + maxTotalCollateral: formatUnits(_maxTotalCollateral, this.market.collateral_token.decimals), + userCollateral: formatNumber(userCollateral, this.market.collateral_token.decimals), + collateralFromUserBorrowed: formatNumber(BN(userBorrowed).div(pAvgBN).toString(), this.market.collateral_token.decimals), + collateralFromMaxDebt: formatUnits(_maxLeverageCollateral, this.market.collateral_token.decimals), + avgPrice: pAvgBN.toString(), + }; + } + + public async leverageBorrowMoreExpectedCollateral(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, quote: IQuote, address = ""): + Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, avgPrice: string }> { + this._checkLeverageZap(); + address = _getAddress.call(this.llamalend, address); + + const { _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, avgPrice } = + await this._leverageExpectedCollateral(userCollateral, userBorrowed, dDebt, quote, address); + return { + totalCollateral: formatUnits(_totalCollateral, this.market.collateral_token.decimals), + userCollateral: formatUnits(_userCollateral, this.market.collateral_token.decimals), + collateralFromUserBorrowed: formatUnits(_collateralFromUserBorrowed, this.market.collateral_token.decimals), + collateralFromDebt: formatUnits(_collateralFromDebt, this.market.collateral_token.decimals), + avgPrice, + } + } + + public async leverageBorrowMoreExpectedMetrics(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, quote: IQuote, healthIsFull = true, address = ""): Promise { + this._checkLeverageZap(); + address = _getAddress.call(this.llamalend, address); + + + const [_n2, _n1] = await this._leverageBands(userCollateral, userBorrowed, debt, -1, quote, address); + + const prices = await this.market._getPrices(_n2, _n1); + const health = await this._leverageHealth(userCollateral, userBorrowed, debt, -1, quote, healthIsFull, address); + + return { + priceImpact: quote.priceImpact, + bands: [Number(_n2), Number(_n1)], + prices, + health, + } + } + + private async _leverageBorrowMore( + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + router: string, + calldata: string, + estimateGas: boolean + ): Promise { + if (!(await this.market.userLoanExists())) throw Error("Loan does not exist"); + const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals); + const _userBorrowed = parseUnits(userBorrowed, this.market.borrowed_token.decimals); + const _debt = parseUnits(debt, this.market.borrowed_token.decimals); + + const zapCalldata = buildCalldataForLeverageZapV2(router, calldata); + + const contract = this.llamalend.contracts[this.market.addresses.controller].contract; + const gas = await contract.borrow_more_extended.estimateGas( + _userCollateral, + _debt, + this.llamalend.constants.ALIASES.leverage_zap_v2, + [0, parseUnits(this._getMarketId(), 0), _userBorrowed], + zapCalldata, + { ...this.llamalend.constantOptions } + ); + if (estimateGas) return smartNumber(gas); + + await this.llamalend.updateFeeData(); + const gasLimit = _mulBy1_3(DIGas(gas)); + + return (await contract.borrow_more_extended( + _userCollateral, + _debt, + this.llamalend.constants.ALIASES.leverage_zap_v2, + [0, parseUnits(this._getMarketId(), 0), _userBorrowed], + zapCalldata, + { ...this.llamalend.options, gasLimit } + )).hash + } + + public async leverageBorrowMoreEstimateGas(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, router: string, calldata: string): Promise { + this._checkLeverageZap(); + if (!(await this.leverageCreateLoanIsApproved(userCollateral, userBorrowed))) throw Error("Approval is needed for gas estimation"); + return await this._leverageBorrowMore(userCollateral, userBorrowed, debt, router, calldata, true) as number; + } + + public async leverageBorrowMore(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, router: string, calldata: string): Promise { + this._checkLeverageZap(); + await this.leverageCreateLoanApprove(userCollateral, userBorrowed); + return await this._leverageBorrowMore(userCollateral, userBorrowed, debt, router, calldata, false) as string; + } + + // ---------------- LEVERAGE REPAY ---------------- + + private _leverageRepayExpectedBorrowed = (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote): + { _totalBorrowed: bigint, _borrowedFromStateCollateral: bigint, _borrowedFromUserCollateral: bigint, avgPrice: string } => { + this._checkLeverageZap(); + const _stateCollateral = parseUnits(stateCollateral, this.market.collateral_token.decimals); + const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals); + let _borrowedExpected = BigInt(0); + let _borrowedFromStateCollateral = BigInt(0); + let _borrowedFromUserCollateral = BigInt(0); + if (_stateCollateral + _userCollateral > BigInt(0)) { + _borrowedExpected = BigInt(quote.outAmount); + _borrowedFromStateCollateral = _stateCollateral * BigInt(10 ** 18) / (_stateCollateral + _userCollateral) * _borrowedExpected / BigInt(10 ** 18); + _borrowedFromUserCollateral = _borrowedExpected - _borrowedFromStateCollateral; + } + const _totalBorrowed = _borrowedExpected + parseUnits(userBorrowed, this.market.borrowed_token.decimals); + const avgPrice = toBN(_borrowedExpected, this.market.borrowed_token.decimals).div(toBN(_stateCollateral + _userCollateral, this.market.collateral_token.decimals)).toString(); + + return { _totalBorrowed, _borrowedFromStateCollateral, _borrowedFromUserCollateral, avgPrice } + }; + + public leverageRepayExpectedBorrowed = async (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote): + Promise<{ totalBorrowed: string, borrowedFromStateCollateral: string, borrowedFromUserCollateral: string, userBorrowed: string, avgPrice: string }> => { + this._checkLeverageZap(); + + const { _totalBorrowed, _borrowedFromStateCollateral, _borrowedFromUserCollateral, avgPrice } = + this._leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed, quote); + + return { + totalBorrowed: formatUnits(_totalBorrowed, this.market.borrowed_token.decimals), + borrowedFromStateCollateral: formatUnits(_borrowedFromStateCollateral, this.market.borrowed_token.decimals), + borrowedFromUserCollateral: formatUnits(_borrowedFromUserCollateral, this.market.borrowed_token.decimals), + userBorrowed: formatNumber(userBorrowed, this.market.borrowed_token.decimals), + avgPrice, + } + }; + + public async leverageRepayIsFull(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote, address = ""): Promise { + this._checkLeverageZap(); + address = _getAddress.call(this.llamalend, address); + const { _borrowed: _stateBorrowed, _debt } = await this.market._userState(address); + const { _totalBorrowed } = this._leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed, quote); + + return _stateBorrowed + _totalBorrowed > _debt; + } + + public async leverageRepayIsAvailable(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote, address = ""): Promise { + // 0. const { collateral, stablecoin, debt } = await this.market.userState(address); + // 1. maxCollateral for deleverage is collateral from line above. + // 2. If user is underwater (stablecoin > 0), only full repayment is available: + // await this.deleverageRepayStablecoins(deleverageCollateral) + stablecoin > debt + this._checkLeverageZap(); + address = _getAddress.call(this.llamalend, address); + const { collateral, borrowed, debt } = await this.market.userState(address); + // Loan does not exist + if (BN(debt).eq(0)) return false; + // Can't spend more than user has + if (BN(stateCollateral).gt(collateral)) return false; + // Only full repayment and closing the position is available if user is underwater+ + if (BN(borrowed).gt(0)) return await this.leverageRepayIsFull(stateCollateral, userCollateral, userBorrowed, quote, address); + + return true; + } + + public async leverageRepayExpectedMetrics(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, healthIsFull = true, quote: IQuote, address = ""): Promise { + this._checkLeverageZap(); + address = _getAddress.call(this.llamalend, address); + + const [_n2, _n1] = await this._leverageRepayBands(stateCollateral, userCollateral, userBorrowed, quote, address); + const prices = await this.market._getPrices(_n2, _n1); + const health = await this._leverageRepayHealth(stateCollateral, userCollateral, userBorrowed, quote, healthIsFull, address); + + const _stateCollateral = parseUnits(stateCollateral, this.market.collateral_token.decimals); + const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals); + const priceImpact = _stateCollateral + _userCollateral > BigInt(0) ? quote.priceImpact : 0; + + return { + priceImpact, + bands: [Number(_n2), Number(_n1)], + prices, + health, + } + } + + private _leverageRepayBands = memoize( async (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote, address: string): Promise<[bigint, bigint]> => { + address = _getAddress.call(this.llamalend, address); + if (!(await this.leverageRepayIsAvailable(stateCollateral, userCollateral, userBorrowed, quote, address))) return [parseUnits(0, 0), parseUnits(0, 0)]; + + const _stateRepayCollateral = parseUnits(stateCollateral, this.market.collateral_token.decimals); + const { _collateral: _stateCollateral, _debt: _stateDebt, _N } = await this.market._userState(address); + if (_stateDebt == BigInt(0)) throw Error(`Loan for ${address} does not exist`); + if (_stateCollateral < _stateRepayCollateral) throw Error(`Can't use more collateral than user's position has (${_stateRepayCollateral}) > ${_stateCollateral})`); + + let _n1 = parseUnits(0, 0); + let _n2 = parseUnits(0, 0); + const { _totalBorrowed: _repayExpected } = this._leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed, quote); + try { + _n1 = await this.llamalend.contracts[this.market.addresses.controller].contract.calculate_debt_n1(_stateCollateral - _stateRepayCollateral, _stateDebt - _repayExpected, _N); + _n2 = _n1 + (_N - BigInt(1)); + } catch { + console.log("Full repayment"); + } + + return [_n2, _n1]; + }, + { + promise: true, + maxAge: 5 * 60 * 1000, // 5m + }); + + + private async _leverageRepayHealth(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote, full = true, address = ""): Promise { + this._checkLeverageZap(); + address = _getAddress.call(this.llamalend, address); + const { _borrowed: _stateBorrowed, _debt, _N } = await this.market._userState(address); + if (_stateBorrowed > BigInt(0)) return "0.0"; + if (!(await this.leverageRepayIsAvailable(stateCollateral, userCollateral, userBorrowed, quote, address))) return "0.0"; + + const { _totalBorrowed } = this._leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed, quote); + const _dCollateral = parseUnits(stateCollateral, this.market.collateral_token.decimals) * BigInt(-1); + const _dDebt = _totalBorrowed * BigInt(-1); + + if (_debt + _dDebt <= BigInt(0)) return "0.0"; + const contract = this.llamalend.contracts[this.market.addresses.controller].contract; + let _health = await contract.health_calculator(address, _dCollateral, _dDebt, full, _N, this.llamalend.constantOptions) as bigint; + _health = _health * BigInt(100); + + return this.llamalend.formatUnits(_health); + } + + public async leverageRepayIsApproved(userCollateral: TAmount, userBorrowed: TAmount): Promise { + this._checkLeverageZap(); + return await hasAllowance.call(this.llamalend, + [this.market.collateral_token.address, this.market.borrowed_token.address], + [userCollateral, userBorrowed], + this.llamalend.signerAddress, + this.llamalend.constants.ALIASES.leverage_zap_v2 + ); + } + + public async leverageRepayApproveEstimateGas (userCollateral: TAmount, userBorrowed: TAmount): Promise { + this._checkLeverageZap(); + return await ensureAllowanceEstimateGas.call(this.llamalend, + [this.market.collateral_token.address, this.market.borrowed_token.address], + [userCollateral, userBorrowed], + this.llamalend.constants.ALIASES.leverage_zap_v2 + ); + } + + public async leverageRepayApprove(userCollateral: TAmount, userBorrowed: TAmount): Promise { + this._checkLeverageZap(); + return await ensureAllowance.call(this.llamalend, + [this.market.collateral_token.address, this.market.borrowed_token.address], + [userCollateral, userBorrowed], + this.llamalend.constants.ALIASES.leverage_zap_v2 + ); + } + + private async _leverageRepay( + stateCollateral: TAmount, + userCollateral: TAmount, + userBorrowed: TAmount, + router: string, + calldata: string, + estimateGas: boolean + ): Promise { + if (!(await this.market.userLoanExists())) throw Error("Loan does not exist"); + const _stateCollateral = parseUnits(stateCollateral, this.market.collateral_token.decimals); + const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals); + const _userBorrowed = parseUnits(userBorrowed, this.market.borrowed_token.decimals); + let zapCalldata = buildCalldataForLeverageZapV2(router, "0x"); + if (_stateCollateral + _userCollateral > BigInt(0)) { + zapCalldata = buildCalldataForLeverageZapV2(router, calldata) + } + + console.log('params', [0, parseUnits(this._getMarketId(), 0), _userCollateral, _userBorrowed], calldata) + const contract = this.llamalend.contracts[this.market.addresses.controller].contract; + const gas = await contract.repay_extended.estimateGas( + this.llamalend.constants.ALIASES.leverage_zap_v2, + [0, parseUnits(this._getMarketId(), 0), _userCollateral, _userBorrowed], + zapCalldata + ); + if (estimateGas) return smartNumber(gas); + + await this.llamalend.updateFeeData(); + const gasLimit = _mulBy1_3(DIGas(gas)); + + return (await contract.repay_extended( + this.llamalend.constants.ALIASES.leverage_zap_v2, + [0, parseUnits(this._getMarketId(), 0), _userCollateral, _userBorrowed], + zapCalldata, + { ...this.llamalend.options, gasLimit } + )).hash + } + + public async leverageRepayEstimateGas(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, router: string, calldata: string): Promise { + this._checkLeverageZap(); + if (!(await this.leverageRepayIsApproved(userCollateral, userBorrowed))) throw Error("Approval is needed for gas estimation"); + return await this._leverageRepay(stateCollateral, userCollateral, userBorrowed, router, calldata, true) as number; + } + + public async leverageRepay(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, router: string, calldata: string): Promise { + this._checkLeverageZap(); + await this.leverageRepayApprove(userCollateral, userBorrowed); + return await this._leverageRepay(stateCollateral, userCollateral, userBorrowed, router, calldata, false) as string; + } +} \ No newline at end of file diff --git a/src/llamalend.ts b/src/llamalend.ts index dfa877c..a769ca2 100644 --- a/src/llamalend.ts +++ b/src/llamalend.ts @@ -348,6 +348,7 @@ class Llamalend implements ILlamalend { this.setContract(this.constants.ALIASES['one_way_factory'], OneWayLendingFactoryABI); this.setContract(this.constants.ALIASES['gauge_controller'], GaugeControllerABI); this.setContract(this.constants.ALIASES['leverage_zap'], LeverageZapABI); + this.setContract(this.constants.ALIASES['leverage_zap_v2'], LeverageZapABI); if (this.chainId === 1) { this.setContract(this.constants.ALIASES.minter, MinterABI); this.setContract(this.constants.ALIASES.gauge_factory, GaugeFactoryMainnetABI); diff --git a/src/utils.ts b/src/utils.ts index a43d190..05ecf04 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -515,4 +515,15 @@ export const calculateFutureLeverage = ( } else { return currentCollateralBN.minus(collateralBN).div(totalDepositBN.minus(collateralBN)).toString(); } -}; \ No newline at end of file +}; + +export const buildCalldataForLeverageZapV2 = (routerAddress: string, exchangeCalldata: string): string => { + const cleanCalldata = exchangeCalldata.startsWith('0x') ? exchangeCalldata.slice(2) : exchangeCalldata; + + const abiCoder = ethers.AbiCoder.defaultAbiCoder(); + return abiCoder.encode( + ['address', 'bytes'], + [routerAddress, '0x' + cleanCalldata] + ); +}; + From 731b1487951e8cf4b55975f97e88b6ef0ffe6161 Mon Sep 17 00:00:00 2001 From: fedorovdg Date: Fri, 12 Dec 2025 12:58:33 +0400 Subject: [PATCH 2/7] refactor: destructured parameters in leverageZapV2 --- src/lendMarkets/interfaces/leverageZapV2.ts | 325 ++++++++++++++++---- src/lendMarkets/modules/leverageZapV2.ts | 230 +++++++++++--- 2 files changed, 454 insertions(+), 101 deletions(-) diff --git a/src/lendMarkets/interfaces/leverageZapV2.ts b/src/lendMarkets/interfaces/leverageZapV2.ts index 8111e8a..fbb14aa 100644 --- a/src/lendMarkets/interfaces/leverageZapV2.ts +++ b/src/lendMarkets/interfaces/leverageZapV2.ts @@ -5,69 +5,278 @@ export interface ILeverageZapV2 { maxLeverage: (N: number) => Promise, - createLoanMaxRecv: (userCollateral: TAmount, userBorrowed: TAmount, range: number, getExpected: GetExpectedFn) => - Promise<{ - maxDebt: string, - maxTotalCollateral: string, - userCollateral: string, - collateralFromUserBorrowed: string, - collateralFromMaxDebt: string, - maxLeverage: string, - avgPrice: string, - }>, - createLoanMaxRecvAllRanges: (userCollateral: TAmount, userBorrowed: TAmount, getExpected: GetExpectedFn) => - Promise>, - createLoanExpectedCollateral: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, quote: IQuote) => - Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, leverage: string, avgPrice: string }>, - createLoanExpectedMetrics: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, quote: IQuote, healthIsFull?: boolean) => Promise, - createLoanMaxRange: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, getExpected: GetExpectedFn) => Promise, - createLoanBandsAllRanges: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, getExpected: GetExpectedFn, quote: IQuote) => Promise>, - createLoanPricesAllRanges: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, getExpected: GetExpectedFn, quote: IQuote) => Promise>, - createLoanIsApproved: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, - createLoanApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, - createLoan: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, router: string, calldata: string) => Promise, + createLoanMaxRecv: ({ + userCollateral, + userBorrowed, + range, + getExpected + }: { + userCollateral: TAmount, + userBorrowed: TAmount, + range: number, + getExpected: GetExpectedFn + }) => Promise<{ + maxDebt: string, + maxTotalCollateral: string, + userCollateral: string, + collateralFromUserBorrowed: string, + collateralFromMaxDebt: string, + maxLeverage: string, + avgPrice: string, + }>, + createLoanMaxRecvAllRanges: ({ + userCollateral, + userBorrowed, + getExpected + }: { + userCollateral: TAmount, + userBorrowed: TAmount, + getExpected: GetExpectedFn + }) => Promise>, + createLoanExpectedCollateral: ({ + userCollateral, + userBorrowed, + debt, + quote + }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + quote: IQuote + }) => Promise<{ + totalCollateral: string, + userCollateral: string, + collateralFromUserBorrowed: string, + collateralFromDebt: string, + leverage: string, + avgPrice: string + }>, + createLoanExpectedMetrics: ({ + userCollateral, + userBorrowed, + debt, + range, + quote, + healthIsFull + }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + range: number, + quote: IQuote, + healthIsFull?: boolean + }) => Promise, + createLoanMaxRange: ({ + userCollateral, + userBorrowed, + debt, + getExpected + }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + getExpected: GetExpectedFn + }) => Promise, + createLoanBandsAllRanges: ({ + userCollateral, + userBorrowed, + debt, + getExpected, + quote + }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + getExpected: GetExpectedFn, + quote: IQuote + }) => Promise>, + createLoanPricesAllRanges: ({ + userCollateral, + userBorrowed, + debt, + getExpected, + quote + }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + getExpected: GetExpectedFn, + quote: IQuote + }) => Promise>, + createLoanIsApproved: ({ + userCollateral, + userBorrowed + }: { + userCollateral: TAmount, + userBorrowed: TAmount + }) => Promise, + createLoanApprove: ({ + userCollateral, + userBorrowed + }: { + userCollateral: TAmount, + userBorrowed: TAmount + }) => Promise, + createLoan: ({ + userCollateral, + userBorrowed, + debt, + range, + router, + calldata + }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + range: number, + router: string, + calldata: string + }) => Promise, - borrowMoreMaxRecv: (userCollateral: TAmount, userBorrowed: TAmount, getExpected: GetExpectedFn, address?: string) => - Promise<{ - maxDebt: string, - maxTotalCollateral: string, - userCollateral: string, - collateralFromUserBorrowed: string, - collateralFromMaxDebt: string, - avgPrice: string, - }>, - borrowMoreExpectedCollateral: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, quote: IQuote, address?: string) => - Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, avgPrice: string }>, - borrowMoreExpectedMetrics: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, quote: IQuote, healthIsFull?: boolean, address?: string) => Promise, - borrowMoreIsApproved: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, - borrowMoreApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, - borrowMore: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, router: string, calldata: string) => Promise, + borrowMoreMaxRecv: ({ userCollateral, userBorrowed, getExpected, address }: { + userCollateral: TAmount, + userBorrowed: TAmount, + getExpected: GetExpectedFn, + address?: string + }) => Promise<{ + maxDebt: string, + maxTotalCollateral: string, + userCollateral: string, + collateralFromUserBorrowed: string, + collateralFromMaxDebt: string, + avgPrice: string, + }>, + borrowMoreExpectedCollateral: ({ userCollateral, userBorrowed, dDebt, quote, address }: { + userCollateral: TAmount, + userBorrowed: TAmount, + dDebt: TAmount, + quote: IQuote, + address?: string + }) => Promise<{ + totalCollateral: string, + userCollateral: string, + collateralFromUserBorrowed: string, + collateralFromDebt: string, + avgPrice: string + }>, + borrowMoreExpectedMetrics: ({ userCollateral, userBorrowed, debt, quote, healthIsFull, address }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + quote: IQuote, + healthIsFull?: boolean, + address?: string + }) => Promise, + borrowMoreIsApproved: ({ userCollateral, userBorrowed }: { + userCollateral: TAmount, + userBorrowed: TAmount + }) => Promise, + borrowMoreApprove: ({ userCollateral, userBorrowed }: { + userCollateral: TAmount, + userBorrowed: TAmount + }) => Promise, + borrowMore: ({ userCollateral, userBorrowed, debt, router, calldata }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + router: string, + calldata: string + }) => Promise, - repayExpectedBorrowed: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote) => - Promise<{ totalBorrowed: string, borrowedFromStateCollateral: string, borrowedFromUserCollateral: string, userBorrowed: string, avgPrice: string }>, - repayIsFull: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote, address?: string) => Promise, - repayIsAvailable: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote, address?: string) => Promise, - repayExpectedMetrics: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, healthIsFull: boolean, quote: IQuote, address: string) => Promise, - repayIsApproved: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, - repayApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, - repay: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, router: string, calldata: string) => Promise, + repayExpectedBorrowed: ({ stateCollateral, userCollateral, userBorrowed, quote }: { + stateCollateral: TAmount, + userCollateral: TAmount, + userBorrowed: TAmount, + quote: IQuote + }) => Promise<{ + totalBorrowed: string, + borrowedFromStateCollateral: string, + borrowedFromUserCollateral: string, + userBorrowed: string, + avgPrice: string + }>, + repayIsFull: ({ stateCollateral, userCollateral, userBorrowed, quote, address }: { + stateCollateral: TAmount, + userCollateral: TAmount, + userBorrowed: TAmount, + quote: IQuote, + address?: string + }) => Promise, + repayIsAvailable: ({ stateCollateral, userCollateral, userBorrowed, quote, address }: { + stateCollateral: TAmount, + userCollateral: TAmount, + userBorrowed: TAmount, + quote: IQuote, + address?: string + }) => Promise, + repayExpectedMetrics: ({ stateCollateral, userCollateral, userBorrowed, healthIsFull, quote, address }: { + stateCollateral: TAmount, + userCollateral: TAmount, + userBorrowed: TAmount, + healthIsFull: boolean, + quote: IQuote, + address: string + }) => Promise, + repayIsApproved: ({ userCollateral, userBorrowed }: { + userCollateral: TAmount, + userBorrowed: TAmount + }) => Promise, + repayApprove: ({ userCollateral, userBorrowed }: { + userCollateral: TAmount, + userBorrowed: TAmount + }) => Promise, + repay: ({ stateCollateral, userCollateral, userBorrowed, router, calldata }: { + stateCollateral: TAmount, + userCollateral: TAmount, + userBorrowed: TAmount, + router: string, + calldata: string + }) => Promise, estimateGas: { - createLoanApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, - createLoan: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, router: string, calldata: string) => Promise, + createLoanApprove: ({ userCollateral, userBorrowed }: { + userCollateral: TAmount, + userBorrowed: TAmount + }) => Promise, + createLoan: ({ userCollateral, userBorrowed, debt, range, router, calldata }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + range: number, + router: string, + calldata: string + }) => Promise, - borrowMoreApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, - borrowMore: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, router: string, calldata: string) => Promise, + borrowMoreApprove: ({ userCollateral, userBorrowed }: { + userCollateral: TAmount, + userBorrowed: TAmount + }) => Promise, + borrowMore: ({ userCollateral, userBorrowed, debt, router, calldata }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + router: string, + calldata: string + }) => Promise, - repayApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, - repay: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, router: string, calldata: string) => Promise, + repayApprove: ({ userCollateral, userBorrowed }: { + userCollateral: TAmount, + userBorrowed: TAmount + }) => Promise, + repay: ({ stateCollateral, userCollateral, userBorrowed, router, calldata }: { + stateCollateral: TAmount, + userCollateral: TAmount, + userBorrowed: TAmount, + router: string, + calldata: string + }) => Promise, } } diff --git a/src/lendMarkets/modules/leverageZapV2.ts b/src/lendMarkets/modules/leverageZapV2.ts index d5ce710..9b45c1f 100644 --- a/src/lendMarkets/modules/leverageZapV2.ts +++ b/src/lendMarkets/modules/leverageZapV2.ts @@ -70,8 +70,12 @@ export class LeverageZapV2Module { return BN(1).div(BN(1).minus(k_effective_BN)).toString() } - public async leverageCreateLoanMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, range: number, getExpected: GetExpectedFn): - Promise<{ + public async leverageCreateLoanMaxRecv({ userCollateral, userBorrowed, range, getExpected }: { + userCollateral: TAmount, + userBorrowed: TAmount, + range: number, + getExpected: GetExpectedFn + }): Promise<{ maxDebt: string, maxTotalCollateral: string, userCollateral: string, @@ -133,8 +137,11 @@ export class LeverageZapV2Module { }; } - public leverageCreateLoanMaxRecvAllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, getExpected: GetExpectedFn): - Promise { + public async leverageCreateLoanExpectedCollateral({ userCollateral, userBorrowed, debt, quote }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + quote: IQuote + }): Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, leverage: string, avgPrice: string }> { this._checkLeverageZap(); const { _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, avgPrice } = @@ -261,7 +272,14 @@ export class LeverageZapV2Module { } } - public async leverageCreateLoanExpectedMetrics(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, quote: IQuote, healthIsFull = true): Promise { + public async leverageCreateLoanExpectedMetrics({ userCollateral, userBorrowed, debt, range, quote, healthIsFull = true }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + range: number, + quote: IQuote, + healthIsFull?: boolean + }): Promise { this._checkLeverageZap(); const [_n2, _n1] = await this._leverageBands(userCollateral, userBorrowed, debt, range, quote); @@ -277,9 +295,14 @@ export class LeverageZapV2Module { } } - public async leverageCreateLoanMaxRange(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, getExpected: GetExpectedFn): Promise { + public async leverageCreateLoanMaxRange({ userCollateral, userBorrowed, debt, getExpected }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + getExpected: GetExpectedFn + }): Promise { this._checkLeverageZap(); - const maxRecv = await this.leverageCreateLoanMaxRecvAllRanges(userCollateral, userBorrowed, getExpected); + const maxRecv = await this.leverageCreateLoanMaxRecvAllRanges({ userCollateral, userBorrowed, getExpected }); for (let N = this.market.minBands; N <= this.market.maxBands; N++) { if (BN(debt).gt(maxRecv[N].maxDebt)) return N - 1; } @@ -331,7 +354,7 @@ export class LeverageZapV2Module { } private async _leverageCreateLoanBandsAllRanges(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, getExpected: GetExpectedFn, quote: IQuote): Promise> { - const maxN = await this.leverageCreateLoanMaxRange(userCollateral, userBorrowed, debt, getExpected); + const maxN = await this.leverageCreateLoanMaxRange({ userCollateral, userBorrowed, debt, getExpected }); const _n1_arr = await this._leverageCalcN1AllRanges(userCollateral, userBorrowed, debt, maxN, quote); const _n2_arr: bigint[] = []; for (let N = this.market.minBands; N <= maxN; N++) { @@ -346,7 +369,13 @@ export class LeverageZapV2Module { return _bands; } - public async leverageCreateLoanBandsAllRanges(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, getExpected: GetExpectedFn, quote: IQuote): Promise> { + public async leverageCreateLoanBandsAllRanges({ userCollateral, userBorrowed, debt, getExpected, quote }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + getExpected: GetExpectedFn, + quote: IQuote + }): Promise> { this._checkLeverageZap(); const _bands = await this._leverageCreateLoanBandsAllRanges(userCollateral, userBorrowed, debt, getExpected, quote); @@ -362,7 +391,13 @@ export class LeverageZapV2Module { return bands; } - public async leverageCreateLoanPricesAllRanges(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, getExpected: GetExpectedFn, quote: IQuote): Promise> { + public async leverageCreateLoanPricesAllRanges({ userCollateral, userBorrowed, debt, getExpected, quote }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + getExpected: GetExpectedFn, + quote: IQuote + }): Promise> { this._checkLeverageZap(); const _bands = await this._leverageCreateLoanBandsAllRanges(userCollateral, userBorrowed, debt, getExpected, quote); @@ -401,7 +436,10 @@ export class LeverageZapV2Module { return formatUnits(_health); } - public async leverageCreateLoanIsApproved(userCollateral: TAmount, userBorrowed: TAmount): Promise { + public async leverageCreateLoanIsApproved({ userCollateral, userBorrowed }: { + userCollateral: TAmount, + userBorrowed: TAmount + }): Promise { this._checkLeverageZap(); const collateralAllowance = await hasAllowance.call(this.llamalend, [this.market.collateral_token.address], [userCollateral], this.llamalend.signerAddress, this.market.addresses.controller); @@ -411,7 +449,10 @@ export class LeverageZapV2Module { return collateralAllowance && borrowedAllowance } - public async leverageCreateLoanApproveEstimateGas (userCollateral: TAmount, userBorrowed: TAmount): Promise { + public async leverageCreateLoanApproveEstimateGas ({ userCollateral, userBorrowed }: { + userCollateral: TAmount, + userBorrowed: TAmount + }): Promise { this._checkLeverageZap(); const collateralGas = await ensureAllowanceEstimateGas.call(this.llamalend, [this.market.collateral_token.address], [userCollateral], this.market.addresses.controller); @@ -425,7 +466,10 @@ export class LeverageZapV2Module { } } - public async leverageCreateLoanApprove(userCollateral: TAmount, userBorrowed: TAmount): Promise { + public async leverageCreateLoanApprove({ userCollateral, userBorrowed }: { + userCollateral: TAmount, + userBorrowed: TAmount + }): Promise { this._checkLeverageZap(); const collateralApproveTx = await ensureAllowance.call(this.llamalend, [this.market.collateral_token.address], [userCollateral], this.market.addresses.controller); @@ -478,22 +522,40 @@ export class LeverageZapV2Module { )).hash } - public async leverageCreateLoanEstimateGas(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, router: string, calldata: string): Promise { + public async leverageCreateLoanEstimateGas({ userCollateral, userBorrowed, debt, range, router, calldata }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + range: number, + router: string, + calldata: string + }): Promise { this._checkLeverageZap(); - if (!(await this.leverageCreateLoanIsApproved(userCollateral, userBorrowed))) throw Error("Approval is needed for gas estimation"); + if (!(await this.leverageCreateLoanIsApproved({ userCollateral, userBorrowed }))) throw Error("Approval is needed for gas estimation"); return await this._leverageCreateLoan(userCollateral, userBorrowed, debt, range, router, calldata, true) as number; } - public async leverageCreateLoan(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, router: string, calldata: string): Promise { + public async leverageCreateLoan({ userCollateral, userBorrowed, debt, range, router, calldata }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + range: number, + router: string, + calldata: string + }): Promise { this._checkLeverageZap(); - await this.leverageCreateLoanApprove(userCollateral, userBorrowed); + await this.leverageCreateLoanApprove({ userCollateral, userBorrowed }); return await this._leverageCreateLoan(userCollateral, userBorrowed, debt, range, router, calldata, false) as string; } // ---------------- LEVERAGE BORROW MORE ---------------- - public async leverageBorrowMoreMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, getExpected: GetExpectedFn, address = ""): - Promise<{ + public async leverageBorrowMoreMaxRecv({ userCollateral, userBorrowed, getExpected, address = "" }: { + userCollateral: TAmount, + userBorrowed: TAmount, + getExpected: GetExpectedFn, + address?: string + }): Promise<{ maxDebt: string, maxTotalCollateral: string, userCollateral: string, @@ -555,8 +617,13 @@ export class LeverageZapV2Module { }; } - public async leverageBorrowMoreExpectedCollateral(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, quote: IQuote, address = ""): - Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, avgPrice: string }> { + public async leverageBorrowMoreExpectedCollateral({ userCollateral, userBorrowed, dDebt, quote, address = "" }: { + userCollateral: TAmount, + userBorrowed: TAmount, + dDebt: TAmount, + quote: IQuote, + address?: string + }): Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, avgPrice: string }> { this._checkLeverageZap(); address = _getAddress.call(this.llamalend, address); @@ -571,7 +638,14 @@ export class LeverageZapV2Module { } } - public async leverageBorrowMoreExpectedMetrics(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, quote: IQuote, healthIsFull = true, address = ""): Promise { + public async leverageBorrowMoreExpectedMetrics({ userCollateral, userBorrowed, debt, quote, healthIsFull = true, address = "" }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + quote: IQuote, + healthIsFull?: boolean, + address?: string + }): Promise { this._checkLeverageZap(); address = _getAddress.call(this.llamalend, address); @@ -628,15 +702,41 @@ export class LeverageZapV2Module { )).hash } - public async leverageBorrowMoreEstimateGas(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, router: string, calldata: string): Promise { + public async leverageBorrowMoreIsApproved({ userCollateral, userBorrowed }: { + userCollateral: TAmount, + userBorrowed: TAmount + }): Promise { + return await this.leverageCreateLoanIsApproved({ userCollateral, userBorrowed }); + } + + public async leverageBorrowMoreApprove({ userCollateral, userBorrowed }: { + userCollateral: TAmount, + userBorrowed: TAmount + }): Promise { + return await this.leverageCreateLoanApprove({ userCollateral, userBorrowed }); + } + + public async leverageBorrowMoreEstimateGas({ userCollateral, userBorrowed, debt, router, calldata }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + router: string, + calldata: string + }): Promise { this._checkLeverageZap(); - if (!(await this.leverageCreateLoanIsApproved(userCollateral, userBorrowed))) throw Error("Approval is needed for gas estimation"); + if (!(await this.leverageCreateLoanIsApproved({ userCollateral, userBorrowed }))) throw Error("Approval is needed for gas estimation"); return await this._leverageBorrowMore(userCollateral, userBorrowed, debt, router, calldata, true) as number; } - public async leverageBorrowMore(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, router: string, calldata: string): Promise { + public async leverageBorrowMore({ userCollateral, userBorrowed, debt, router, calldata }: { + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + router: string, + calldata: string + }): Promise { this._checkLeverageZap(); - await this.leverageCreateLoanApprove(userCollateral, userBorrowed); + await this.leverageCreateLoanApprove({ userCollateral, userBorrowed }); return await this._leverageBorrowMore(userCollateral, userBorrowed, debt, router, calldata, false) as string; } @@ -661,8 +761,12 @@ export class LeverageZapV2Module { return { _totalBorrowed, _borrowedFromStateCollateral, _borrowedFromUserCollateral, avgPrice } }; - public leverageRepayExpectedBorrowed = async (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote): - Promise<{ totalBorrowed: string, borrowedFromStateCollateral: string, borrowedFromUserCollateral: string, userBorrowed: string, avgPrice: string }> => { + public leverageRepayExpectedBorrowed = async ({ stateCollateral, userCollateral, userBorrowed, quote }: { + stateCollateral: TAmount, + userCollateral: TAmount, + userBorrowed: TAmount, + quote: IQuote + }): Promise<{ totalBorrowed: string, borrowedFromStateCollateral: string, borrowedFromUserCollateral: string, userBorrowed: string, avgPrice: string }> => { this._checkLeverageZap(); const { _totalBorrowed, _borrowedFromStateCollateral, _borrowedFromUserCollateral, avgPrice } = @@ -677,7 +781,13 @@ export class LeverageZapV2Module { } }; - public async leverageRepayIsFull(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote, address = ""): Promise { + public async leverageRepayIsFull({ stateCollateral, userCollateral, userBorrowed, quote, address = "" }: { + stateCollateral: TAmount, + userCollateral: TAmount, + userBorrowed: TAmount, + quote: IQuote, + address?: string + }): Promise { this._checkLeverageZap(); address = _getAddress.call(this.llamalend, address); const { _borrowed: _stateBorrowed, _debt } = await this.market._userState(address); @@ -686,7 +796,13 @@ export class LeverageZapV2Module { return _stateBorrowed + _totalBorrowed > _debt; } - public async leverageRepayIsAvailable(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote, address = ""): Promise { + public async leverageRepayIsAvailable({ stateCollateral, userCollateral, userBorrowed, quote, address = "" }: { + stateCollateral: TAmount, + userCollateral: TAmount, + userBorrowed: TAmount, + quote: IQuote, + address?: string + }): Promise { // 0. const { collateral, stablecoin, debt } = await this.market.userState(address); // 1. maxCollateral for deleverage is collateral from line above. // 2. If user is underwater (stablecoin > 0), only full repayment is available: @@ -699,12 +815,19 @@ export class LeverageZapV2Module { // Can't spend more than user has if (BN(stateCollateral).gt(collateral)) return false; // Only full repayment and closing the position is available if user is underwater+ - if (BN(borrowed).gt(0)) return await this.leverageRepayIsFull(stateCollateral, userCollateral, userBorrowed, quote, address); + if (BN(borrowed).gt(0)) return await this.leverageRepayIsFull({ stateCollateral, userCollateral, userBorrowed, quote, address }); return true; } - public async leverageRepayExpectedMetrics(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, healthIsFull = true, quote: IQuote, address = ""): Promise { + public async leverageRepayExpectedMetrics({ stateCollateral, userCollateral, userBorrowed, healthIsFull, quote, address }: { + stateCollateral: TAmount, + userCollateral: TAmount, + userBorrowed: TAmount, + healthIsFull: boolean, + quote: IQuote, + address: string + }): Promise { this._checkLeverageZap(); address = _getAddress.call(this.llamalend, address); @@ -726,7 +849,7 @@ export class LeverageZapV2Module { private _leverageRepayBands = memoize( async (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote, address: string): Promise<[bigint, bigint]> => { address = _getAddress.call(this.llamalend, address); - if (!(await this.leverageRepayIsAvailable(stateCollateral, userCollateral, userBorrowed, quote, address))) return [parseUnits(0, 0), parseUnits(0, 0)]; + if (!(await this.leverageRepayIsAvailable({ stateCollateral, userCollateral, userBorrowed, quote, address }))) return [parseUnits(0, 0), parseUnits(0, 0)]; const _stateRepayCollateral = parseUnits(stateCollateral, this.market.collateral_token.decimals); const { _collateral: _stateCollateral, _debt: _stateDebt, _N } = await this.market._userState(address); @@ -756,7 +879,7 @@ export class LeverageZapV2Module { address = _getAddress.call(this.llamalend, address); const { _borrowed: _stateBorrowed, _debt, _N } = await this.market._userState(address); if (_stateBorrowed > BigInt(0)) return "0.0"; - if (!(await this.leverageRepayIsAvailable(stateCollateral, userCollateral, userBorrowed, quote, address))) return "0.0"; + if (!(await this.leverageRepayIsAvailable({ stateCollateral, userCollateral, userBorrowed, quote, address }))) return "0.0"; const { _totalBorrowed } = this._leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed, quote); const _dCollateral = parseUnits(stateCollateral, this.market.collateral_token.decimals) * BigInt(-1); @@ -770,7 +893,10 @@ export class LeverageZapV2Module { return this.llamalend.formatUnits(_health); } - public async leverageRepayIsApproved(userCollateral: TAmount, userBorrowed: TAmount): Promise { + public async leverageRepayIsApproved({ userCollateral, userBorrowed }: { + userCollateral: TAmount, + userBorrowed: TAmount + }): Promise { this._checkLeverageZap(); return await hasAllowance.call(this.llamalend, [this.market.collateral_token.address, this.market.borrowed_token.address], @@ -780,7 +906,10 @@ export class LeverageZapV2Module { ); } - public async leverageRepayApproveEstimateGas (userCollateral: TAmount, userBorrowed: TAmount): Promise { + public async leverageRepayApproveEstimateGas ({ userCollateral, userBorrowed }: { + userCollateral: TAmount, + userBorrowed: TAmount + }): Promise { this._checkLeverageZap(); return await ensureAllowanceEstimateGas.call(this.llamalend, [this.market.collateral_token.address, this.market.borrowed_token.address], @@ -789,7 +918,10 @@ export class LeverageZapV2Module { ); } - public async leverageRepayApprove(userCollateral: TAmount, userBorrowed: TAmount): Promise { + public async leverageRepayApprove({ userCollateral, userBorrowed }: { + userCollateral: TAmount, + userBorrowed: TAmount + }): Promise { this._checkLeverageZap(); return await ensureAllowance.call(this.llamalend, [this.market.collateral_token.address, this.market.borrowed_token.address], @@ -835,15 +967,27 @@ export class LeverageZapV2Module { )).hash } - public async leverageRepayEstimateGas(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, router: string, calldata: string): Promise { + public async leverageRepayEstimateGas({ stateCollateral, userCollateral, userBorrowed, router, calldata }: { + stateCollateral: TAmount, + userCollateral: TAmount, + userBorrowed: TAmount, + router: string, + calldata: string + }): Promise { this._checkLeverageZap(); - if (!(await this.leverageRepayIsApproved(userCollateral, userBorrowed))) throw Error("Approval is needed for gas estimation"); + if (!(await this.leverageRepayIsApproved({ userCollateral, userBorrowed }))) throw Error("Approval is needed for gas estimation"); return await this._leverageRepay(stateCollateral, userCollateral, userBorrowed, router, calldata, true) as number; } - public async leverageRepay(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, router: string, calldata: string): Promise { + public async leverageRepay({ stateCollateral, userCollateral, userBorrowed, router, calldata }: { + stateCollateral: TAmount, + userCollateral: TAmount, + userBorrowed: TAmount, + router: string, + calldata: string + }): Promise { this._checkLeverageZap(); - await this.leverageRepayApprove(userCollateral, userBorrowed); + await this.leverageRepayApprove({ userCollateral, userBorrowed }); return await this._leverageRepay(stateCollateral, userCollateral, userBorrowed, router, calldata, false) as string; } } \ No newline at end of file From ae65605d7bc00267a1af24ecdc650ab6f23f6997 Mon Sep 17 00:00:00 2001 From: fedorovdg Date: Fri, 12 Dec 2025 13:11:05 +0400 Subject: [PATCH 3/7] docs: update docs for leverageZapV2 --- README.md | 425 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 394 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index dd50dc0..9f2582c 100644 --- a/README.md +++ b/README.md @@ -947,7 +947,7 @@ await lendMarket.forceUpdateUserState(txHash, "0x123..."); })() ``` -### Leverage (createLoan, borrowMore, repay) for lendMarket +### Leverage (createLoan, borrowMore, repay) for lendMarket leverageZapV2 LendMarket leverage operations support different routers and require quote information from external routing services. @@ -1053,11 +1053,11 @@ interface IQuote { let userBorrowed = 1000; let debt = 2000; const range = 10; - await lendMarket.leverage.maxLeverage(range); + await lendMarket.leverageZapV2.maxLeverage(range); // 7.4728229145282742179 // Get maximum possible debt for given parameters - await lendMarket.leverage.createLoanMaxRecv(userCollateral, userBorrowed, range, getExpected); + await lendMarket.leverageZapV2.createLoanMaxRecv({ userCollateral, userBorrowed, range, getExpected }); // { // maxDebt: '26089.494406081862861214', // maxTotalCollateral: '9.539182089833411347', @@ -1071,7 +1071,7 @@ interface IQuote { // Get quote for swapping (debt + userBorrowed)!!! // Get expected collateral amount - await lendMarket.leverage.createLoanExpectedCollateral(userCollateral, userBorrowed, debt, quote); + await lendMarket.leverageZapV2.createLoanExpectedCollateral({ userCollateral, userBorrowed, debt, quote }); // { // totalCollateral: '1.946422996710829', // userCollateral: '1.0', @@ -1082,14 +1082,14 @@ interface IQuote { // } // NEW: Get all metrics in one call - const metrics = await lendMarket.leverage.createLoanExpectedMetrics( + const metrics = await lendMarket.leverageZapV2.createLoanExpectedMetrics({ userCollateral, userBorrowed, debt, range, quote, - true // healthIsFull - true for full health, false for not full - ); + healthIsFull: true // true for full health, false for not full + }); // { // priceImpact: 0.08944411854377342, // % // bands: [76, 67], @@ -1097,9 +1097,9 @@ interface IQuote { // health: '195.8994783042570637' // % // - await lendMarket.leverage.createLoanIsApproved(userCollateral, userBorrowed); + await lendMarket.leverageZapV2.createLoanIsApproved({ userCollateral, userBorrowed }); // false - await lendMarket.leverage.createLoanApprove(userCollateral, userBorrowed); + await lendMarket.leverageZapV2.createLoanApprove({ userCollateral, userBorrowed }); // [ // '0xd5491d9f1e9d8ac84b03867494e35b25efad151c597d2fa4211d7bf5d540c98e', // '0x93565f37ec5be902a824714a30bddc25cf9cd9ed39b4c0e8de61fab44af5bc8c' @@ -1107,7 +1107,7 @@ interface IQuote { // Create loan, passing router address and calldata from router - await lendMarket.leverage.createLoan(userCollateral, userBorrowed, debt, range, router, calldata); + await lendMarket.leverageZapV2.createLoan({ userCollateral, userBorrowed, debt, range, router, calldata }); // 0xeb1b7a92bcb02598f00dc8bbfe8fa3a554e7a2b1ca764e0ee45e2bf583edf731 await lendMarket.wallet.balances(); @@ -1152,7 +1152,7 @@ interface IQuote { debt = 10000; // Get maximum possible debt for given parameters - await lendMarket.leverage.borrowMoreMaxRecv(userCollateral, userBorrowed, getExpected); + await lendMarket.leverageZapV2.borrowMoreMaxRecv({ userCollateral, userBorrowed, getExpected }); // { // maxDebt: '76182.8497941193262889', // maxTotalCollateral: '26.639775583730298462', @@ -1164,7 +1164,7 @@ interface IQuote { // Get quote for swapping (debt + userBorrowed) - await lendMarket.leverage.borrowMoreExpectedCollateral(userCollateral, userBorrowed, debt, quote); + await lendMarket.leverageZapV2.borrowMoreExpectedCollateral({ userCollateral, userBorrowed, dDebt: debt, quote }); // { // totalCollateral: '5.783452104143246413', // userCollateral: '2.0', @@ -1174,13 +1174,13 @@ interface IQuote { // } // NEW: Get all metrics in one call - const metricsBM = await lendMarket.leverage.borrowMoreExpectedMetrics( + const metricsBM = await lendMarket.leverageZapV2.borrowMoreExpectedMetrics({ userCollateral, userBorrowed, debt, quote, - true // healthIsFull - true for full health, false for not full - ); + healthIsFull: true // true for full health, false for not full + }); // { // priceImpact: 0.010784277354269765, // % // bands: [47, 38], @@ -1188,13 +1188,13 @@ interface IQuote { // health: '91.6798951784708552' // % // } - await lendMarket.leverage.borrowMoreIsApproved(userCollateral, userBorrowed); + await lendMarket.leverageZapV2.borrowMoreIsApproved({ userCollateral, userBorrowed }); // true - await lendMarket.leverage.borrowMoreApprove(userCollateral, userBorrowed); + await lendMarket.leverageZapV2.borrowMoreApprove({ userCollateral, userBorrowed }); // [] // Execute borrowMore, passing router address and calldata from router - await lendMarket.leverage.borrowMore(userCollateral, userBorrowed, debt, router, calldata); + await lendMarket.leverageZapV2.borrowMore({ userCollateral, userBorrowed, debt, router, calldata }); // 0x6357dd6ea7250d7adb2344cd9295f8255fd8fbbe85f00120fbcd1ebf139e057c await lendMarket.wallet.balances(); @@ -1243,7 +1243,7 @@ interface IQuote { // Get quote for swapping (stateCollateral + userCollateral) - await lendMarket.leverage.repayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed, quote); + await lendMarket.leverageZapV2.repayExpectedBorrowed({ stateCollateral, userCollateral, userBorrowed, quote }); // { // totalBorrowed: '10998.882838599741571472', // borrowedFromStateCollateral: '6332.588559066494374648', @@ -1252,19 +1252,20 @@ interface IQuote { // avgPrice: '3166.29427953324743125312' // } - await lendMarket.leverage.repayIsFull(stateCollateral, userCollateral, userBorrowed, quote); + await lendMarket.leverageZapV2.repayIsFull({ stateCollateral, userCollateral, userBorrowed, quote }); // false - await lendMarket.leverage.repayIsAvailable(stateCollateral, userCollateral, userBorrowed, quote); + await lendMarket.leverageZapV2.repayIsAvailable({ stateCollateral, userCollateral, userBorrowed, quote }); // true // NEW: Get all metrics in one call - const metricsRepay = await lendMarket.leverage.repayExpectedMetrics( + const metricsRepay = await lendMarket.leverageZapV2.repayExpectedMetrics({ stateCollateral, userCollateral, userBorrowed, - true, // healthIsFull - true for full health, false for not full - quote - ); + healthIsFull: true, // true for full health, false for not full + quote, + address: llamalend.signerAddress + }); // { // priceImpact: 0.013150142802201724, // % // bands: [199, 190], @@ -1272,13 +1273,13 @@ interface IQuote { // health: '1699.6097751079226865' // % // } - await lendMarket.leverage.repayIsApproved(userCollateral, userBorrowed); + await lendMarket.leverageZapV2.repayIsApproved({ userCollateral, userBorrowed }); // false - await lendMarket.leverage.repayApprove(userCollateral, userBorrowed); + await lendMarket.leverageZapV2.repayApprove({ userCollateral, userBorrowed }); // ['0xd8a8d3b3f67395e1a4f4d4f95b041edcaf1c9f7bab5eb8a8a767467678295498'] // Execute repay, passing router address and calldata from router - await lendMarket.leverage.repay(stateCollateral, userCollateral, userBorrowed, router, calldata); + await lendMarket.leverageZapV2.repay({ stateCollateral, userCollateral, userBorrowed, router, calldata }); // 0xe48a97fef1c54180a2c7d104d210a95ac1a516fdd22109682179f1582da23a82 await lendMarket.wallet.balances(); @@ -1318,7 +1319,7 @@ interface IQuote { const debt = 2000; // Get maximum values for all possible ranges - await lendMarket.leverage.createLoanMaxRecvAllRanges(userCollateral, userBorrowed, getExpected); + await lendMarket.leverageZapV2.createLoanMaxRecvAllRanges({ userCollateral, userBorrowed, getExpected }); // { // '4': { // maxDebt: '37916.338071504823875251', @@ -1371,7 +1372,369 @@ interface IQuote { // Get quote for specific debt + userBorrowed // and provide getExpected callback - await lendMarket.leverage.createLoanBandsAllRanges(userCollateral, userBorrowed, debt, getExpected, quote); + await lendMarket.leverageZapV2.createLoanBandsAllRanges({ userCollateral, userBorrowed, debt, getExpected, quote }); + // { + // '4': [ 73, 70 ], + // '5': [ 73, 69 ], + // '6': [ 74, 69 ], + // '7': [ 74, 68 ], + // + // ... + // + // '50': [ 97, 48 ] + // } + + await lendMarket.leverageZapV2.createLoanPricesAllRanges({ userCollateral, userBorrowed, debt, getExpected, quote }); + // { + // '4': [ '1073.323292757532604807', '1136.910693647788699808' ], + // '5': [ '1073.323292757532604807', '1153.387660222394333133' ], + // '6': [ '1057.990102860996424743', '1153.387660222394333133' ], + // '7': [ '1057.990102860996424743', '1170.103423414023236507' ], + // + // ... + // + // '50': [ '759.898822708156242647', '1560.282492846180089068' ] + // } +``` + +### Leverage (createLoan, borrowMore, repay) for lendMarket (Deprecated!!! Will be deleted in the future, please use leverageZapV2) +```ts +(async () => { + await llamalend.init('JsonRpc', {}, {}, API_KEY_1INCH); + await llamalend.lendMarkets.fetchMarkets(); + + const lendMarket = llamalend.getLendMarket('one-way-market-0'); + console.log(lendMarket.collateral_token, lendMarket.borrowed_token); + // { + // address: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1', + // decimals: 18, + // name: 'Wrapped Ether', + // symbol: 'WETH' + // } + // + // { + // address: '0x498bf2b1e120fed3ad3d42ea2165e9b73f99c1e5', + // decimals: 18, + // name: 'curve.finance USD Stablecoin', + // symbol: 'crvUSD' + // } + console.log(await lendMarket.wallet.balances()); + // { + // collateral: '100.0', + // borrowed: '2000000.0', + // vaultShares: '0.0', + // gauge: '0' + // } + + + // - Create Loan - + + // Creates leveraged position (userCollateral + collateralFromUserBorrowed + leverage_collateral) + // ^ + // | + // userCollateral | debt debt + userBorrowed + // user ---> controller ----> leverage_zap ----> router + // | ^ | ^ ^ | + // | |__________________| | |___________________| + // | leverageCollateral + collateralFromUserBorrowed + // |_____________________________________| + // userBorrowed + + let userCollateral = 1; + let userBorrowed = 1000; + let debt = 2000; + const range = 10; + const slippage = 0.5; // % + await lendMarket.leverage.maxLeverage(range); + // 7.4728229145282742179 + await lendMarket.leverage.createLoanMaxRecv(userCollateral, userBorrowed, range); + // { + // maxDebt: '26089.494406081862861214', + // maxTotalCollateral: '9.539182089833411347', + // userCollateral: '1', + // collateralFromUserBorrowed: '0.315221168834966496', + // collateralFromMaxDebt: '8.223960920998444851', + // maxLeverage: '7.25291100528992828612', + // avgPrice: '3172.3757757003568790858' + // } + await lendMarket.leverage.createLoanExpectedCollateral(userCollateral, userBorrowed, debt, slippage); + // { + // totalCollateral: '1.946422996710829', + // userCollateral: '1.0', + // collateralFromUserBorrowed: '0.315474332236942984', + // collateralFromDebt: '0.630948664473886', + // leverage: '1.4796358613861877' + // avgPrice: '3169.8299919022623523421' + // } + await lendMarket.leverage.createLoanPriceImpact(userBorrowed, debt); + // 0.08944411854377342 % + await lendMarket.leverage.createLoanMaxRange(userCollateral, userBorrowed, debt); + // 50 + await lendMarket.leverage.createLoanBands(userCollateral, userBorrowed, debt, range); + // [ 76, 67 ] + await lendMarket.leverage.createLoanPrices(userCollateral, userBorrowed, debt, range); + // [ '1027.977701011670136614', '1187.061409925215211173' ] + await lendMarket.leverage.createLoanHealth(userCollateral, userBorrowed, debt, range); + // 195.8994783042570637 + await lendMarket.leverage.createLoanHealth(userCollateral, userBorrowed, debt, range, false); + // 3.2780908310686365 + await lendMarket.leverage.createLoanIsApproved(userCollateral, userBorrowed); + // false + await lendMarket.leverage.createLoanApprove(userCollateral, userBorrowed); + // [ + // '0xd5491d9f1e9d8ac84b03867494e35b25efad151c597d2fa4211d7bf5d540c98e', + // '0x93565f37ec5be902a824714a30bddc25cf9cd9ed39b4c0e8de61fab44af5bc8c' + // ] + await lendMarket.leverage.createLoanRouteImage(userBorrowed, debt); + // 'data:image/svg+xml;base64,PHN2ZyBpZD0ic2Fua2V5UGFyZW50U3ZnIiB4bWxucz...' + + + // You must call lendMarket.leverage.createLoanExpectedCollateral() with the same args before + await lendMarket.leverage.createLoan(userCollateral, userBorrowed, debt, range); + // 0xeb1b7a92bcb02598f00dc8bbfe8fa3a554e7a2b1ca764e0ee45e2bf583edf731 + + await lendMarket.wallet.balances(); + // { + // collateral: '99.0', + // borrowed: '599000.0', + // vaultShares: '1400000000.0', + // gauge: '0' + // } + await lendMarket.userState(); + // { + // collateral: '1.945616160868693648', + // borrowed: '0.0', + // debt: '2000.0', + // N: '10' + // } + await lendMarket.userBands(); + // [ 76, 67 ] + await lendMarket.userPrices(); + // [ '1027.977718614028011906', '1187.061430251609195098' ] + await lendMarket.userHealth(); + // 195.8372633833293605 + await lendMarket.userHealth(false); + // 3.2518122092914609 + + + // - Borrow More - + + // Updates leveraged position (dCollateral = userCollateral + collateralFromUserBorrowed + leverageCollateral) + // ^ + // | + // userCollateral | dDebt dDebt + userBorrowed + // user ---> controller ----> leverage_zap ----> router + // | ^ | ^ ^ | + // | |__________________| | |___________________| + // | leverageCollateral + collateralFromUSerBorrowed + // |_____________________________________| + // userBorrowed + + userCollateral = 2; + userBorrowed = 2000; + debt = 10000; + await lendMarket.leverage.borrowMoreMaxRecv(userCollateral, userBorrowed); + // { + // maxDebt: '76182.8497941193262889', + // maxTotalCollateral: '26.639775583730298462', + // userCollateral: '2', + // collateralFromUserBorrowed: '1.677318306610359627', + // collateralFromMaxDebt: '22.962457277119938834', + // avgPrice: '3172.55402418338331369083' + // } + await lendMarket.leverage.borrowMoreExpectedCollateral(userCollateral, userBorrowed, debt, slippage); + // { + // totalCollateral: '5.783452104143246413', + // userCollateral: '2.0', + // collateralFromUserBorrowed: '0.630575350690541071', + // collateralFromDebt: '3.152876753452705342' + // avgPrice: '3171.70659749038129067231' + // } + await lendMarket.leverage.borrowMorePriceImpact(userBorrowed, debt); + // 0.010784277354269765 % + await lendMarket.leverage.borrowMoreBands(userCollateral, userBorrowed, debt); + // [ 47, 38 ] + await lendMarket.leverage.borrowMorePrices(userCollateral, userBorrowed, debt); + // [ '1560.282474721398939216', '1801.742501325928269008' ] + await lendMarket.leverage.borrowMoreHealth(userCollateral, userBorrowed, debt, true); + // 91.6798951784708552 + await lendMarket.leverage.borrowMoreHealth(userCollateral, userBorrowed, debt, false); + // 3.7614279042995641 + await lendMarket.leverage.borrowMoreIsApproved(userCollateral, userBorrowed); + // true + await lendMarket.leverage.borrowMoreApprove(userCollateral, userBorrowed); + // [] + await lendMarket.leverage.borrowMoreRouteImage(userBorrowed, debt); + // 'data:image/svg+xml;base64,PHN2ZyBpZD0ic2Fua2V5UGFyZW50U3ZnIiB4bWxucz...' + + // You must call lendMarket.leverage.borrowMoreExpectedCollateral() with the same args before + await lendMarket.leverage.borrowMore(userCollateral, userBorrowed, debt, slippage); + // 0x6357dd6ea7250d7adb2344cd9295f8255fd8fbbe85f00120fbcd1ebf139e057c + + await lendMarket.wallet.balances(); + // { + // collateral: '97.0', + // borrowed: '597000.0', + // vaultShares: '1400000000.0', + // gauge: '0' + // } + await lendMarket.userState(); + // { + // collateral: '7.727839965845165558', + // borrowed: '0.0', + // debt: '12000.000010193901375446', + // N: '10' + // } + await lendMarket.userBands(); + // [ 47, 38 ] + await lendMarket.userPrices(); + // [ '1560.28248267408177179', '1801.742510509320950242' ] + await lendMarket.userHealth(); + // 91.6519475547753288 + await lendMarket.userHealth(false); + // 3.7449386373872907 + + + // - Repay - + + + // Deleveraged position (-dDebt = borrowedFromStateCollateral + borrowedFromUSerCollateral + userBorrowed) + // ^ + // | userCollateral + // user ___|__________________________ + // | | + // | | stateCollateral ↓ userCollateral + stateCollateral + // | controller --> leverage_zap --> router + // | ^ | ^ ^ | + // | |______________________| | |___________________| + // | | borrowedFromStateCollateral + // |________________________________| + + // userBorrowed borrowedFromUSerCollateral + + const stateCollateral = 2; + userCollateral = 1; + userBorrowed = 1500; + await lendMarket.leverage.repayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed, slippage); + // { + // totalBorrowed: '10998.882838599741571472', + // borrowedFromStateCollateral: '6332.588559066494374648', + // borrowedFromUserCollateral: '3166.294279533247196824', + // userBorrowed: '1500' + // avgPrice: '3166.29427953324743125312' + // } + + await lendMarket.leverage.repayPriceImpact(stateCollateral, userCollateral); + // 0.013150142802201724 % + await lendMarket.leverage.repayIsFull(stateCollateral, userCollateral, userBorrowed); + // false + await lendMarket.leverage.repayIsAvailable(stateCollateral, userCollateral, userBorrowed); + // true + await lendMarket.leverage.repayBands(stateCollateral, userCollateral, userBorrowed); + // [ 199, 190 ] + await lendMarket.leverage.repayPrices(stateCollateral, userCollateral, userBorrowed); + // [ '175.130965754280721633', '202.233191367561902757' ] + await lendMarket.leverage.repayHealth(stateCollateral, userCollateral, userBorrowed, true); + // 1699.6097751079226865 + await lendMarket.leverage.repayHealth(stateCollateral, userCollateral, userBorrowed, false); + // 3.4560086962806991 + await lendMarket.leverage.repayIsApproved(userCollateral, userBorrowed); + // false + await lendMarket.leverage.repayApprove(userCollateral, userBorrowed); + // ['0xd8a8d3b3f67395e1a4f4d4f95b041edcaf1c9f7bab5eb8a8a767467678295498'] + await lendMarket.leverage.repayRouteImage(stateCollateral, userCollateral); + // 'data:image/svg+xml;base64,PHN2ZyBpZD0ic2Fua2V5UGFyZW50U3ZnIiB4bWxucz...' + + // You must call lendMarket.leverage.repayExpectedBorrowed() with the same args before + await lendMarket.leverage.repay(stateCollateral, userCollateral, userBorrowed, slippage); + // 0xe48a97fef1c54180a2c7d104d210a95ac1a516fdd22109682179f1582da23a82 + + await lendMarket.wallet.balances(); + // { + // collateral: '96.0', + // borrowed: '595500.0', + // vaultShares: '1400000000.0', + // gauge: '0' + // } + await lendMarket.userState(); + // { + // collateral: '5.727839965845165558', + // borrowed: '0.0', + // debt: '992.083214663467727334', + // N: '10' + // } + await lendMarket.userBands(); + // [ 199, 190 ] + await lendMarket.userPrices(); + // [ '175.13096689602455189', '202.233192685995210783' ] + await lendMarket.userHealth(); + // 1716.0249924305707883 + await lendMarket.userHealth(false); + // 3.6389352509210336 +})() +``` + +### Leverage createLoan all ranges methods for lendMarket +```ts + await llamalend.init('JsonRpc', {}, {}, API_KEY_1INCH); + await llamalend.lendMarkets.fetchMarkets(); + + const lendMarket = llamalend.getLendMarket('one-way-market-0'); + + const userCollateral = 1; + const userBorrowed = 1000; + const debt = 2000; + await lendMarket.leverage.createLoanMaxRecvAllRanges(userCollateral, userBorrowed); + // { + // '4': { + // maxDebt: '37916.338071504823875251', + // maxTotalCollateral: '13.286983617364703479', + // userCollateral: '1', + // collateralFromUserBorrowed: '0.315728154966395280', + // collateralFromMaxDebt: '11.971255462398308199', + // maxLeverage: '10.09857816541446843865', + // avgPrice: '3167.28167656266072703689' + // }, + // '5': { + // maxDebt: '35363.440522143354729759', + // maxTotalCollateral: '12.480961984286574804', + // userCollateral: '1', + // collateralFromUserBorrowed: '0.315728154966395280', + // collateralFromMaxDebt: '11.165233829320179524', + // maxLeverage: '9.48597317551918486951', + // avgPrice: '3167.28167656266072703689' + // }, + // '6': { + // maxDebt: '33122.824118147617102062', + // maxTotalCollateral: '11.773536301065561222', + // userCollateral: '1', + // collateralFromUserBorrowed: '0.315728154966395280', + // collateralFromMaxDebt: '10.457808146099165942', + // maxLeverage: '8.94830459971897955699', + // avgPrice: '3167.28167656266072703689' + // }, + // '7': { + // maxDebt: '31140.555201395785060968', + // maxTotalCollateral: '11.147678193332270290', + // userCollateral: '1', + // collateralFromUserBorrowed: '0.315728154966395280', + // collateralFromMaxDebt: '9.831950038365875010', + // maxLeverage: '8.47263027035929823721', + // avgPrice: '3167.28167656266072703689' + // }, + // + // ... + // + // '50': { + // maxDebt: '8122.705063645852013929', + // maxTotalCollateral: '3.880294838047496482', + // userCollateral: '1', + // collateralFromUserBorrowed: '0.315728154966395280', + // collateralFromMaxDebt: '2.564566683081101202', + // maxLeverage: '2.94916151440614435181', + // avgPrice: '3167.28167656266072703689' + // } + + await lendMarket.leverage.createLoanBandsAllRanges(userCollateral, userBorrowed, debt); // { // '4': [ 73, 70 ], // '5': [ 73, 69 ], @@ -1383,7 +1746,7 @@ interface IQuote { // '50': [ 97, 48 ] // } - await lendMarket.leverage.createLoanPricesAllRanges(userCollateral, userBorrowed, debt, getExpected, quote); + await lendMarket.leverage.createLoanPricesAllRanges(userCollateral, userBorrowed, debt); // { // '4': [ '1073.323292757532604807', '1136.910693647788699808' ], // '5': [ '1073.323292757532604807', '1153.387660222394333133' ], From ac0466d68a24a02d480e7f3016341240ad09d8ec Mon Sep 17 00:00:00 2001 From: fedorovdg Date: Fri, 12 Dec 2025 13:12:24 +0400 Subject: [PATCH 4/7] build: v1.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e25bb1c..9b96989 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@curvefi/llamalend-api", - "version": "1.0.42", + "version": "1.1.0", "description": "JavaScript library for Curve Lending", "main": "lib/index.js", "author": "Macket", From 9e0b66ce93112b2e43b30d12fa6db97152013cbf Mon Sep 17 00:00:00 2001 From: fedorovdg Date: Fri, 12 Dec 2025 13:25:42 +0400 Subject: [PATCH 5/7] refactor: fix lint issues --- src/lendMarkets/LendMarketTemplate.ts | 112 ++++++++-------- src/lendMarkets/interfaces/leverageZapV2.ts | 20 +-- src/lendMarkets/modules/leverageZapV2.ts | 136 ++++++++++---------- 3 files changed, 133 insertions(+), 135 deletions(-) diff --git a/src/lendMarkets/LendMarketTemplate.ts b/src/lendMarkets/LendMarketTemplate.ts index 718c039..b4d9d13 100644 --- a/src/lendMarkets/LendMarketTemplate.ts +++ b/src/lendMarkets/LendMarketTemplate.ts @@ -821,45 +821,44 @@ export class LendMarketTemplate { } private vaultRewardTokens = memoize(async (): Promise<{token: string, symbol: string, decimals: number}[]> => { - if (this.addresses.gauge === this.llamalend.constants.ZERO_ADDRESS) return [] + if (this.addresses.gauge === this.llamalend.constants.ZERO_ADDRESS) return [] - // if (useApi) { - // const rewards = await _getRewardsFromApi(); - // if (!rewards[this.addresses.gauge]) return []; - // rewards[this.addresses.gauge].forEach((r) => _setContracts(r.tokenAddress, ERC20Abi)); - // return rewards[this.addresses.gauge].map((r) => ({ token: r.tokenAddress, symbol: r.symbol, decimals: Number(r.decimals) })); - // } + // if (useApi) { + // const rewards = await _getRewardsFromApi(); + // if (!rewards[this.addresses.gauge]) return []; + // rewards[this.addresses.gauge].forEach((r) => _setContracts(r.tokenAddress, ERC20Abi)); + // return rewards[this.addresses.gauge].map((r) => ({ token: r.tokenAddress, symbol: r.symbol, decimals: Number(r.decimals) })); + // } - const gaugeContract = this.llamalend.contracts[this.addresses.gauge].contract; - const gaugeMulticallContract = this.llamalend.contracts[this.addresses.gauge].multicallContract; - const rewardCount = Number(this.llamalend.formatUnits(await gaugeContract.reward_count(this.llamalend.constantOptions), 0)); + const gaugeContract = this.llamalend.contracts[this.addresses.gauge].contract; + const gaugeMulticallContract = this.llamalend.contracts[this.addresses.gauge].multicallContract; + const rewardCount = Number(this.llamalend.formatUnits(await gaugeContract.reward_count(this.llamalend.constantOptions), 0)); - const tokenCalls = []; - for (let i = 0; i < rewardCount; i++) { - tokenCalls.push(gaugeMulticallContract.reward_tokens(i)); - } - const tokens = (await this.llamalend.multicallProvider.all(tokenCalls) as string[]) - .filter((addr) => addr !== this.llamalend.constants.ZERO_ADDRESS) - .map((addr) => addr.toLowerCase()) - .filter((addr) => this.llamalend.chainId === 1 || addr !== this.llamalend.constants.COINS.crv); - - const tokenInfoCalls = []; - for (const token of tokens) { - this.llamalend.setContract(token, ERC20Abi); - const tokenMulticallContract = this.llamalend.contracts[token].multicallContract; - tokenInfoCalls.push(tokenMulticallContract.symbol(), tokenMulticallContract.decimals()); - } - const tokenInfo = await this.llamalend.multicallProvider.all(tokenInfoCalls); - for (let i = 0; i < tokens.length; i++) { - this.llamalend.constants.DECIMALS[tokens[i]] = Number(tokenInfo[(i * 2) + 1]); - } + const tokenCalls = []; + for (let i = 0; i < rewardCount; i++) { + tokenCalls.push(gaugeMulticallContract.reward_tokens(i)); + } + const tokens = (await this.llamalend.multicallProvider.all(tokenCalls) as string[]) + .filter((addr) => addr !== this.llamalend.constants.ZERO_ADDRESS) + .map((addr) => addr.toLowerCase()) + .filter((addr) => this.llamalend.chainId === 1 || addr !== this.llamalend.constants.COINS.crv); - return tokens.map((token, i) => ({ token, symbol: tokenInfo[i * 2] as string, decimals: Number(tokenInfo[(i * 2) + 1]) })); - }, - { - promise: true, - maxAge: 30 * 60 * 1000, // 30m - }); + const tokenInfoCalls = []; + for (const token of tokens) { + this.llamalend.setContract(token, ERC20Abi); + const tokenMulticallContract = this.llamalend.contracts[token].multicallContract; + tokenInfoCalls.push(tokenMulticallContract.symbol(), tokenMulticallContract.decimals()); + } + const tokenInfo = await this.llamalend.multicallProvider.all(tokenInfoCalls); + for (let i = 0; i < tokens.length; i++) { + this.llamalend.constants.DECIMALS[tokens[i]] = Number(tokenInfo[(i * 2) + 1]); + } + + return tokens.map((token, i) => ({ token, symbol: tokenInfo[i * 2] as string, decimals: Number(tokenInfo[(i * 2) + 1]) })); + }, { + promise: true, + maxAge: 30 * 60 * 1000, // 30m + }); private vaultRewardsApr = async (useApi = true): Promise => { if(useApi) { @@ -971,30 +970,29 @@ export class LendMarketTemplate { base_price: string, A: string, }> => { - const llammaContract = this.llamalend.contracts[this.addresses.amm].multicallContract; - const controllerContract = this.llamalend.contracts[this.addresses.controller].multicallContract; + const llammaContract = this.llamalend.contracts[this.addresses.amm].multicallContract; + const controllerContract = this.llamalend.contracts[this.addresses.controller].multicallContract; - const calls = [ - llammaContract.fee(), - llammaContract.admin_fee(), - controllerContract.liquidation_discount(), - controllerContract.loan_discount(), - llammaContract.get_base_price(), - llammaContract.A(), - ] - - const [_fee, _admin_fee, _liquidation_discount, _loan_discount, _base_price, _A]: bigint[] = await this.llamalend.multicallProvider.all(calls) as bigint[]; - const A = formatUnits(_A, 0) - const base_price = formatUnits(_base_price) - const [fee, admin_fee, liquidation_discount, loan_discount] = [_fee, _admin_fee, _liquidation_discount, _loan_discount] - .map((_x) => formatUnits(_x * BigInt(100))); + const calls = [ + llammaContract.fee(), + llammaContract.admin_fee(), + controllerContract.liquidation_discount(), + controllerContract.loan_discount(), + llammaContract.get_base_price(), + llammaContract.A(), + ] - return { fee, admin_fee, liquidation_discount, loan_discount, base_price, A } - }, - { - promise: true, - maxAge: 5 * 60 * 1000, // 5m - }); + const [_fee, _admin_fee, _liquidation_discount, _loan_discount, _base_price, _A]: bigint[] = await this.llamalend.multicallProvider.all(calls) as bigint[]; + const A = formatUnits(_A, 0) + const base_price = formatUnits(_base_price) + const [fee, admin_fee, liquidation_discount, loan_discount] = [_fee, _admin_fee, _liquidation_discount, _loan_discount] + .map((_x) => formatUnits(_x * BigInt(100))); + + return { fee, admin_fee, liquidation_discount, loan_discount, base_price, A } + }, { + promise: true, + maxAge: 5 * 60 * 1000, // 5m + }); private _getRate = async (isGetter = true): Promise => { let _rate; diff --git a/src/lendMarkets/interfaces/leverageZapV2.ts b/src/lendMarkets/interfaces/leverageZapV2.ts index fbb14aa..d5f1a88 100644 --- a/src/lendMarkets/interfaces/leverageZapV2.ts +++ b/src/lendMarkets/interfaces/leverageZapV2.ts @@ -9,7 +9,7 @@ export interface ILeverageZapV2 { userCollateral, userBorrowed, range, - getExpected + getExpected, }: { userCollateral: TAmount, userBorrowed: TAmount, @@ -27,7 +27,7 @@ export interface ILeverageZapV2 { createLoanMaxRecvAllRanges: ({ userCollateral, userBorrowed, - getExpected + getExpected, }: { userCollateral: TAmount, userBorrowed: TAmount, @@ -45,7 +45,7 @@ export interface ILeverageZapV2 { userCollateral, userBorrowed, debt, - quote + quote, }: { userCollateral: TAmount, userBorrowed: TAmount, @@ -65,7 +65,7 @@ export interface ILeverageZapV2 { debt, range, quote, - healthIsFull + healthIsFull, }: { userCollateral: TAmount, userBorrowed: TAmount, @@ -78,7 +78,7 @@ export interface ILeverageZapV2 { userCollateral, userBorrowed, debt, - getExpected + getExpected, }: { userCollateral: TAmount, userBorrowed: TAmount, @@ -90,7 +90,7 @@ export interface ILeverageZapV2 { userBorrowed, debt, getExpected, - quote + quote, }: { userCollateral: TAmount, userBorrowed: TAmount, @@ -103,7 +103,7 @@ export interface ILeverageZapV2 { userBorrowed, debt, getExpected, - quote + quote, }: { userCollateral: TAmount, userBorrowed: TAmount, @@ -113,14 +113,14 @@ export interface ILeverageZapV2 { }) => Promise>, createLoanIsApproved: ({ userCollateral, - userBorrowed + userBorrowed, }: { userCollateral: TAmount, userBorrowed: TAmount }) => Promise, createLoanApprove: ({ userCollateral, - userBorrowed + userBorrowed, }: { userCollateral: TAmount, userBorrowed: TAmount @@ -131,7 +131,7 @@ export interface ILeverageZapV2 { debt, range, router, - calldata + calldata, }: { userCollateral: TAmount, userBorrowed: TAmount, diff --git a/src/lendMarkets/modules/leverageZapV2.ts b/src/lendMarkets/modules/leverageZapV2.ts index 9b45c1f..9a489e3 100644 --- a/src/lendMarkets/modules/leverageZapV2.ts +++ b/src/lendMarkets/modules/leverageZapV2.ts @@ -150,79 +150,79 @@ export class LeverageZapV2Module { maxLeverage: string, avgPrice: string, }>> => { - this._checkLeverageZap(); - const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals); - const contract = this.llamalend.contracts[this.llamalend.constants.ALIASES.leverage_zap_v2].multicallContract; - - const oraclePriceBand = await this.market.oraclePriceBand(); - const pAvgApproxBN = BN(await this.market.calcTickPrice(oraclePriceBand)); // upper tick of oracle price band - let pAvgBN: BigNumber | null = null; - const arrLength = this.market.maxBands - this.market.minBands + 1; - let maxLeverageCollateralBN: BigNumber[] = new Array(arrLength).fill(BN(0)); - let _maxLeverageCollateral: bigint[] = new Array(arrLength).fill(BigInt(0)); - let maxBorrowablePrevBN: BigNumber[] = new Array(arrLength).fill(BN(0)); - let maxBorrowableBN: BigNumber[] = new Array(arrLength).fill(BN(0)); - let _maxBorrowable: bigint[] = new Array(arrLength).fill(BigInt(0)); - - for (let i = 0; i < 5; i++) { - const pBN = pAvgBN ?? pAvgApproxBN; - maxBorrowablePrevBN = maxBorrowableBN; - const _userEffectiveCollateral: bigint = _userCollateral + fromBN(BN(userBorrowed).div(pBN), this.market.collateral_token.decimals); - const calls = []; - for (let N = this.market.minBands; N <= this.market.maxBands; N++) { - const j = N - this.market.minBands; - calls.push(contract.max_borrowable(this.market.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral[j], N, fromBN(pBN))); - } - _maxBorrowable = (await this.llamalend.multicallProvider.all(calls) as bigint[]).map((_mb) => _mb * BigInt(998) / BigInt(1000)); - maxBorrowableBN = _maxBorrowable.map((_mb) => toBN(_mb, this.market.borrowed_token.decimals)); - - const deltaBN = maxBorrowableBN.map((mb, l) => mb.minus(maxBorrowablePrevBN[l]).abs().div(mb)); - if (BigNumber.max(...deltaBN).lt(0.0005)) { - maxBorrowableBN = maxBorrowablePrevBN; - break; - } - - if (pAvgBN === null){ - const _y = BigInt((await getExpected( - this.market.addresses.borrowed_token, - this.market.addresses.collateral_token, - _maxBorrowable[0], - this.market.addresses.amm - )).outAmount); - const yBN = toBN(_y, this.market.collateral_token.decimals); - pAvgBN = maxBorrowableBN[0].div(yBN); - } - - maxLeverageCollateralBN = maxBorrowableBN.map((mb) => mb.div(pAvgBN as BigNumber)); - _maxLeverageCollateral = maxLeverageCollateralBN.map((mlc) => fromBN(mlc, this.market.collateral_token.decimals)); - } + this._checkLeverageZap(); + const _userCollateral = parseUnits(userCollateral, this.market.collateral_token.decimals); + const contract = this.llamalend.contracts[this.llamalend.constants.ALIASES.leverage_zap_v2].multicallContract; - const userEffectiveCollateralBN = BN(userCollateral).plus(BN(userBorrowed).div(pAvgBN as BigNumber)); - - const res: IDict<{ - maxDebt: string, - maxTotalCollateral: string, - userCollateral: string, - collateralFromUserBorrowed: string, - collateralFromMaxDebt: string, - maxLeverage: string, - avgPrice: string, - }> = {}; + const oraclePriceBand = await this.market.oraclePriceBand(); + const pAvgApproxBN = BN(await this.market.calcTickPrice(oraclePriceBand)); // upper tick of oracle price band + let pAvgBN: BigNumber | null = null; + const arrLength = this.market.maxBands - this.market.minBands + 1; + let maxLeverageCollateralBN: BigNumber[] = new Array(arrLength).fill(BN(0)); + let _maxLeverageCollateral: bigint[] = new Array(arrLength).fill(BigInt(0)); + let maxBorrowablePrevBN: BigNumber[] = new Array(arrLength).fill(BN(0)); + let maxBorrowableBN: BigNumber[] = new Array(arrLength).fill(BN(0)); + let _maxBorrowable: bigint[] = new Array(arrLength).fill(BigInt(0)); + + for (let i = 0; i < 5; i++) { + const pBN = pAvgBN ?? pAvgApproxBN; + maxBorrowablePrevBN = maxBorrowableBN; + const _userEffectiveCollateral: bigint = _userCollateral + fromBN(BN(userBorrowed).div(pBN), this.market.collateral_token.decimals); + const calls = []; for (let N = this.market.minBands; N <= this.market.maxBands; N++) { const j = N - this.market.minBands; - res[N] = { - maxDebt: formatNumber(maxBorrowableBN[j].toString(), this.market.borrowed_token.decimals), - maxTotalCollateral: formatNumber(maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).toString(), this.market.collateral_token.decimals), - userCollateral: formatNumber(userCollateral, this.market.collateral_token.decimals), - collateralFromUserBorrowed: formatNumber(BN(userBorrowed).div(pAvgBN as BigNumber).toString(), this.market.collateral_token.decimals), - collateralFromMaxDebt: formatNumber(maxLeverageCollateralBN[j].toString(), this.market.collateral_token.decimals), - maxLeverage: maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).div(userEffectiveCollateralBN).toString(), - avgPrice: (pAvgBN as BigNumber).toString(), - }; + calls.push(contract.max_borrowable(this.market.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral[j], N, fromBN(pBN))); } + _maxBorrowable = (await this.llamalend.multicallProvider.all(calls) as bigint[]).map((_mb) => _mb * BigInt(998) / BigInt(1000)); + maxBorrowableBN = _maxBorrowable.map((_mb) => toBN(_mb, this.market.borrowed_token.decimals)); - return res; - }, + const deltaBN = maxBorrowableBN.map((mb, l) => mb.minus(maxBorrowablePrevBN[l]).abs().div(mb)); + if (BigNumber.max(...deltaBN).lt(0.0005)) { + maxBorrowableBN = maxBorrowablePrevBN; + break; + } + + if (pAvgBN === null){ + const _y = BigInt((await getExpected( + this.market.addresses.borrowed_token, + this.market.addresses.collateral_token, + _maxBorrowable[0], + this.market.addresses.amm + )).outAmount); + const yBN = toBN(_y, this.market.collateral_token.decimals); + pAvgBN = maxBorrowableBN[0].div(yBN); + } + + maxLeverageCollateralBN = maxBorrowableBN.map((mb) => mb.div(pAvgBN as BigNumber)); + _maxLeverageCollateral = maxLeverageCollateralBN.map((mlc) => fromBN(mlc, this.market.collateral_token.decimals)); + } + + const userEffectiveCollateralBN = BN(userCollateral).plus(BN(userBorrowed).div(pAvgBN as BigNumber)); + + const res: IDict<{ + maxDebt: string, + maxTotalCollateral: string, + userCollateral: string, + collateralFromUserBorrowed: string, + collateralFromMaxDebt: string, + maxLeverage: string, + avgPrice: string, + }> = {}; + for (let N = this.market.minBands; N <= this.market.maxBands; N++) { + const j = N - this.market.minBands; + res[N] = { + maxDebt: formatNumber(maxBorrowableBN[j].toString(), this.market.borrowed_token.decimals), + maxTotalCollateral: formatNumber(maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).toString(), this.market.collateral_token.decimals), + userCollateral: formatNumber(userCollateral, this.market.collateral_token.decimals), + collateralFromUserBorrowed: formatNumber(BN(userBorrowed).div(pAvgBN as BigNumber).toString(), this.market.collateral_token.decimals), + collateralFromMaxDebt: formatNumber(maxLeverageCollateralBN[j].toString(), this.market.collateral_token.decimals), + maxLeverage: maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).div(userEffectiveCollateralBN).toString(), + avgPrice: (pAvgBN as BigNumber).toString(), + }; + } + + return res; + }, { promise: true, maxAge: 60 * 1000, // 1m From 885eaef5ba77571ccfb1b5865dc6dbe989917dcf Mon Sep 17 00:00:00 2001 From: fedorovdg Date: Fri, 12 Dec 2025 13:45:58 +0400 Subject: [PATCH 6/7] chore: add lint:fix --- package.json | 1 + src/cache/index.ts | 2 +- src/lendMarkets/LendMarketTemplate.ts | 342 +++++++++++------------ src/lendMarkets/modules/leverageZapV2.ts | 110 ++++---- 4 files changed, 228 insertions(+), 227 deletions(-) diff --git a/package.json b/package.json index 9b96989..f9ea56d 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "scripts": { "build": "rm -rf lib && tsc --project tsconfig.build.json", "lint": "eslint src --ext .ts", + "lint:fix": "eslint src --ext .ts --fix", "watch": "tsc --watch", "watch:lib": "rm -rf lib && tsc --watch --project tsconfig.build.json" }, diff --git a/src/cache/index.ts b/src/cache/index.ts index d42d1c9..0970130 100644 --- a/src/cache/index.ts +++ b/src/cache/index.ts @@ -1,5 +1,5 @@ class Cache { - // eslint-disable-next-line no-use-before-define + private static instance: Cache; readonly cache: Map; diff --git a/src/lendMarkets/LendMarketTemplate.ts b/src/lendMarkets/LendMarketTemplate.ts index b4d9d13..762763d 100644 --- a/src/lendMarkets/LendMarketTemplate.ts +++ b/src/lendMarkets/LendMarketTemplate.ts @@ -1086,23 +1086,23 @@ export class LendMarketTemplate { } private statsBandsInfo = memoize(async (): Promise<{ activeBand: number, maxBand: number, minBand: number, liquidationBand: number | null }> => { - const ammContract = this.llamalend.contracts[this.addresses.amm].multicallContract; - const calls = [ - ammContract.active_band_with_skip(), - ammContract.max_band(), - ammContract.min_band(), - ] - - const [activeBand, maxBand, minBand] = (await this.llamalend.multicallProvider.all(calls) as bigint[]).map((_b) => Number(_b)); - const { borrowed, collateral } = await this.statsBandBalances(activeBand); - let liquidationBand = null; - if (Number(borrowed) > 0 && Number(collateral) > 0) liquidationBand = activeBand; - return { activeBand, maxBand, minBand, liquidationBand } - }, - { - promise: true, - maxAge: 60 * 1000, // 1m - }); + const ammContract = this.llamalend.contracts[this.addresses.amm].multicallContract; + const calls = [ + ammContract.active_band_with_skip(), + ammContract.max_band(), + ammContract.min_band(), + ] + + const [activeBand, maxBand, minBand] = (await this.llamalend.multicallProvider.all(calls) as bigint[]).map((_b) => Number(_b)); + const { borrowed, collateral } = await this.statsBandBalances(activeBand); + let liquidationBand = null; + if (Number(borrowed) > 0 && Number(collateral) > 0) liquidationBand = activeBand; + return { activeBand, maxBand, minBand, liquidationBand } + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); private async statsBandBalances(n: number): Promise<{ borrowed: string, collateral: string }> { const ammContract = this.llamalend.contracts[this.addresses.amm].multicallContract; @@ -1253,31 +1253,31 @@ export class LendMarketTemplate { // ---------------- PRICES ---------------- public A = memoize(async(): Promise => { - const _A = await this.llamalend.contracts[this.addresses.amm].contract.A(this.llamalend.constantOptions) as bigint; - return formatUnits(_A, 0); - }, - { - promise: true, - maxAge: 86400 * 1000, // 1d - }); + const _A = await this.llamalend.contracts[this.addresses.amm].contract.A(this.llamalend.constantOptions) as bigint; + return formatUnits(_A, 0); + }, + { + promise: true, + maxAge: 86400 * 1000, // 1d + }); public basePrice = memoize(async(): Promise => { - const _price = await this.llamalend.contracts[this.addresses.amm].contract.get_base_price(this.llamalend.constantOptions) as bigint; - return formatUnits(_price); - }, - { - promise: true, - maxAge: 86400 * 1000, // 1d - }); + const _price = await this.llamalend.contracts[this.addresses.amm].contract.get_base_price(this.llamalend.constantOptions) as bigint; + return formatUnits(_price); + }, + { + promise: true, + maxAge: 86400 * 1000, // 1d + }); public oraclePrice = memoize(async (): Promise => { - const _price = await this.llamalend.contracts[this.addresses.amm].contract.price_oracle(this.llamalend.constantOptions) as bigint; - return formatUnits(_price); - }, - { - promise: true, - maxAge: 60 * 1000, // 1m - }); + const _price = await this.llamalend.contracts[this.addresses.amm].contract.price_oracle(this.llamalend.constantOptions) as bigint; + return formatUnits(_price); + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); public async oraclePriceBand(): Promise { const oraclePriceBN = BN(await this.oraclePrice()); @@ -1346,16 +1346,16 @@ export class LendMarketTemplate { } public _userState = memoize(async (address = ""): Promise<{ _collateral: bigint, _borrowed: bigint, _debt: bigint, _N: bigint }> => { - address = _getAddress.call(this.llamalend, address); - const contract = this.llamalend.contracts[this.addresses.controller].contract; - const [_collateral, _borrowed, _debt, _N] = await contract.user_state(address, this.llamalend.constantOptions) as bigint[]; + address = _getAddress.call(this.llamalend, address); + const contract = this.llamalend.contracts[this.addresses.controller].contract; + const [_collateral, _borrowed, _debt, _N] = await contract.user_state(address, this.llamalend.constantOptions) as bigint[]; - return { _collateral, _borrowed, _debt, _N } - }, - { - promise: true, - maxAge: 10 * 1000, // 10s - }); + return { _collateral, _borrowed, _debt, _N } + }, + { + promise: true, + maxAge: 10 * 1000, // 10s + }); public async userState(address = ""): Promise<{ collateral: string, borrowed: string, debt: string, N: string }> { const { _collateral, _borrowed, _debt, _N } = await this._userState(address); @@ -1464,25 +1464,25 @@ export class LendMarketTemplate { } public createLoanMaxRecvAllRanges = memoize(async (collateral: number | string): Promise<{ [index: number]: string }> => { - const _collateral = parseUnits(collateral, this.collateral_token.decimals); + const _collateral = parseUnits(collateral, this.collateral_token.decimals); - const calls = []; - for (let N = this.minBands; N <= this.maxBands; N++) { - calls.push(this.llamalend.contracts[this.addresses.controller].multicallContract.max_borrowable(_collateral, N, 0)); - } - const _amounts = await this.llamalend.multicallProvider.all(calls) as bigint[]; + const calls = []; + for (let N = this.minBands; N <= this.maxBands; N++) { + calls.push(this.llamalend.contracts[this.addresses.controller].multicallContract.max_borrowable(_collateral, N, 0)); + } + const _amounts = await this.llamalend.multicallProvider.all(calls) as bigint[]; - const res: { [index: number]: string } = {}; - for (let N = this.minBands; N <= this.maxBands; N++) { - res[N] = formatUnits(_amounts[N - this.minBands], this.borrowed_token.decimals); - } + const res: { [index: number]: string } = {}; + for (let N = this.minBands; N <= this.maxBands; N++) { + res[N] = formatUnits(_amounts[N - this.minBands], this.borrowed_token.decimals); + } - return res; - }, - { - promise: true, - maxAge: 5 * 60 * 1000, // 5m - }); + return res; + }, + { + promise: true, + maxAge: 5 * 60 * 1000, // 5m + }); public async getMaxRange(collateral: number | string, debt: number | string): Promise { const maxRecv = await this.createLoanMaxRecvAllRanges(collateral); @@ -2377,51 +2377,51 @@ export class LendMarketTemplate { maxLeverage: string, avgPrice: string, }>> => { - this._checkLeverageZap(); - const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); - const contract = this.llamalend.contracts[this.llamalend.constants.ALIASES.leverage_zap].multicallContract; - - const oraclePriceBand = await this.oraclePriceBand(); - const pAvgApproxBN = BN(await this.calcTickPrice(oraclePriceBand)); // upper tick of oracle price band - let pAvgBN: BigNumber | null = null; - const arrLength = this.maxBands - this.minBands + 1; - let maxLeverageCollateralBN: BigNumber[] = new Array(arrLength).fill(BN(0)); - let _maxLeverageCollateral: bigint[] = new Array(arrLength).fill(BigInt(0)); - let maxBorrowablePrevBN: BigNumber[] = new Array(arrLength).fill(BN(0)); - let maxBorrowableBN: BigNumber[] = new Array(arrLength).fill(BN(0)); - let _maxBorrowable: bigint[] = new Array(arrLength).fill(BigInt(0)); - - for (let i = 0; i < 5; i++) { - const pBN = pAvgBN ?? pAvgApproxBN; - maxBorrowablePrevBN = maxBorrowableBN; - const _userEffectiveCollateral: bigint = _userCollateral + fromBN(BN(userBorrowed).div(pBN), this.collateral_token.decimals); - const calls = []; - for (let N = this.minBands; N <= this.maxBands; N++) { - const j = N - this.minBands; - calls.push(contract.max_borrowable(this.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral[j], N, fromBN(pBN))); - } - _maxBorrowable = (await this.llamalend.multicallProvider.all(calls) as bigint[]).map((_mb) => _mb * BigInt(998) / BigInt(1000)); - maxBorrowableBN = _maxBorrowable.map((_mb) => toBN(_mb, this.borrowed_token.decimals)); + this._checkLeverageZap(); + const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); + const contract = this.llamalend.contracts[this.llamalend.constants.ALIASES.leverage_zap].multicallContract; - const deltaBN = maxBorrowableBN.map((mb, l) => mb.minus(maxBorrowablePrevBN[l]).abs().div(mb)); - if (BigNumber.max(...deltaBN).lt(0.0005)) { - maxBorrowableBN = maxBorrowablePrevBN; - break; - } + const oraclePriceBand = await this.oraclePriceBand(); + const pAvgApproxBN = BN(await this.calcTickPrice(oraclePriceBand)); // upper tick of oracle price band + let pAvgBN: BigNumber | null = null; + const arrLength = this.maxBands - this.minBands + 1; + let maxLeverageCollateralBN: BigNumber[] = new Array(arrLength).fill(BN(0)); + let _maxLeverageCollateral: bigint[] = new Array(arrLength).fill(BigInt(0)); + let maxBorrowablePrevBN: BigNumber[] = new Array(arrLength).fill(BN(0)); + let maxBorrowableBN: BigNumber[] = new Array(arrLength).fill(BN(0)); + let _maxBorrowable: bigint[] = new Array(arrLength).fill(BigInt(0)); - if (pAvgBN === null){ - const _y = BigInt(await _getExpectedOdos.call(this.llamalend, this.addresses.borrowed_token, this.addresses.collateral_token, _maxBorrowable[0], this.addresses.amm)); - const yBN = toBN(_y, this.collateral_token.decimals); - pAvgBN = maxBorrowableBN[0].div(yBN); - } + for (let i = 0; i < 5; i++) { + const pBN = pAvgBN ?? pAvgApproxBN; + maxBorrowablePrevBN = maxBorrowableBN; + const _userEffectiveCollateral: bigint = _userCollateral + fromBN(BN(userBorrowed).div(pBN), this.collateral_token.decimals); + const calls = []; + for (let N = this.minBands; N <= this.maxBands; N++) { + const j = N - this.minBands; + calls.push(contract.max_borrowable(this.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral[j], N, fromBN(pBN))); + } + _maxBorrowable = (await this.llamalend.multicallProvider.all(calls) as bigint[]).map((_mb) => _mb * BigInt(998) / BigInt(1000)); + maxBorrowableBN = _maxBorrowable.map((_mb) => toBN(_mb, this.borrowed_token.decimals)); - maxLeverageCollateralBN = maxBorrowableBN.map((mb) => mb.div(pAvgBN as BigNumber)); - _maxLeverageCollateral = maxLeverageCollateralBN.map((mlc) => fromBN(mlc, this.collateral_token.decimals)); + const deltaBN = maxBorrowableBN.map((mb, l) => mb.minus(maxBorrowablePrevBN[l]).abs().div(mb)); + if (BigNumber.max(...deltaBN).lt(0.0005)) { + maxBorrowableBN = maxBorrowablePrevBN; + break; } - const userEffectiveCollateralBN = BN(userCollateral).plus(BN(userBorrowed).div(pAvgBN as BigNumber)); + if (pAvgBN === null){ + const _y = BigInt(await _getExpectedOdos.call(this.llamalend, this.addresses.borrowed_token, this.addresses.collateral_token, _maxBorrowable[0], this.addresses.amm)); + const yBN = toBN(_y, this.collateral_token.decimals); + pAvgBN = maxBorrowableBN[0].div(yBN); + } - const res: IDict<{ + maxLeverageCollateralBN = maxBorrowableBN.map((mb) => mb.div(pAvgBN as BigNumber)); + _maxLeverageCollateral = maxLeverageCollateralBN.map((mlc) => fromBN(mlc, this.collateral_token.decimals)); + } + + const userEffectiveCollateralBN = BN(userCollateral).plus(BN(userBorrowed).div(pAvgBN as BigNumber)); + + const res: IDict<{ maxDebt: string, maxTotalCollateral: string, userCollateral: string, @@ -2430,25 +2430,25 @@ export class LendMarketTemplate { maxLeverage: string, avgPrice: string, }> = {}; - for (let N = this.minBands; N <= this.maxBands; N++) { - const j = N - this.minBands; - res[N] = { - maxDebt: formatNumber(maxBorrowableBN[j].toString(), this.borrowed_token.decimals), - maxTotalCollateral: formatNumber(maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).toString(), this.collateral_token.decimals), - userCollateral: formatNumber(userCollateral, this.collateral_token.decimals), - collateralFromUserBorrowed: formatNumber(BN(userBorrowed).div(pAvgBN as BigNumber).toString(), this.collateral_token.decimals), - collateralFromMaxDebt: formatNumber(maxLeverageCollateralBN[j].toString(), this.collateral_token.decimals), - maxLeverage: maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).div(userEffectiveCollateralBN).toString(), - avgPrice: (pAvgBN as BigNumber).toString(), - }; - } + for (let N = this.minBands; N <= this.maxBands; N++) { + const j = N - this.minBands; + res[N] = { + maxDebt: formatNumber(maxBorrowableBN[j].toString(), this.borrowed_token.decimals), + maxTotalCollateral: formatNumber(maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).toString(), this.collateral_token.decimals), + userCollateral: formatNumber(userCollateral, this.collateral_token.decimals), + collateralFromUserBorrowed: formatNumber(BN(userBorrowed).div(pAvgBN as BigNumber).toString(), this.collateral_token.decimals), + collateralFromMaxDebt: formatNumber(maxLeverageCollateralBN[j].toString(), this.collateral_token.decimals), + maxLeverage: maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).div(userEffectiveCollateralBN).toString(), + avgPrice: (pAvgBN as BigNumber).toString(), + }; + } - return res; - }, - { - promise: true, - maxAge: 60 * 1000, // 1m - }); + return res; + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); private _setSwapDataToCache = async (inputCoinAddress: string, outputCoinAddress: string, _amount: bigint, slippage: number) => { let swapData = await _getQuoteOdos.call(this.llamalend, inputCoinAddress, outputCoinAddress, _amount, this.addresses.amm, true, slippage); @@ -2529,36 +2529,36 @@ export class LendMarketTemplate { } private _leverageCalcN1 = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, user?: string): Promise => { - if (range > 0) this._checkRange(range); - let _stateDebt = BigInt(0); - if (user) { - const { _debt, _borrowed, _N } = await this._userState(user); - if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); - _stateDebt = _debt; - if (range < 0) range = Number(this.llamalend.formatUnits(_N, 0)); - } - const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt, user); - const _debt = _stateDebt + parseUnits(debt, this.borrowed_token.decimals); - return await this.llamalend.contracts[this.addresses.controller].contract.calculate_debt_n1(_futureStateCollateral, _debt, range, this.llamalend.constantOptions); - }, - { - promise: true, - maxAge: 60 * 1000, // 1m - }); + if (range > 0) this._checkRange(range); + let _stateDebt = BigInt(0); + if (user) { + const { _debt, _borrowed, _N } = await this._userState(user); + if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); + _stateDebt = _debt; + if (range < 0) range = Number(this.llamalend.formatUnits(_N, 0)); + } + const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt, user); + const _debt = _stateDebt + parseUnits(debt, this.borrowed_token.decimals); + return await this.llamalend.contracts[this.addresses.controller].contract.calculate_debt_n1(_futureStateCollateral, _debt, range, this.llamalend.constantOptions); + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); private _leverageCalcN1AllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, maxN: number): Promise => { - const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt); - const _debt = parseUnits(debt, this.borrowed_token.decimals); - const calls = []; - for (let N = this.minBands; N <= maxN; N++) { - calls.push(this.llamalend.contracts[this.addresses.controller].multicallContract.calculate_debt_n1(_futureStateCollateral, _debt, N)); - } - return await this.llamalend.multicallProvider.all(calls) as bigint[]; - }, - { - promise: true, - maxAge: 60 * 1000, // 1m - }); + const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt); + const _debt = parseUnits(debt, this.borrowed_token.decimals); + const calls = []; + for (let N = this.minBands; N <= maxN; N++) { + calls.push(this.llamalend.contracts[this.addresses.controller].multicallContract.calculate_debt_n1(_futureStateCollateral, _debt, N)); + } + return await this.llamalend.multicallProvider.all(calls) as bigint[]; + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); private async _leverageBands(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, user?: string): Promise<[bigint, bigint]> { const _n1 = await this._leverageCalcN1(userCollateral, userBorrowed, debt, range, user); @@ -3006,30 +3006,30 @@ export class LendMarketTemplate { } private _leverageRepayBands = memoize( async (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address: string): Promise<[bigint, bigint]> => { - address = _getAddress.call(this.llamalend, address); - if (!(await this.leverageRepayIsAvailable(stateCollateral, userCollateral, userBorrowed, address))) return [parseUnits(0, 0), parseUnits(0, 0)]; - - const _stateRepayCollateral = parseUnits(stateCollateral, this.collateral_token.decimals); - const { _collateral: _stateCollateral, _debt: _stateDebt, _N } = await this._userState(address); - if (_stateDebt == BigInt(0)) throw Error(`Loan for ${address} does not exist`); - if (_stateCollateral < _stateRepayCollateral) throw Error(`Can't use more collateral than user's position has (${_stateRepayCollateral}) > ${_stateCollateral})`); - - let _n1 = parseUnits(0, 0); - let _n2 = parseUnits(0, 0); - const { _totalBorrowed: _repayExpected } = this._leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed); - try { - _n1 = await this.llamalend.contracts[this.addresses.controller].contract.calculate_debt_n1(_stateCollateral - _stateRepayCollateral, _stateDebt - _repayExpected, _N); - _n2 = _n1 + (_N - BigInt(1)); - } catch { - console.log("Full repayment"); - } + address = _getAddress.call(this.llamalend, address); + if (!(await this.leverageRepayIsAvailable(stateCollateral, userCollateral, userBorrowed, address))) return [parseUnits(0, 0), parseUnits(0, 0)]; + + const _stateRepayCollateral = parseUnits(stateCollateral, this.collateral_token.decimals); + const { _collateral: _stateCollateral, _debt: _stateDebt, _N } = await this._userState(address); + if (_stateDebt == BigInt(0)) throw Error(`Loan for ${address} does not exist`); + if (_stateCollateral < _stateRepayCollateral) throw Error(`Can't use more collateral than user's position has (${_stateRepayCollateral}) > ${_stateCollateral})`); + + let _n1 = parseUnits(0, 0); + let _n2 = parseUnits(0, 0); + const { _totalBorrowed: _repayExpected } = this._leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed); + try { + _n1 = await this.llamalend.contracts[this.addresses.controller].contract.calculate_debt_n1(_stateCollateral - _stateRepayCollateral, _stateDebt - _repayExpected, _N); + _n2 = _n1 + (_N - BigInt(1)); + } catch { + console.log("Full repayment"); + } - return [_n2, _n1]; - }, - { - promise: true, - maxAge: 5 * 60 * 1000, // 5m - }); + return [_n2, _n1]; + }, + { + promise: true, + maxAge: 5 * 60 * 1000, // 5m + }); private async leverageRepayBands(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise<[number, number]> { this._checkLeverageZap(); diff --git a/src/lendMarkets/modules/leverageZapV2.ts b/src/lendMarkets/modules/leverageZapV2.ts index 9a489e3..e606ddc 100644 --- a/src/lendMarkets/modules/leverageZapV2.ts +++ b/src/lendMarkets/modules/leverageZapV2.ts @@ -223,10 +223,10 @@ export class LeverageZapV2Module { return res; }, - { - promise: true, - maxAge: 60 * 1000, // 1m - }); + { + promise: true, + maxAge: 60 * 1000, // 1m + }); private _leverageExpectedCollateral = async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, quote: IQuote, user?: string): Promise<{ _futureStateCollateral: bigint, _totalCollateral: bigint, _userCollateral: bigint, @@ -311,36 +311,36 @@ export class LeverageZapV2Module { } private _leverageCalcN1 = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, quote: IQuote, user?: string): Promise => { - if (range > 0) this.market._checkRange(range); - let _stateDebt = BigInt(0); - if (user) { - const { _debt, _borrowed, _N } = await this.market._userState(user); - if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); - _stateDebt = _debt; - if (range < 0) range = Number(this.llamalend.formatUnits(_N, 0)); - } - const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt, quote, user); - const _debt = _stateDebt + parseUnits(debt, this.market.borrowed_token.decimals); - return await this.llamalend.contracts[this.market.addresses.controller].contract.calculate_debt_n1(_futureStateCollateral, _debt, range, this.llamalend.constantOptions); - }, - { - promise: true, - maxAge: 60 * 1000, // 1m - }); + if (range > 0) this.market._checkRange(range); + let _stateDebt = BigInt(0); + if (user) { + const { _debt, _borrowed, _N } = await this.market._userState(user); + if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); + _stateDebt = _debt; + if (range < 0) range = Number(this.llamalend.formatUnits(_N, 0)); + } + const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt, quote, user); + const _debt = _stateDebt + parseUnits(debt, this.market.borrowed_token.decimals); + return await this.llamalend.contracts[this.market.addresses.controller].contract.calculate_debt_n1(_futureStateCollateral, _debt, range, this.llamalend.constantOptions); + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); private _leverageCalcN1AllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, maxN: number, quote: IQuote): Promise => { - const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt, quote); - const _debt = parseUnits(debt, this.market.borrowed_token.decimals); - const calls = []; - for (let N = this.market.minBands; N <= maxN; N++) { - calls.push(this.llamalend.contracts[this.market.addresses.controller].multicallContract.calculate_debt_n1(_futureStateCollateral, _debt, N)); - } - return await this.llamalend.multicallProvider.all(calls) as bigint[]; - }, - { - promise: true, - maxAge: 60 * 1000, // 1m - }); + const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt, quote); + const _debt = parseUnits(debt, this.market.borrowed_token.decimals); + const calls = []; + for (let N = this.market.minBands; N <= maxN; N++) { + calls.push(this.llamalend.contracts[this.market.addresses.controller].multicallContract.calculate_debt_n1(_futureStateCollateral, _debt, N)); + } + return await this.llamalend.multicallProvider.all(calls) as bigint[]; + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); private async _leverageBands(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, quote: IQuote, user?: string): Promise<[bigint, bigint]> { const _n1 = await this._leverageCalcN1(userCollateral, userBorrowed, debt, range, quote, user); @@ -848,30 +848,30 @@ export class LeverageZapV2Module { } private _leverageRepayBands = memoize( async (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote, address: string): Promise<[bigint, bigint]> => { - address = _getAddress.call(this.llamalend, address); - if (!(await this.leverageRepayIsAvailable({ stateCollateral, userCollateral, userBorrowed, quote, address }))) return [parseUnits(0, 0), parseUnits(0, 0)]; - - const _stateRepayCollateral = parseUnits(stateCollateral, this.market.collateral_token.decimals); - const { _collateral: _stateCollateral, _debt: _stateDebt, _N } = await this.market._userState(address); - if (_stateDebt == BigInt(0)) throw Error(`Loan for ${address} does not exist`); - if (_stateCollateral < _stateRepayCollateral) throw Error(`Can't use more collateral than user's position has (${_stateRepayCollateral}) > ${_stateCollateral})`); - - let _n1 = parseUnits(0, 0); - let _n2 = parseUnits(0, 0); - const { _totalBorrowed: _repayExpected } = this._leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed, quote); - try { - _n1 = await this.llamalend.contracts[this.market.addresses.controller].contract.calculate_debt_n1(_stateCollateral - _stateRepayCollateral, _stateDebt - _repayExpected, _N); - _n2 = _n1 + (_N - BigInt(1)); - } catch { - console.log("Full repayment"); - } + address = _getAddress.call(this.llamalend, address); + if (!(await this.leverageRepayIsAvailable({ stateCollateral, userCollateral, userBorrowed, quote, address }))) return [parseUnits(0, 0), parseUnits(0, 0)]; + + const _stateRepayCollateral = parseUnits(stateCollateral, this.market.collateral_token.decimals); + const { _collateral: _stateCollateral, _debt: _stateDebt, _N } = await this.market._userState(address); + if (_stateDebt == BigInt(0)) throw Error(`Loan for ${address} does not exist`); + if (_stateCollateral < _stateRepayCollateral) throw Error(`Can't use more collateral than user's position has (${_stateRepayCollateral}) > ${_stateCollateral})`); + + let _n1 = parseUnits(0, 0); + let _n2 = parseUnits(0, 0); + const { _totalBorrowed: _repayExpected } = this._leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed, quote); + try { + _n1 = await this.llamalend.contracts[this.market.addresses.controller].contract.calculate_debt_n1(_stateCollateral - _stateRepayCollateral, _stateDebt - _repayExpected, _N); + _n2 = _n1 + (_N - BigInt(1)); + } catch { + console.log("Full repayment"); + } - return [_n2, _n1]; - }, - { - promise: true, - maxAge: 5 * 60 * 1000, // 5m - }); + return [_n2, _n1]; + }, + { + promise: true, + maxAge: 5 * 60 * 1000, // 5m + }); private async _leverageRepayHealth(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, quote: IQuote, full = true, address = ""): Promise { From 56e2c6c5b76faa6929aab9dbb8b69db3c7d2417c Mon Sep 17 00:00:00 2001 From: fedorovdg Date: Wed, 17 Dec 2025 11:56:20 +0400 Subject: [PATCH 7/7] refactor: remove console.log --- src/lendMarkets/LendMarketTemplate.ts | 1 - src/lendMarkets/modules/leverageZapV2.ts | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/lendMarkets/LendMarketTemplate.ts b/src/lendMarkets/LendMarketTemplate.ts index 762763d..1e18970 100644 --- a/src/lendMarkets/LendMarketTemplate.ts +++ b/src/lendMarkets/LendMarketTemplate.ts @@ -3118,7 +3118,6 @@ export class LendMarketTemplate { calldata = await _assembleTxOdos.call(this.llamalend, swapData.pathId as string); } - console.log('params', [0, parseUnits(this._getMarketId(), 0), _userCollateral, _userBorrowed], calldata) const contract = this.llamalend.contracts[this.addresses.controller].contract; const gas = await contract.repay_extended.estimateGas( this.llamalend.constants.ALIASES.leverage_zap, diff --git a/src/lendMarkets/modules/leverageZapV2.ts b/src/lendMarkets/modules/leverageZapV2.ts index e606ddc..e6cac4e 100644 --- a/src/lendMarkets/modules/leverageZapV2.ts +++ b/src/lendMarkets/modules/leverageZapV2.ts @@ -862,11 +862,10 @@ export class LeverageZapV2Module { try { _n1 = await this.llamalend.contracts[this.market.addresses.controller].contract.calculate_debt_n1(_stateCollateral - _stateRepayCollateral, _stateDebt - _repayExpected, _N); _n2 = _n1 + (_N - BigInt(1)); + return [_n2, _n1]; } catch { - console.log("Full repayment"); + return [_n2, _n1]; } - - return [_n2, _n1]; }, { promise: true, @@ -947,7 +946,6 @@ export class LeverageZapV2Module { zapCalldata = buildCalldataForLeverageZapV2(router, calldata) } - console.log('params', [0, parseUnits(this._getMarketId(), 0), _userCollateral, _userBorrowed], calldata) const contract = this.llamalend.contracts[this.market.addresses.controller].contract; const gas = await contract.repay_extended.estimateGas( this.llamalend.constants.ALIASES.leverage_zap_v2,