From 8d15563e39185cdc868c1dad867ca5e7675123ac Mon Sep 17 00:00:00 2001 From: macket Date: Wed, 27 Mar 2024 15:53:18 +0400 Subject: [PATCH 01/36] feat: calculations for leverage with borrowed token --- src/markets/OneWayMarketTemplate.ts | 87 +++++++++++++++++------------ 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index effa3d7..6622ed1 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -1833,14 +1833,14 @@ export class OneWayMarketTemplate { return d_k_effective_BN.times(S); } - public async maxLeverage(N: number): Promise { + private 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() } - private async leverageCreateLoanMaxRecv(userCollateral: TAmount, range: number): + private async leverageCreateLoanMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, range: number): Promise<{ maxBorrowable: string, maxTotalCollateral: string, maxLeverage: string, collateralAvgPrice: string }> { // max_borrowable = userCollateral / (1 / (k_effective * max_p_base) - 1 / p_avg) this._checkLeverageZap(); @@ -1858,7 +1858,8 @@ export class OneWayMarketTemplate { for (let i = 0; i < 5; i++) { maxBorrowablePrevBN = maxBorrowableBN; - _maxBorrowable = await contract.max_borrowable(this.addresses.controller, _userCollateral, _maxLeverageCollateral, range, fromBN(pAvgBN)); + const _userEffectiveCollateral: bigint = _userCollateral + fromBN(BN(userBorrowed).div(pAvgBN), this.collateral_token.decimals); + _maxBorrowable = await contract.max_borrowable(this.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral, range, fromBN(pAvgBN)); maxBorrowableBN = toBN(_maxBorrowable, this.borrowed_token.decimals); if (maxBorrowableBN.minus(maxBorrowablePrevBN).abs().div(maxBorrowablePrevBN).lt(0.0005)) { @@ -1871,15 +1872,17 @@ export class OneWayMarketTemplate { pAvgBN = maxBorrowableBN.div(maxLeverageCollateralBN) } + const userEffectiveCollateralBN = BN(userCollateral).plus(BN(userBorrowed).div(pAvgBN)); + return { maxBorrowable: maxBorrowableBN.toString(), - maxTotalCollateral: maxLeverageCollateralBN.plus(userCollateral).toString(), - maxLeverage: maxLeverageCollateralBN.plus(userCollateral).div(userCollateral).toString(), + maxTotalCollateral: maxLeverageCollateralBN.plus(userEffectiveCollateralBN).toString(), + maxLeverage: maxLeverageCollateralBN.plus(userEffectiveCollateralBN).div(userEffectiveCollateralBN).toString(), collateralAvgPrice: pAvgBN.toString(), }; } - private leverageCreateLoanMaxRecvAllRanges = memoize(async (userCollateral: TAmount): + private leverageCreateLoanMaxRecvAllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount): Promise> => { this._checkLeverageZap(); const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); @@ -1897,11 +1900,13 @@ export class OneWayMarketTemplate { 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, _userCollateral, _maxLeverageCollateral[j], N, fromBN(pAvgBN ?? pAvgApproxBN))); + calls.push(contract.max_borrowable(this.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral[j], N, fromBN(pBN))); } _maxBorrowable = await lending.multicallProvider.all(calls); maxBorrowableBN = _maxBorrowable.map((_mb) => toBN(_mb, this.borrowed_token.decimals)); @@ -1922,13 +1927,15 @@ export class OneWayMarketTemplate { _maxLeverageCollateral = maxLeverageCollateralBN.map((mlc) => fromBN(mlc, this.collateral_token.decimals)); } + const userEffectiveCollateralBN = BN(userCollateral).plus(BN(userBorrowed).div(pAvgBN as BigNumber)); + const res: IDict<{ maxBorrowable: string, maxTotalCollateral: string, maxLeverage: string, collateralAvgPrice: string }> = {}; for (let N = this.minBands; N <= this.maxBands; N++) { const j = N - this.minBands; res[N] = { maxBorrowable: maxBorrowableBN[j].toString(), - maxTotalCollateral: maxLeverageCollateralBN[j].plus(userCollateral).toString(), - maxLeverage: maxLeverageCollateralBN[j].plus(userCollateral).div(userCollateral).toString(), + maxTotalCollateral: maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).toString(), + maxLeverage: maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).div(userEffectiveCollateralBN).toString(), collateralAvgPrice: (pAvgBN as BigNumber).toString(), }; } @@ -1940,20 +1947,22 @@ export class OneWayMarketTemplate { maxAge: 60 * 1000, // 1m }); - private leverageCreateLoanTotalCollateral = memoize(async (userCollateral: TAmount, debt: TAmount): Promise => { + private leverageCreateLoanTotalCollateral = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise => { const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); const _debt = parseUnits(debt, this.borrowed_token.decimals); - const _leverageCollateral = BigInt(await _getQuote1inch(this.addresses.borrowed_token, this.addresses.collateral_token, _debt)); + const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); + // additionalCollateral = (userBorrowed / p) + leverageCollateral + const _additionalCollateral = BigInt(await _getQuote1inch(this.addresses.borrowed_token, this.addresses.collateral_token, _debt + _userBorrowed)); - return formatUnits(_userCollateral + _leverageCollateral, this.collateral_token.decimals); + return formatUnits(_userCollateral + _additionalCollateral, this.collateral_token.decimals); }, { promise: true, maxAge: 60 * 1000, // 1m }); - private async leverageGetMaxRange(userCollateral: number | string, debt: number | string): Promise { - const maxRecv = await this.leverageCreateLoanMaxRecvAllRanges(userCollateral); + private async leverageGetMaxRange(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise { + const maxRecv = await this.leverageCreateLoanMaxRecvAllRanges(userCollateral, userBorrowed); for (let N = this.minBands; N <= this.maxBands; N++) { if (BN(debt).gt(maxRecv[N].maxBorrowable)) return N - 1; } @@ -1961,33 +1970,41 @@ export class OneWayMarketTemplate { return this.maxBands; } - private async _leverageCalcN1(userCollateral: TAmount, debt: TAmount, range: number): Promise { + private _leverageCalcN1 = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number): Promise => { this._checkRange(range); - const _totalCollateral = parseUnits(await this.leverageCreateLoanTotalCollateral(userCollateral, debt), this.collateral_token.decimals); + const _totalCollateral = parseUnits(await this.leverageCreateLoanTotalCollateral(userCollateral, userBorrowed, debt), this.collateral_token.decimals); const _debt = parseUnits(debt, this.borrowed_token.decimals); return await lending.contracts[this.addresses.controller].contract.calculate_debt_n1(_totalCollateral, _debt, range, lending.constantOptions); - } + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); - private async _leverageCalcN1AllRanges(userCollateral: number | string, debt: number | string, maxN: number): Promise { - const _totalCollateral = parseUnits(await this.leverageCreateLoanTotalCollateral(userCollateral, debt), this.collateral_token.decimals); + private _leverageCalcN1AllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, maxN: number): Promise => { + const _totalCollateral = parseUnits(await this.leverageCreateLoanTotalCollateral(userCollateral, userBorrowed, debt), this.collateral_token.decimals); const _debt = parseUnits(debt, this.borrowed_token.decimals); const calls = []; for (let N = this.minBands; N <= maxN; N++) { calls.push(lending.contracts[this.addresses.controller].multicallContract.calculate_debt_n1(_totalCollateral, _debt, N)); } return await lending.multicallProvider.all(calls) as bigint[]; - } + }, + { + promise: true, + maxAge: 60 * 1000, // 1m + }); - private async _leverageCreateLoanBands(collateral: number | string, debt: number | string, range: number): Promise<[bigint, bigint]> { - const _n1 = await this._leverageCalcN1(collateral, debt, range); + private async _leverageCreateLoanBands(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number): Promise<[bigint, bigint]> { + const _n1 = await this._leverageCalcN1(userCollateral, userBorrowed, debt, range); const _n2 = _n1 + BigInt(range - 1); return [_n2, _n1]; } - private async _leverageCreateLoanBandsAllRanges(collateral: number | string, debt: number | string): Promise> { - const maxN = await this.leverageGetMaxRange(collateral, debt); - const _n1_arr = await this._leverageCalcN1AllRanges(collateral, debt, maxN); + private async _leverageCreateLoanBandsAllRanges(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise> { + const maxN = await this.leverageGetMaxRange(userCollateral, userBorrowed, debt); + const _n1_arr = await this._leverageCalcN1AllRanges(userCollateral, userBorrowed, debt, maxN); const _n2_arr: bigint[] = []; for (let N = this.minBands; N <= maxN; N++) { _n2_arr.push(_n1_arr[N - this.minBands] + BigInt(N - 1)); @@ -2001,16 +2018,16 @@ export class OneWayMarketTemplate { return _bands; } - private async leverageCreateLoanBands(collateral: number | string, debt: number | string, range: number): Promise<[number, number]> { + private async leverageCreateLoanBands(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number): Promise<[number, number]> { this._checkLeverageZap(); - const [_n2, _n1] = await this._leverageCreateLoanBands(collateral, debt, range); + const [_n2, _n1] = await this._leverageCreateLoanBands(userCollateral, userBorrowed, debt, range); return [Number(_n2), Number(_n1)]; } - private async leverageCreateLoanBandsAllRanges(collateral: number | string, debt: number | string): Promise> { + private async leverageCreateLoanBandsAllRanges(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise> { this._checkLeverageZap(); - const _bands = await this._leverageCreateLoanBandsAllRanges(collateral, debt); + const _bands = await this._leverageCreateLoanBandsAllRanges(userCollateral, userBorrowed, debt); const bands: { [index: number]: [number, number] | null } = {}; for (let N = this.minBands; N <= this.maxBands; N++) { @@ -2024,16 +2041,16 @@ export class OneWayMarketTemplate { return bands; } - private async leverageCreateLoanPrices(collateral: number | string, debt: number | string, range: number): Promise { + private async leverageCreateLoanPrices(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number): Promise { this._checkLeverageZap(); - const [_n2, _n1] = await this._leverageCreateLoanBands(collateral, debt, range); + const [_n2, _n1] = await this._leverageCreateLoanBands(userCollateral, userBorrowed, debt, range); return await this._getPrices(_n2, _n1); } - private async leverageCreateLoanPricesAllRanges(collateral: number | string, debt: number | string): Promise> { + private async leverageCreateLoanPricesAllRanges(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise> { this._checkLeverageZap(); - const _bands = await this._leverageCreateLoanBandsAllRanges(collateral, debt); + const _bands = await this._leverageCreateLoanBandsAllRanges(userCollateral, userBorrowed, debt); const prices: { [index: number]: [string, string] | null } = {}; for (let N = this.minBands; N <= this.maxBands; N++) { @@ -2047,10 +2064,10 @@ export class OneWayMarketTemplate { return prices; } - private async leverageCreateLoanHealth(collateral: number | string, debt: number | string, range: number, full = true): Promise { + private async leverageCreateLoanHealth(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, full = true): Promise { this._checkLeverageZap(); const address = "0x0000000000000000000000000000000000000000"; - const totalCollateral = await this.leverageCreateLoanTotalCollateral(collateral, debt); + const totalCollateral = await this.leverageCreateLoanTotalCollateral(userCollateral, userBorrowed, debt); const _totalCollateral = parseUnits(totalCollateral, this.collateral_token.decimals); const _debt = parseUnits(debt); From 89795e17df101104f369544e5396479b9fd7953f Mon Sep 17 00:00:00 2001 From: macket Date: Wed, 27 Mar 2024 23:14:40 +0400 Subject: [PATCH 02/36] feat: general methods for both leverageCreateLoan and leverageBorrowMore --- src/markets/OneWayMarketTemplate.ts | 91 +++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 18 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 6622ed1..4239c60 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -1809,7 +1809,7 @@ export class OneWayMarketTemplate { return await this._liquidate(lending.signerAddress, slippage, false) as string; } - // ---------------- LEVERAGE ---------------- + // ---------------- LEVERAGE CREATE LOAN ---------------- private _checkLeverageZap(): void { if (lending.constants.ALIASES.leverage_zap === "0x0000000000000000000000000000000000000000") { @@ -1840,12 +1840,21 @@ export class OneWayMarketTemplate { return BN(1).div(BN(1).minus(k_effective_BN)).toString() } - private async leverageCreateLoanMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, range: number): + private async _leverageMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, range: number, user?: string): Promise<{ maxBorrowable: string, maxTotalCollateral: string, maxLeverage: string, collateralAvgPrice: string }> { // max_borrowable = userCollateral / (1 / (k_effective * max_p_base) - 1 / p_avg) this._checkLeverageZap(); this._checkRange(range); - const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); + let _stateCollateral = BigInt(0); + let _stateDebt = BigInt(0); + if (user) { + const { _collateral, _borrowed, _debt } = await this._userState(); + if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); + _stateCollateral = _collateral; + _stateDebt = _debt; + } + const _userCollateral = _stateCollateral + parseUnits(userCollateral, this.collateral_token.decimals); + const userCollateralBN = toBN(_userCollateral, this.collateral_token.decimals); const contract = lending.contracts[lending.constants.ALIASES.leverage_zap].contract; const oraclePriceBand = await this.oraclePriceBand(); @@ -1860,6 +1869,7 @@ export class OneWayMarketTemplate { maxBorrowablePrevBN = maxBorrowableBN; const _userEffectiveCollateral: bigint = _userCollateral + fromBN(BN(userBorrowed).div(pAvgBN), this.collateral_token.decimals); _maxBorrowable = await contract.max_borrowable(this.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral, range, fromBN(pAvgBN)); + _maxBorrowable -= _stateDebt; maxBorrowableBN = toBN(_maxBorrowable, this.borrowed_token.decimals); if (maxBorrowableBN.minus(maxBorrowablePrevBN).abs().div(maxBorrowablePrevBN).lt(0.0005)) { @@ -1872,7 +1882,7 @@ export class OneWayMarketTemplate { pAvgBN = maxBorrowableBN.div(maxLeverageCollateralBN) } - const userEffectiveCollateralBN = BN(userCollateral).plus(BN(userBorrowed).div(pAvgBN)); + const userEffectiveCollateralBN = userCollateralBN.plus(userCollateral).plus(BN(userBorrowed).div(pAvgBN)); return { maxBorrowable: maxBorrowableBN.toString(), @@ -1882,6 +1892,11 @@ export class OneWayMarketTemplate { }; } + // private async leverageCreateLoanMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, range: number): + // Promise<{ maxBorrowable: string, maxTotalCollateral: string, maxLeverage: string, collateralAvgPrice: string }> { + // + // } + private leverageCreateLoanMaxRecvAllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount): Promise> => { this._checkLeverageZap(); @@ -1947,20 +1962,40 @@ export class OneWayMarketTemplate { maxAge: 60 * 1000, // 1m }); - private leverageCreateLoanTotalCollateral = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise => { + private _leverageTotalCollateral = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, user?: string): Promise => { const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); const _debt = parseUnits(debt, this.borrowed_token.decimals); const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); // additionalCollateral = (userBorrowed / p) + leverageCollateral const _additionalCollateral = BigInt(await _getQuote1inch(this.addresses.borrowed_token, this.addresses.collateral_token, _debt + _userBorrowed)); + let _stateCollateral = BigInt(0); + if (user) { + const { _collateral, _borrowed } = await this._userState(); + if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); + _stateCollateral = _collateral; + } - return formatUnits(_userCollateral + _additionalCollateral, this.collateral_token.decimals); + return formatUnits(_stateCollateral + _userCollateral + _additionalCollateral, this.collateral_token.decimals); }, { promise: true, maxAge: 60 * 1000, // 1m }); + // private leverageCreateLoanTotalCollateral = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise => { + // const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); + // const _debt = parseUnits(debt, this.borrowed_token.decimals); + // const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); + // // additionalCollateral = (userBorrowed / p) + leverageCollateral + // const _additionalCollateral = BigInt(await _getQuote1inch(this.addresses.borrowed_token, this.addresses.collateral_token, _debt + _userBorrowed)); + // + // return formatUnits(_userCollateral + _additionalCollateral, this.collateral_token.decimals); + // }, + // { + // promise: true, + // maxAge: 60 * 1000, // 1m + // }); + private async leverageGetMaxRange(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise { const maxRecv = await this.leverageCreateLoanMaxRecvAllRanges(userCollateral, userBorrowed); for (let N = this.minBands; N <= this.maxBands; N++) { @@ -1970,10 +2005,16 @@ export class OneWayMarketTemplate { return this.maxBands; } - private _leverageCalcN1 = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number): Promise => { + private _leverageCalcN1 = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, user?: string): Promise => { this._checkRange(range); - const _totalCollateral = parseUnits(await this.leverageCreateLoanTotalCollateral(userCollateral, userBorrowed, debt), this.collateral_token.decimals); - const _debt = parseUnits(debt, this.borrowed_token.decimals); + let _stateDebt = BigInt(0); + if (user) { + const { _debt, _borrowed } = await this._userState(); + if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); + _stateDebt = _debt; + } + const _totalCollateral = parseUnits(await this._leverageTotalCollateral(userCollateral, userBorrowed, debt, user), this.collateral_token.decimals); + const _debt = _stateDebt + parseUnits(debt, this.borrowed_token.decimals); return await lending.contracts[this.addresses.controller].contract.calculate_debt_n1(_totalCollateral, _debt, range, lending.constantOptions); }, { @@ -1982,7 +2023,7 @@ export class OneWayMarketTemplate { }); private _leverageCalcN1AllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, maxN: number): Promise => { - const _totalCollateral = parseUnits(await this.leverageCreateLoanTotalCollateral(userCollateral, userBorrowed, debt), this.collateral_token.decimals); + const _totalCollateral = parseUnits(await this._leverageTotalCollateral(userCollateral, userBorrowed, debt), this.collateral_token.decimals); const _debt = parseUnits(debt, this.borrowed_token.decimals); const calls = []; for (let N = this.minBands; N <= maxN; N++) { @@ -1995,8 +2036,8 @@ export class OneWayMarketTemplate { maxAge: 60 * 1000, // 1m }); - private async _leverageCreateLoanBands(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number): Promise<[bigint, bigint]> { - const _n1 = await this._leverageCalcN1(userCollateral, userBorrowed, debt, range); + 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); const _n2 = _n1 + BigInt(range - 1); return [_n2, _n1]; @@ -2020,7 +2061,7 @@ export class OneWayMarketTemplate { private async leverageCreateLoanBands(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number): Promise<[number, number]> { this._checkLeverageZap(); - const [_n2, _n1] = await this._leverageCreateLoanBands(userCollateral, userBorrowed, debt, range); + const [_n2, _n1] = await this._leverageBands(userCollateral, userBorrowed, debt, range); return [Number(_n2), Number(_n1)]; } @@ -2043,7 +2084,7 @@ export class OneWayMarketTemplate { private async leverageCreateLoanPrices(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number): Promise { this._checkLeverageZap(); - const [_n2, _n1] = await this._leverageCreateLoanBands(userCollateral, userBorrowed, debt, range); + const [_n2, _n1] = await this._leverageBands(userCollateral, userBorrowed, debt, range); return await this._getPrices(_n2, _n1); } @@ -2064,17 +2105,31 @@ export class OneWayMarketTemplate { return prices; } - private async leverageCreateLoanHealth(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, full = true): Promise { + private async _leverageHealth( + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + range: number, + full: boolean, + user = lending.constants.ZERO_ADDRESS + ): Promise { this._checkLeverageZap(); - const address = "0x0000000000000000000000000000000000000000"; - const totalCollateral = await this.leverageCreateLoanTotalCollateral(userCollateral, userBorrowed, debt); + const totalCollateral = await this._leverageTotalCollateral(userCollateral, userBorrowed, debt, user); const _totalCollateral = parseUnits(totalCollateral, this.collateral_token.decimals); + if (user) { + const { _borrowed } = await this._userState(); + if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); + } const _debt = parseUnits(debt); const contract = lending.contracts[this.addresses.controller].contract; - let _health = await contract.health_calculator(address, _totalCollateral, _debt, full, range, lending.constantOptions) as bigint; + let _health = await contract.health_calculator(user, _totalCollateral, _debt, full, range, lending.constantOptions) as bigint; _health = _health * BigInt(100); return formatUnits(_health); } + + private async leverageCreateLoanHealth(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, full = true): Promise { + return await this._leverageHealth(userCollateral, userBorrowed, debt, range, full); + } } From 8557d1d817ee4f35d2f5d5f89706b119feafee09 Mon Sep 17 00:00:00 2001 From: macket Date: Thu, 28 Mar 2024 10:06:47 +0400 Subject: [PATCH 03/36] fix: revert bandBalances fix --- src/markets/OneWayMarketTemplate.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 4239c60..05ef2b8 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -859,8 +859,8 @@ export class OneWayMarketTemplate { // bands_x and bands_y always return amounts with 18 decimals return { - borrowed: formatNumber(formatUnits(_balances[0], this.borrowed_token.decimals), this.borrowed_token.decimals), - collateral: formatNumber(formatUnits(_balances[1], this.collateral_token.decimals), this.collateral_token.decimals), + borrowed: formatNumber(formatUnits(_balances[0]), this.borrowed_token.decimals), + collateral: formatNumber(formatUnits(_balances[1]), this.collateral_token.decimals), } } @@ -880,8 +880,8 @@ export class OneWayMarketTemplate { const _i = i - minBand // bands_x and bands_y always return amounts with 18 decimals bands[i] = { - borrowed: formatNumber(formatUnits(_bands[2 * _i], this.borrowed_token.decimals), this.borrowed_token.decimals), - collateral: formatNumber(formatUnits(_bands[(2 * _i) + 1], this.collateral_token.decimals), this.collateral_token.decimals), + borrowed: formatNumber(formatUnits(_bands[2 * _i]), this.borrowed_token.decimals), + collateral: formatNumber(formatUnits(_bands[(2 * _i) + 1]), this.collateral_token.decimals), } } From edb4939f505d994f76209dbd0271402b428ca04b Mon Sep 17 00:00:00 2001 From: macket Date: Thu, 28 Mar 2024 10:40:35 +0400 Subject: [PATCH 04/36] chore: leverage.createLoan public methods --- src/markets/OneWayMarketTemplate.ts | 50 +++++++++++++++++++---------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 05ef2b8..56fc048 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -148,6 +148,20 @@ export class OneWayMarketTemplate { claimRewards: () => Promise, } }; + leverage: { + maxLeverage: (N: number) => Promise, + createLoanMaxRecv: (userCollateral: TAmount, userBorrowed: TAmount, range: number) => + Promise<{ maxBorrowable: string, maxTotalCollateral: string, maxLeverage: string, collateralAvgPrice: string }>, + createLoanMaxRecvAllRanges: (userCollateral: TAmount, userBorrowed: TAmount) => + Promise>, + createLoanTotalCollateral: (userCollateral: TAmount, userBorrowed: TAmount, range: number) => Promise, + getMaxRange: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise, + createLoanBands: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number) => Promise<[number, number]>, + createLoanBandsAllRanges: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise>, + createLoanPrices: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number) => Promise, + createLoanPricesAllRanges: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise>, + createLoanHealth: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, full?: boolean) => Promise, + }; constructor(id: string) { this.id = id; @@ -241,6 +255,18 @@ export class OneWayMarketTemplate { claimRewards: this.vaultClaimRewardsEstimateGas.bind(this), }, } + this.leverage = { + maxLeverage: this.maxLeverage.bind(this), + createLoanMaxRecv: this.leverageCreateLoanMaxRecv.bind(this), + createLoanMaxRecvAllRanges: this.leverageCreateLoanMaxRecvAllRanges.bind(this), + createLoanTotalCollateral: this.leverageCreateLoanTotalCollateral.bind(this), + getMaxRange: this.leverageGetMaxRange.bind(this), + createLoanBands: this.leverageCreateLoanBands.bind(this), + createLoanBandsAllRanges: this.leverageCreateLoanBandsAllRanges.bind(this), + createLoanPrices: this.leverageCreateLoanPrices.bind(this), + createLoanPricesAllRanges: this.leverageCreateLoanPricesAllRanges.bind(this), + createLoanHealth: this.leverageCreateLoanHealth.bind(this), + } } @@ -1892,10 +1918,10 @@ export class OneWayMarketTemplate { }; } - // private async leverageCreateLoanMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, range: number): - // Promise<{ maxBorrowable: string, maxTotalCollateral: string, maxLeverage: string, collateralAvgPrice: string }> { - // - // } + private async leverageCreateLoanMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, range: number): + Promise<{ maxBorrowable: string, maxTotalCollateral: string, maxLeverage: string, collateralAvgPrice: string }> { + return await this._leverageMaxRecv(userCollateral, userBorrowed, range); + } private leverageCreateLoanMaxRecvAllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount): Promise> => { @@ -1982,19 +2008,9 @@ export class OneWayMarketTemplate { maxAge: 60 * 1000, // 1m }); - // private leverageCreateLoanTotalCollateral = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise => { - // const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); - // const _debt = parseUnits(debt, this.borrowed_token.decimals); - // const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); - // // additionalCollateral = (userBorrowed / p) + leverageCollateral - // const _additionalCollateral = BigInt(await _getQuote1inch(this.addresses.borrowed_token, this.addresses.collateral_token, _debt + _userBorrowed)); - // - // return formatUnits(_userCollateral + _additionalCollateral, this.collateral_token.decimals); - // }, - // { - // promise: true, - // maxAge: 60 * 1000, // 1m - // }); + private async leverageCreateLoanTotalCollateral(userCollateral: TAmount, userBorrowed: TAmount, range: number): Promise { + return await this._leverageTotalCollateral(userCollateral, userBorrowed, range); + } private async leverageGetMaxRange(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise { const maxRecv = await this.leverageCreateLoanMaxRecvAllRanges(userCollateral, userBorrowed); From c30ea2909b6fc6d838635e49e3b04de67abee054 Mon Sep 17 00:00:00 2001 From: macket Date: Tue, 2 Apr 2024 21:53:01 +0400 Subject: [PATCH 05/36] feat: leverage borrow more --- src/markets/OneWayMarketTemplate.ts | 93 +++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 18 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 56fc048..18625e8 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -150,17 +150,25 @@ export class OneWayMarketTemplate { }; leverage: { maxLeverage: (N: number) => Promise, + createLoanMaxRecv: (userCollateral: TAmount, userBorrowed: TAmount, range: number) => Promise<{ maxBorrowable: string, maxTotalCollateral: string, maxLeverage: string, collateralAvgPrice: string }>, createLoanMaxRecvAllRanges: (userCollateral: TAmount, userBorrowed: TAmount) => Promise>, createLoanTotalCollateral: (userCollateral: TAmount, userBorrowed: TAmount, range: number) => Promise, - getMaxRange: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise, + createLoanMaxRange: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise, createLoanBands: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number) => Promise<[number, number]>, createLoanBandsAllRanges: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise>, createLoanPrices: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number) => Promise, createLoanPricesAllRanges: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise>, createLoanHealth: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, full?: boolean) => Promise, + + borrowMoreMaxRecv: (userCollateral: TAmount, userBorrowed: TAmount, address?: string) => + Promise<{ maxBorrowable: string, maxTotalCollateral: string, collateralAvgPrice: string }>, + borrowMoreTotalCollateral: (userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, + borrowMoreBands: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, address?: string) => Promise<[number, number]>, + borrowMorePrices: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, address?: string) => Promise, + borrowMoreHealth: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, full?: boolean, address?: string) => Promise, }; constructor(id: string) { @@ -257,15 +265,22 @@ export class OneWayMarketTemplate { } this.leverage = { maxLeverage: this.maxLeverage.bind(this), + createLoanMaxRecv: this.leverageCreateLoanMaxRecv.bind(this), createLoanMaxRecvAllRanges: this.leverageCreateLoanMaxRecvAllRanges.bind(this), createLoanTotalCollateral: this.leverageCreateLoanTotalCollateral.bind(this), - getMaxRange: this.leverageGetMaxRange.bind(this), + createLoanMaxRange: this.leverageCreateLoanMaxRange.bind(this), createLoanBands: this.leverageCreateLoanBands.bind(this), createLoanBandsAllRanges: this.leverageCreateLoanBandsAllRanges.bind(this), createLoanPrices: this.leverageCreateLoanPrices.bind(this), createLoanPricesAllRanges: this.leverageCreateLoanPricesAllRanges.bind(this), createLoanHealth: this.leverageCreateLoanHealth.bind(this), + + borrowMoreMaxRecv: this.leverageBorrowMoreMaxRecv.bind(this), + borrowMoreTotalCollateral: this.leverageBorrowMoreTotalCollateral.bind(this), + borrowMoreBands: this.leverageBorrowMoreBands.bind(this), + borrowMorePrices: this.leverageBorrowMorePrices.bind(this), + borrowMoreHealth: this.leverageBorrowMoreHealth.bind(this), } } @@ -1874,27 +1889,26 @@ export class OneWayMarketTemplate { let _stateCollateral = BigInt(0); let _stateDebt = BigInt(0); if (user) { - const { _collateral, _borrowed, _debt } = await this._userState(); + const { _collateral, _borrowed, _debt } = await this._userState(user); if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); _stateCollateral = _collateral; _stateDebt = _debt; } const _userCollateral = _stateCollateral + parseUnits(userCollateral, this.collateral_token.decimals); - const userCollateralBN = toBN(_userCollateral, this.collateral_token.decimals); + const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); const contract = lending.contracts[lending.constants.ALIASES.leverage_zap].contract; const oraclePriceBand = await this.oraclePriceBand(); let pAvgBN = BN(await this.calcTickPrice(oraclePriceBand)); // upper tick of oracle price band - let maxLeverageCollateralBN = BN(0); - let _maxLeverageCollateral = BigInt(0); let maxBorrowablePrevBN = BN(0); let maxBorrowableBN = BN(0); - let _maxBorrowable = BigInt(0); + let _userEffectiveCollateral = BigInt(0); + let _maxLeverageCollateral = BigInt(0); for (let i = 0; i < 5; i++) { maxBorrowablePrevBN = maxBorrowableBN; - const _userEffectiveCollateral: bigint = _userCollateral + fromBN(BN(userBorrowed).div(pAvgBN), this.collateral_token.decimals); - _maxBorrowable = await contract.max_borrowable(this.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral, range, fromBN(pAvgBN)); + _userEffectiveCollateral = _userCollateral + fromBN(BN(userBorrowed).div(pAvgBN), this.collateral_token.decimals); + let _maxBorrowable = await contract.max_borrowable(this.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral, range, fromBN(pAvgBN)); _maxBorrowable -= _stateDebt; maxBorrowableBN = toBN(_maxBorrowable, this.borrowed_token.decimals); @@ -1903,12 +1917,14 @@ export class OneWayMarketTemplate { break; } - _maxLeverageCollateral = BigInt(await _getQuote1inch(this.addresses.borrowed_token, this.addresses.collateral_token, _maxBorrowable)); - maxLeverageCollateralBN = toBN(_maxLeverageCollateral, this.collateral_token.decimals); - pAvgBN = maxBorrowableBN.div(maxLeverageCollateralBN) + // additionalCollateral = (userBorrowed / p) + leverageCollateral + const _maxAdditionalCollateral = BigInt(await _getQuote1inch(this.addresses.borrowed_token, this.addresses.collateral_token, _maxBorrowable + _userBorrowed)); + pAvgBN = maxBorrowableBN.plus(userBorrowed).div(toBN(_maxAdditionalCollateral, this.collateral_token.decimals)); + _maxLeverageCollateral = _maxAdditionalCollateral - fromBN(BN(userBorrowed).div(pAvgBN), this.collateral_token.decimals); } - const userEffectiveCollateralBN = userCollateralBN.plus(userCollateral).plus(BN(userBorrowed).div(pAvgBN)); + const userEffectiveCollateralBN = toBN(_userEffectiveCollateral, this.collateral_token.decimals); + const maxLeverageCollateralBN = toBN(_maxLeverageCollateral, this.collateral_token.decimals); return { maxBorrowable: maxBorrowableBN.toString(), @@ -1996,7 +2012,7 @@ export class OneWayMarketTemplate { const _additionalCollateral = BigInt(await _getQuote1inch(this.addresses.borrowed_token, this.addresses.collateral_token, _debt + _userBorrowed)); let _stateCollateral = BigInt(0); if (user) { - const { _collateral, _borrowed } = await this._userState(); + const { _collateral, _borrowed } = await this._userState(user); if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); _stateCollateral = _collateral; } @@ -2012,7 +2028,7 @@ export class OneWayMarketTemplate { return await this._leverageTotalCollateral(userCollateral, userBorrowed, range); } - private async leverageGetMaxRange(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise { + private async leverageCreateLoanMaxRange(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise { const maxRecv = await this.leverageCreateLoanMaxRecvAllRanges(userCollateral, userBorrowed); for (let N = this.minBands; N <= this.maxBands; N++) { if (BN(debt).gt(maxRecv[N].maxBorrowable)) return N - 1; @@ -2025,7 +2041,7 @@ export class OneWayMarketTemplate { this._checkRange(range); let _stateDebt = BigInt(0); if (user) { - const { _debt, _borrowed } = await this._userState(); + const { _debt, _borrowed } = await this._userState(user); if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); _stateDebt = _debt; } @@ -2060,7 +2076,7 @@ export class OneWayMarketTemplate { } private async _leverageCreateLoanBandsAllRanges(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise> { - const maxN = await this.leverageGetMaxRange(userCollateral, userBorrowed, debt); + const maxN = await this.leverageCreateLoanMaxRange(userCollateral, userBorrowed, debt); const _n1_arr = await this._leverageCalcN1AllRanges(userCollateral, userBorrowed, debt, maxN); const _n2_arr: bigint[] = []; for (let N = this.minBands; N <= maxN; N++) { @@ -2133,7 +2149,7 @@ export class OneWayMarketTemplate { const totalCollateral = await this._leverageTotalCollateral(userCollateral, userBorrowed, debt, user); const _totalCollateral = parseUnits(totalCollateral, this.collateral_token.decimals); if (user) { - const { _borrowed } = await this._userState(); + const { _borrowed } = await this._userState(user); if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); } const _debt = parseUnits(debt); @@ -2148,4 +2164,45 @@ export class OneWayMarketTemplate { private async leverageCreateLoanHealth(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, full = true): Promise { return await this._leverageHealth(userCollateral, userBorrowed, debt, range, full); } + + // ---------------- LEVERAGE BORROW MORE ---------------- + + private async leverageBorrowMoreMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, address = ""): + Promise<{ maxBorrowable: string, maxTotalCollateral: string, collateralAvgPrice: string }> { + address = _getAddress(address); + const { N } = await this.userState(address); + const { maxBorrowable, maxTotalCollateral, collateralAvgPrice } = await this._leverageMaxRecv(userCollateral, userBorrowed, Number(N), address); + + return { maxBorrowable, maxTotalCollateral, collateralAvgPrice } + } + + private async leverageBorrowMoreTotalCollateral(userCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise { + address = _getAddress(address); + const { N } = await this.userState(address); + return await this._leverageTotalCollateral(userCollateral, userBorrowed, Number(N), address); + } + + private async leverageBorrowMoreBands(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, address = ""): Promise<[number, number]> { + address = _getAddress(address); + this._checkLeverageZap(); + const { N } = await this.userState(address); + const [_n2, _n1] = await this._leverageBands(userCollateral, userBorrowed, debt, Number(N), address); + + return [Number(_n2), Number(_n1)]; + } + + private async leverageBorrowMorePrices(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, address = ""): Promise { + address = _getAddress(address); + this._checkLeverageZap(); + const { N } = await this.userState(address); + const [_n2, _n1] = await this._leverageBands(userCollateral, userBorrowed, debt, Number(N), address); + + return await this._getPrices(_n2, _n1); + } + + private async leverageBorrowMoreHealth(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, full = true, address = ""): Promise { + address = _getAddress(address); + const { N } = await this.userState(address); + return await this._leverageHealth(userCollateral, userBorrowed, debt, Number(N), full, address); + } } From a2a1a0ba95d54da0dbd57f727c9df3c2c40ca4c6 Mon Sep 17 00:00:00 2001 From: macket Date: Wed, 3 Apr 2024 12:48:09 +0400 Subject: [PATCH 06/36] refactor: leverage borrowMore methods --- src/markets/OneWayMarketTemplate.ts | 59 ++++++++++++++++------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 18625e8..3524233 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -165,10 +165,10 @@ export class OneWayMarketTemplate { borrowMoreMaxRecv: (userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise<{ maxBorrowable: string, maxTotalCollateral: string, collateralAvgPrice: string }>, - borrowMoreTotalCollateral: (userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, - borrowMoreBands: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, address?: string) => Promise<[number, number]>, - borrowMorePrices: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, address?: string) => Promise, - borrowMoreHealth: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, full?: boolean, address?: string) => Promise, + borrowMoreTotalCollateral: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise, + borrowMoreBands: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise<[number, number]>, + borrowMorePrices: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise, + borrowMoreHealth: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, full?: boolean, address?: string) => Promise, }; constructor(id: string) { @@ -1062,13 +1062,17 @@ export class OneWayMarketTemplate { return await lending.contracts[this.addresses.controller].contract.loan_exists(address, lending.constantOptions); } - public async _userState(address = ""): Promise<{ _collateral: bigint, _borrowed: bigint, _debt: bigint, _N: bigint }> { + public _userState = memoize(async (address = ""): Promise<{ _collateral: bigint, _borrowed: bigint, _debt: bigint, _N: bigint }> => { address = _getAddress(address); const contract = lending.contracts[this.addresses.controller].contract; const [_collateral, _borrowed, _debt, _N] = await contract.user_state(address, lending.constantOptions) as bigint[]; return { _collateral, _borrowed, _debt, _N } - } + }, + { + promise: true, + maxAge: 3 * 1000, // 3s + }); public async userState(address = ""): Promise<{ collateral: string, borrowed: string, debt: string, N: string }> { const { _collateral, _borrowed, _debt, _N } = await this._userState(address); @@ -1853,7 +1857,7 @@ export class OneWayMarketTemplate { // ---------------- LEVERAGE CREATE LOAN ---------------- private _checkLeverageZap(): void { - if (lending.constants.ALIASES.leverage_zap === "0x0000000000000000000000000000000000000000") { + if (lending.constants.ALIASES.leverage_zap === lending.constants.ZERO_ADDRESS) { throw Error(`There is no leverage contract on this network. ID: ${lending.chainId}`); } } @@ -1885,14 +1889,15 @@ export class OneWayMarketTemplate { Promise<{ maxBorrowable: string, maxTotalCollateral: string, maxLeverage: string, collateralAvgPrice: string }> { // max_borrowable = userCollateral / (1 / (k_effective * max_p_base) - 1 / p_avg) this._checkLeverageZap(); - this._checkRange(range); + if (range > 0) this._checkRange(range); let _stateCollateral = BigInt(0); let _stateDebt = BigInt(0); if (user) { - const { _collateral, _borrowed, _debt } = await this._userState(user); + const { _collateral, _borrowed, _debt, _N } = await this._userState(user); if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); _stateCollateral = _collateral; _stateDebt = _debt; + if (range < 0) range = Number(lending.formatUnits(_N, 0)); } const _userCollateral = _stateCollateral + parseUnits(userCollateral, this.collateral_token.decimals); const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); @@ -2038,12 +2043,13 @@ export class OneWayMarketTemplate { } private _leverageCalcN1 = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, user?: string): Promise => { - this._checkRange(range); + if (range > 0) this._checkRange(range); let _stateDebt = BigInt(0); if (user) { - const { _debt, _borrowed } = await this._userState(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(lending.formatUnits(_N, 0)); } const _totalCollateral = parseUnits(await this._leverageTotalCollateral(userCollateral, userBorrowed, debt, user), this.collateral_token.decimals); const _debt = _stateDebt + parseUnits(debt, this.borrowed_token.decimals); @@ -2070,6 +2076,10 @@ export class OneWayMarketTemplate { 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); + if (range < 0) { + const { N } = await this.userState(user); + range = Number(N); + } const _n2 = _n1 + BigInt(range - 1); return [_n2, _n1]; @@ -2146,11 +2156,13 @@ export class OneWayMarketTemplate { user = lending.constants.ZERO_ADDRESS ): Promise { this._checkLeverageZap(); + if (range > 0) this._checkRange(range); const totalCollateral = await this._leverageTotalCollateral(userCollateral, userBorrowed, debt, user); const _totalCollateral = parseUnits(totalCollateral, this.collateral_token.decimals); if (user) { - const { _borrowed } = await this._userState(user); + const { _borrowed, _N } = await this._userState(user); if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); + if (range < 0) range = Number(lending.formatUnits(_N, 0)); } const _debt = parseUnits(debt); @@ -2170,39 +2182,34 @@ export class OneWayMarketTemplate { private async leverageBorrowMoreMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise<{ maxBorrowable: string, maxTotalCollateral: string, collateralAvgPrice: string }> { address = _getAddress(address); - const { N } = await this.userState(address); - const { maxBorrowable, maxTotalCollateral, collateralAvgPrice } = await this._leverageMaxRecv(userCollateral, userBorrowed, Number(N), address); + const { maxBorrowable, maxTotalCollateral, collateralAvgPrice } = await this._leverageMaxRecv(userCollateral, userBorrowed, -1, address); return { maxBorrowable, maxTotalCollateral, collateralAvgPrice } } - private async leverageBorrowMoreTotalCollateral(userCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise { + private async leverageBorrowMoreTotalCollateral(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address = ""): Promise { address = _getAddress(address); - const { N } = await this.userState(address); - return await this._leverageTotalCollateral(userCollateral, userBorrowed, Number(N), address); + return await this._leverageTotalCollateral(userCollateral, userBorrowed, dDebt, address); } - private async leverageBorrowMoreBands(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, address = ""): Promise<[number, number]> { + private async leverageBorrowMoreBands(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address = ""): Promise<[number, number]> { address = _getAddress(address); this._checkLeverageZap(); - const { N } = await this.userState(address); - const [_n2, _n1] = await this._leverageBands(userCollateral, userBorrowed, debt, Number(N), address); + const [_n2, _n1] = await this._leverageBands(userCollateral, userBorrowed, dDebt, -1, address); return [Number(_n2), Number(_n1)]; } - private async leverageBorrowMorePrices(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, address = ""): Promise { + private async leverageBorrowMorePrices(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address = ""): Promise { address = _getAddress(address); this._checkLeverageZap(); - const { N } = await this.userState(address); - const [_n2, _n1] = await this._leverageBands(userCollateral, userBorrowed, debt, Number(N), address); + const [_n2, _n1] = await this._leverageBands(userCollateral, userBorrowed, dDebt, -1, address); return await this._getPrices(_n2, _n1); } - private async leverageBorrowMoreHealth(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, full = true, address = ""): Promise { + private async leverageBorrowMoreHealth(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, full = true, address = ""): Promise { address = _getAddress(address); - const { N } = await this.userState(address); - return await this._leverageHealth(userCollateral, userBorrowed, debt, Number(N), full, address); + return await this._leverageHealth(userCollateral, userBorrowed, dDebt, -1, full, address); } } From d6e2048ae4a8f3fe4b7f9e9cd36f2ab4587e10c7 Mon Sep 17 00:00:00 2001 From: macket Date: Wed, 3 Apr 2024 15:52:57 +0400 Subject: [PATCH 07/36] feat: leverage repay (calc methods) --- src/markets/OneWayMarketTemplate.ts | 116 ++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 3524233..85ce929 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -169,6 +169,13 @@ export class OneWayMarketTemplate { borrowMoreBands: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise<[number, number]>, borrowMorePrices: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise, borrowMoreHealth: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, full?: boolean, address?: string) => Promise, + + repayExpected: (repayCollateral: TAmount, userBorrowed: TAmount) => Promise, + repayIsFull: (repayCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, + repayIsAvailable: (repayCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, + repayBands: (repayCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise<[number, number]>, + repayPrices: (repayCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, + repayHealth: (repayCollateral: TAmount, userBorrowed: TAmount, full?: boolean, address?: string) => Promise, }; constructor(id: string) { @@ -281,6 +288,13 @@ export class OneWayMarketTemplate { borrowMoreBands: this.leverageBorrowMoreBands.bind(this), borrowMorePrices: this.leverageBorrowMorePrices.bind(this), borrowMoreHealth: this.leverageBorrowMoreHealth.bind(this), + + repayExpected: this.leverageRepayExpected.bind(this), + repayIsFull: this.leverageRepayIsFull.bind(this), + repayIsAvailable: this.leverageRepayIsAvailable.bind(this), + repayBands: this.leverageRepayBands.bind(this), + repayPrices: this.leverageRepayPrices.bind(this), + repayHealth: this.leverageRepayHealth.bind(this), } } @@ -2212,4 +2226,106 @@ export class OneWayMarketTemplate { address = _getAddress(address); return await this._leverageHealth(userCollateral, userBorrowed, dDebt, -1, full, address); } + + // ---------------- LEVERAGE REPAY ---------------- + + private leverageRepayExpected = memoize( async (repayCollateral: TAmount, userBorrowed: TAmount): Promise => { + this._checkLeverageZap(); + const _repayCollateral = parseUnits(repayCollateral, this.collateral_token.decimals); + let _borrowedExpected = BigInt(await _getQuote1inch(this.addresses.collateral_token, this.addresses.borrowed_token, _repayCollateral)); + _borrowedExpected = _borrowedExpected + parseUnits(userBorrowed, this.borrowed_token.decimals); + + return lending.formatUnits(_borrowedExpected, this.borrowed_token.decimals) + }, + { + promise: true, + maxAge: 5 * 60 * 1000, // 5m + }); + + private async leverageRepayIsFull(repayCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise { + this._checkLeverageZap(); + address = _getAddress(address); + const { borrowed: stateBorrowed, debt } = await this.userState(address); + const repayExpected = await this.leverageRepayExpected(repayCollateral, userBorrowed); + + return BN(stateBorrowed).plus(repayExpected).gt(debt); + } + + private async leverageRepayIsAvailable(repayCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise { + // 0. const { collateral, stablecoin, debt } = await this.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(address); + const { collateral, borrowed, debt } = await this.userState(address); + // Loan does not exist + if (BN(debt).eq(0)) return false; + // Can't spend more than user has + if (BN(repayCollateral).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(repayCollateral, userBorrowed, address); + + return true; + } + + private _leverageRepayBands = memoize( async (repayCollateral: TAmount, userBorrowed: TAmount, address: string): Promise<[bigint, bigint]> => { + address = _getAddress(address); + if (!(await this.leverageRepayIsAvailable(repayCollateral, userBorrowed, address))) return [parseUnits(0, 0), parseUnits(0, 0)]; + + const _repayCollateral = parseUnits(repayCollateral, 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 < _repayCollateral) throw Error(`Can't use more collateral than user's position has (${_repayCollateral}) > ${_stateCollateral})`); + + let _n1 = parseUnits(0, 0); + let _n2 = parseUnits(0, 0); + const repayExpected = await this.leverageRepayExpected(repayCollateral, userBorrowed); + const _repayExpected = parseUnits(repayExpected, this.borrowed_token.decimals); + try { + _n1 = await lending.contracts[this.addresses.controller].contract.calculate_debt_n1(_stateCollateral - _repayCollateral, _stateDebt - _repayExpected, _N); + _n2 = _n1 + (_N - BigInt(1)); + } catch (e) { + console.log("Full repayment"); + } + + return [_n2, _n1]; + }, + { + promise: true, + maxAge: 5 * 60 * 1000, // 5m + }); + + private async leverageRepayBands(repayCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise<[number, number]> { + this._checkLeverageZap(); + const [_n2, _n1] = await this._leverageRepayBands(repayCollateral, userBorrowed, address); + + return [Number(_n2), Number(_n1)]; + } + + private async leverageRepayPrices(repayCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise { + this._checkLeverageZap(); + const [_n2, _n1] = await this._leverageRepayBands(repayCollateral, userBorrowed, address); + + return await this._getPrices(_n2, _n1); + } + + private async leverageRepayHealth(repayCollateral: TAmount, userBorrowed: TAmount, full = true, address = ""): Promise { + this._checkLeverageZap(); + address = _getAddress(address); + const { _borrowed: _stateBorrowed, _debt, _N } = await this._userState(address); + if (_stateBorrowed > BigInt(0)) return "0.0"; + if (!(await this.leverageRepayIsAvailable(repayCollateral, userBorrowed, address))) return "0.0"; + + const repayExpected = await this.leverageRepayExpected(repayCollateral, userBorrowed); + const _dCollateral = parseUnits(repayCollateral, this.collateral_token.decimals) * BigInt(-1); + const _dDebt = parseUnits(repayExpected, this.borrowed_token.decimals) * BigInt(-1); + + if (_debt + _dDebt <= BigInt(0)) return "0.0"; + const contract = lending.contracts[this.addresses.controller].contract; + let _health = await contract.health_calculator(address, _dCollateral, _dDebt, full, _N, lending.constantOptions) as bigint; + _health = _health * BigInt(100); + + return lending.formatUnits(_health); + } } From ae7da15334bf5f9cdcefc764a2c8b6949cfc91c1 Mon Sep 17 00:00:00 2001 From: macket Date: Tue, 9 Apr 2024 17:06:34 +0400 Subject: [PATCH 08/36] chore: borrow_more_extended and callback_bytes in Controller ABI --- src/constants/abis/Controller.json | 48 ++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/constants/abis/Controller.json b/src/constants/abis/Controller.json index 7efc4cf..2cb6398 100644 --- a/src/constants/abis/Controller.json +++ b/src/constants/abis/Controller.json @@ -401,6 +401,10 @@ { "name": "callback_args", "type": "uint256[]" + }, + { + "name": "callback_bytes", + "type": "bytes" } ], "name": "create_loan_extended", @@ -452,6 +456,46 @@ "stateMutability": "payable", "type": "function" }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "borrow_more_extended", + "inputs": [ + { + "name": "collateral", + "type": "uint256" + }, + { + "name": "debt", + "type": "uint256" + }, + { + "name": "callbacker", + "type": "address" + }, + { + "name": "callback_args", + "type": "uint256[]" + }, + { + "name": "callback_bytes", + "type": "bytes" + } + ], + "outputs": [] + }, + { + "stateMutability": "nonpayable", + "type": "function", + "name": "repay", + "inputs": [ + { + "name": "_d_debt", + "type": "uint256" + } + ], + "outputs": [] + }, { "inputs": [ { @@ -481,6 +525,10 @@ { "name": "callback_args", "type": "uint256[]" + }, + { + "name": "callback_bytes", + "type": "bytes" } ], "name": "repay_extended", From 98c8a425519920fd34b96a94aa6156a78dccac92 Mon Sep 17 00:00:00 2001 From: macket Date: Tue, 9 Apr 2024 17:07:23 +0400 Subject: [PATCH 09/36] chore: remove max_p_base from LeverageZap ABI --- src/constants/abis/LeverageZap.json | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/constants/abis/LeverageZap.json b/src/constants/abis/LeverageZap.json index 1bc8715..5f695ea 100644 --- a/src/constants/abis/LeverageZap.json +++ b/src/constants/abis/LeverageZap.json @@ -1,21 +1,4 @@ [ - { - "stateMutability": "view", - "type": "function", - "name": "max_p_base", - "inputs": [ - { - "name": "controller", - "type": "address" - } - ], - "outputs": [ - { - "name": "", - "type": "uint256" - } - ] - }, { "stateMutability": "view", "type": "function", From 69e21cea10d0dd2223ace4bbcdeac83d0edab861 Mon Sep 17 00:00:00 2001 From: macket Date: Tue, 9 Apr 2024 17:12:27 +0400 Subject: [PATCH 10/36] feat: _getCalldata1inch and _getRoute1inch --- src/external-api.ts | 39 +++++++++++++++++++++++++++++++++++---- src/interfaces.ts | 9 ++++++++- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/external-api.ts b/src/external-api.ts index 4d26114..c1e03c3 100644 --- a/src/external-api.ts +++ b/src/external-api.ts @@ -1,7 +1,7 @@ import axios from "axios"; import memoize from "memoizee"; import { lending } from "./lending.js"; -import { IExtendedPoolDataFromApi, INetworkName, IPoolFactory } from "./interfaces"; +import { IExtendedPoolDataFromApi, INetworkName, IPoolFactory, T1inchRouteStep } from "./interfaces"; export const _getPoolsFromApi = memoize( @@ -43,8 +43,7 @@ export const _getUserCollateral = memoize( export const _getQuote1inch = memoize( async (fromToken: string, toToken: string, _amount: bigint): Promise => { if (_amount === BigInt(0)) return "0.0"; - const url = `https://api.1inch.dev/swap/v5.2/1/quote?src=${fromToken}&dst=${toToken}&amount=${_amount}& - protocols=${lending.constants.PROTOCOLS_1INCH}&includeTokensInfo=true&includeProtocols=true`; + const url = `https://api.1inch.dev/swap/v6.0/${lending.chainId}/quote?src=${fromToken}&dst=${toToken}&amount=${_amount}&protocols=${lending.constants.PROTOCOLS_1INCH}&includeTokensInfo=true&includeProtocols=true`; const response = await axios.get( url, { @@ -54,7 +53,7 @@ export const _getQuote1inch = memoize( if (response.status !== 200) { throw Error(`1inch error: ${response.status} ${response.statusText}`); } - return response.data.toAmount; + return response.data.dstAmount; }, { @@ -62,3 +61,35 @@ export const _getQuote1inch = memoize( maxAge: 5 * 1000, // 5s } ) + +const _getSwapData1inch = memoize( + async (fromToken: string, toToken: string, _amount: bigint, slippage: number): Promise<{ tx: { data: string }, protocols: T1inchRouteStep[][] }> => { + if (_amount === BigInt(0)) throw Error("Amount must be > 0"); + const url = `https://api.1inch.dev/swap/v6.0/${lending.chainId}/swap?src=${fromToken}&dst=${toToken}&amount=${_amount}&from=${lending.constants.ALIASES.leverage_zap}&slippage=${slippage}&protocols=${lending.constants.PROTOCOLS_1INCH}&includeTokensInfo=true&includeProtocols=true&disableEstimate=true`; + const response = await axios.get( + url, + { + headers: {"accept": "application/json", "Authorization": `Bearer ${lending.apiKey1inch}`}, + validateStatus: () => true, + }); + if (response.status !== 200) { + throw Error(`1inch error: ${response.status} ${response.statusText}`); + } + return response.data; + + }, + { + promise: true, + maxAge: 5 * 1000, // 5s + } +) + +export const _getCalldata1inch = async (fromToken: string, toToken: string, _amount: bigint, slippage: number) => { + const data = await _getSwapData1inch(fromToken, toToken, _amount, slippage); + return data.tx.data; +} + +export const _getRoute1inch = async (fromToken: string, toToken: string, _amount: bigint, slippage: number) => { + const data = await _getSwapData1inch(fromToken, toToken, _amount, slippage); + return data.protocols; +} diff --git a/src/interfaces.ts b/src/interfaces.ts index 5b74868..ef20633 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -121,4 +121,11 @@ export interface IReward { tokenAddress: string, symbol: string, apy: number -} \ No newline at end of file +} + +export type T1inchRouteStep = { + name: string, + part: number, + fromTokenAddress: string, + toTokenAddress: string, +}[] From 2afc2aa9b83db0f2a861b97f0a7b8e9c509a96b1 Mon Sep 17 00:00:00 2001 From: macket Date: Tue, 9 Apr 2024 17:49:08 +0400 Subject: [PATCH 11/36] chore: the list of 1inch protocols for arbitrum --- src/lending.ts | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/src/lending.ts b/src/lending.ts index 8cda4ff..8df6344 100644 --- a/src/lending.ts +++ b/src/lending.ts @@ -53,6 +53,7 @@ export const NETWORK_CONSTANTS: { [index: number]: any } = { NAME: 'ethereum', ALIASES: ALIASES_ETHEREUM, COINS: COINS_ETHEREUM, + PROTOCOLS_1INCH: "UNISWAP_V1,UNISWAP_V2,SUSHI,MOONISWAP,BALANCER,COMPOUND,CURVE,CURVE_V2_SPELL_2_ASSET,CURVE_V2_SGT_2_ASSET,CURVE_V2_THRESHOLDNETWORK_2_ASSET,CHAI,OASIS,KYBER,AAVE,IEARN,BANCOR,SWERVE,BLACKHOLESWAP,DODO,DODO_V2,VALUELIQUID,SHELL,DEFISWAP,SAKESWAP,LUASWAP,MINISWAP,MSTABLE,SYNTHETIX,AAVE_V2,ST_ETH,ONE_INCH_LP,ONE_INCH_LP_1_1,LINKSWAP,S_FINANCE,PSM,POWERINDEX,XSIGMA,SMOOTHY_FINANCE,SADDLE,KYBER_DMM,BALANCER_V2,UNISWAP_V3,SETH_WRAPPER,CURVE_V2,CURVE_V2_EURS_2_ASSET,CURVE_V2_ETH_CRV,CURVE_V2_ETH_CVX,CONVERGENCE_X,ONE_INCH_LIMIT_ORDER,ONE_INCH_LIMIT_ORDER_V2,ONE_INCH_LIMIT_ORDER_V3,ONE_INCH_LIMIT_ORDER_V4,DFX_FINANCE,FIXED_FEE_SWAP,DXSWAP,SHIBASWAP,UNIFI,PSM_PAX,WSTETH,DEFI_PLAZA,FIXED_FEE_SWAP_V3,SYNTHETIX_WRAPPER,SYNAPSE,CURVE_V2_YFI_2_ASSET,CURVE_V2_ETH_PAL,POOLTOGETHER,ETH_BANCOR_V3,ELASTICSWAP,BALANCER_V2_WRAPPER,FRAXSWAP,RADIOSHACK,KYBERSWAP_ELASTIC,CURVE_V2_TWO_CRYPTO,STABLE_PLAZA,ZEROX_LIMIT_ORDER,CURVE_3CRV,KYBER_DMM_STATIC,ANGLE,ROCKET_POOL,ETHEREUM_ELK,ETHEREUM_PANCAKESWAP_V2,SYNTHETIX_ATOMIC_SIP288,PSM_GUSD,INTEGRAL,MAINNET_SOLIDLY,NOMISWAP_STABLE,CURVE_V2_TWOCRYPTO_META,MAVERICK_V1,VERSE,DFX_FINANCE_V3,ZK_BOB,PANCAKESWAP_V3,NOMISWAPEPCS,XFAI,CURVE_V2_TRICRYPTO_NG,SUSHISWAP_V3,SFRX_ETH,SDAI,ETHEREUM_WOMBATSWAP,CARBON,COMPOUND_V3,DODO_V3,SMARDEX,TRADERJOE_V2_1,PMM15,SOLIDLY_V3,RAFT_PSM,CLAYSTACK,CURVE_STABLE_NG,LIF3,BLUEPRINT,AAVE_V3,ORIGIN,BGD_AAVE_STATIC", }, 10: { NAME: 'optimism', @@ -103,6 +104,7 @@ export const NETWORK_CONSTANTS: { [index: number]: any } = { NAME: 'arbitrum', ALIASES: ALIASES_ARBITRUM, COINS: COINS_ARBITRUM, + PROTOCOLS_1INCH: "ARBITRUM_BALANCER_V2,ARBITRUM_ONE_INCH_LIMIT_ORDER,ARBITRUM_ONE_INCH_LIMIT_ORDER_V2,ARBITRUM_ONE_INCH_LIMIT_ORDER_V3,ARBITRUM_ONE_INCH_LIMIT_ORDER_V4,ARBITRUM_DODO,ARBITRUM_DODO_V2,ARBITRUM_SUSHISWAP,ARBITRUM_DXSWAP,ARBITRUM_UNISWAP_V3,ARBITRUM_CURVE,ARBITRUM_CURVE_V2,ARBITRUM_GMX,ARBITRUM_SYNAPSE,ARBITRUM_SADDLE,ARBITRUM_KYBERSWAP_ELASTIC,ARBITRUM_KYBER_DMM_STATIC,ARBITRUM_AAVE_V3,ARBITRUM_ELK,ARBITRUM_WOOFI_V2,ARBITRUM_CAMELOT,ARBITRUM_TRADERJOE,ARBITRUM_TRADERJOE_V2,ARBITRUM_SWAPFISH,ARBITRUM_ZYBER,ARBITRUM_ZYBER_STABLE,ARBITRUM_SOLIDLIZARD,ARBITRUM_ZYBER_V3,ARBITRUM_MYCELIUM,ARBITRUM_TRIDENT,ARBITRUM_SHELL_OCEAN,ARBITRUM_RAMSES,ARBITRUM_TRADERJOE_V2_1,ARBITRUM_NOMISWAPEPCS,ARBITRUM_CAMELOT_V3,ARBITRUM_WOMBATSWAP,ARBITRUM_CHRONOS,ARBITRUM_LIGHTER,ARBITRUM_ARBIDEX,ARBITRUM_ARBIDEX_V3,ARBSWAP,ARBSWAP_STABLE,ARBITRUM_SUSHISWAP_V3,ARBITRUM_RAMSES_V2,ARBITRUM_LEVEL_FINANCE,ARBITRUM_CHRONOS_V3,ARBITRUM_PANCAKESWAP_V3,ARBITRUM_PMM11,ARBITRUM_DODO_V3,ARBITRUM_SMARDEX,ARBITRUM_INTEGRAL,ARBITRUM_DFX_FINANCE_V3,ARBITRUM_CURVE_STABLE_NG", }, 42220: { NAME: 'celo', @@ -165,19 +167,7 @@ class Lending implements ILending { NETWORK_NAME: 'ethereum', ALIASES: {}, ZERO_ADDRESS: ethers.ZeroAddress, - PROTOCOLS_1INCH: "UNISWAP_V1,UNISWAP_V2,SUSHI,MOONISWAP,BALANCER,COMPOUND,CURVE,CURVE_V2_SPELL_2_ASSET," + - "CURVE_V2_SGT_2_ASSET,CURVE_V2_THRESHOLDNETWORK_2_ASSET,CHAI,OASIS,KYBER,AAVE,IEARN,BANCOR,SWERVE," + - "BLACKHOLESWAP,DODO,DODO_V2,VALUELIQUID,SHELL,DEFISWAP,SAKESWAP,LUASWAP,MINISWAP,MSTABLE,PMM2," + - "SYNTHETIX,AAVE_V2,ST_ETH,ONE_INCH_LP,ONE_INCH_LP_1_1,LINKSWAP,S_FINANCE,PSM,POWERINDEX,XSIGMA," + - "SMOOTHY_FINANCE,SADDLE,KYBER_DMM,BALANCER_V2,UNISWAP_V3,SETH_WRAPPER,CURVE_V2,CURVE_V2_EURS_2_ASSET," + - "CURVE_V2_ETH_CRV,CURVE_V2_ETH_CVX,CONVERGENCE_X,ONE_INCH_LIMIT_ORDER,ONE_INCH_LIMIT_ORDER_V2," + - "ONE_INCH_LIMIT_ORDER_V3,DFX_FINANCE,FIXED_FEE_SWAP,DXSWAP,SHIBASWAP,UNIFI,PSM_PAX,WSTETH,DEFI_PLAZA," + - "FIXED_FEE_SWAP_V3,SYNTHETIX_WRAPPER,SYNAPSE,CURVE_V2_YFI_2_ASSET,CURVE_V2_ETH_PAL,POOLTOGETHER," + - "ETH_BANCOR_V3,ELASTICSWAP,BALANCER_V2_WRAPPER,FRAXSWAP,RADIOSHACK,KYBERSWAP_ELASTIC,CURVE_V2_TWO_CRYPTO," + - "STABLE_PLAZA,ZEROX_LIMIT_ORDER,CURVE_3CRV,KYBER_DMM_STATIC,ANGLE,ROCKET_POOL,ETHEREUM_ELK,ETHEREUM_PANCAKESWAP_V2," + - "SYNTHETIX_ATOMIC_SIP288,PSM_GUSD,INTEGRAL,MAINNET_SOLIDLY,NOMISWAP_STABLE,CURVE_V2_TWOCRYPTO_META,MAVERICK_V1,VERSE," + - "DFX_FINANCE_V2,ZK_BOB,PANCAKESWAP_V3,NOMISWAPEPCS,XFAI,CURVE_V2_TRICRYPTO_NG,PMM8_2,SUSHISWAP_V3,SFRX_ETH,SDAI," + - "ETHEREUM_WOMBATSWAP,PMM12,CARBON,COMPOUND_V3,DODO_V3,SMARDEX,TRADERJOE_V2_1", + PROTOCOLS_1INCH: "", }; } @@ -207,19 +197,7 @@ class Lending implements ILending { NETWORK_NAME: 'ethereum', ALIASES: {}, ZERO_ADDRESS: ethers.ZeroAddress, - PROTOCOLS_1INCH: "UNISWAP_V1,UNISWAP_V2,SUSHI,MOONISWAP,BALANCER,COMPOUND,CURVE,CURVE_V2_SPELL_2_ASSET," + - "CURVE_V2_SGT_2_ASSET,CURVE_V2_THRESHOLDNETWORK_2_ASSET,CHAI,OASIS,KYBER,AAVE,IEARN,BANCOR,SWERVE," + - "BLACKHOLESWAP,DODO,DODO_V2,VALUELIQUID,SHELL,DEFISWAP,SAKESWAP,LUASWAP,MINISWAP,MSTABLE,PMM2," + - "SYNTHETIX,AAVE_V2,ST_ETH,ONE_INCH_LP,ONE_INCH_LP_1_1,LINKSWAP,S_FINANCE,PSM,POWERINDEX,XSIGMA," + - "SMOOTHY_FINANCE,SADDLE,KYBER_DMM,BALANCER_V2,UNISWAP_V3,SETH_WRAPPER,CURVE_V2,CURVE_V2_EURS_2_ASSET," + - "CURVE_V2_ETH_CRV,CURVE_V2_ETH_CVX,CONVERGENCE_X,ONE_INCH_LIMIT_ORDER,ONE_INCH_LIMIT_ORDER_V2," + - "ONE_INCH_LIMIT_ORDER_V3,DFX_FINANCE,FIXED_FEE_SWAP,DXSWAP,SHIBASWAP,UNIFI,PSM_PAX,WSTETH,DEFI_PLAZA," + - "FIXED_FEE_SWAP_V3,SYNTHETIX_WRAPPER,SYNAPSE,CURVE_V2_YFI_2_ASSET,CURVE_V2_ETH_PAL,POOLTOGETHER," + - "ETH_BANCOR_V3,ELASTICSWAP,BALANCER_V2_WRAPPER,FRAXSWAP,RADIOSHACK,KYBERSWAP_ELASTIC,CURVE_V2_TWO_CRYPTO," + - "STABLE_PLAZA,ZEROX_LIMIT_ORDER,CURVE_3CRV,KYBER_DMM_STATIC,ANGLE,ROCKET_POOL,ETHEREUM_ELK,ETHEREUM_PANCAKESWAP_V2," + - "SYNTHETIX_ATOMIC_SIP288,PSM_GUSD,INTEGRAL,MAINNET_SOLIDLY,NOMISWAP_STABLE,CURVE_V2_TWOCRYPTO_META,MAVERICK_V1,VERSE," + - "DFX_FINANCE_V2,ZK_BOB,PANCAKESWAP_V3,NOMISWAPEPCS,XFAI,CURVE_V2_TRICRYPTO_NG,PMM8_2,SUSHISWAP_V3,SFRX_ETH,SDAI," + - "ETHEREUM_WOMBATSWAP,PMM12,CARBON,COMPOUND_V3,DODO_V3,SMARDEX,TRADERJOE_V2_1", + PROTOCOLS_1INCH: "", }; // JsonRpc provider @@ -274,6 +252,7 @@ class Lending implements ILending { this.constants.NETWORK_NAME = NETWORK_CONSTANTS[this.chainId].NAME; this.constants.ALIASES = NETWORK_CONSTANTS[this.chainId].ALIASES; this.constants.COINS = NETWORK_CONSTANTS[this.chainId].COINS; + this.constants.PROTOCOLS_1INCH = NETWORK_CONSTANTS[this.chainId].PROTOCOLS_1INCH; this.setContract(this.constants.ALIASES.crv, ERC20ABI); this.multicallProvider = new MulticallProvider(this.chainId, this.provider); From babe1cfe6c9e91fb62d198abf2a143e0e4c0e099 Mon Sep 17 00:00:00 2001 From: macket Date: Wed, 10 Apr 2024 12:14:00 +0400 Subject: [PATCH 12/36] feat: leverage createLoan, borrowMore and repay txs --- src/markets/OneWayMarketTemplate.ts | 230 +++++++++++++++++++++++++++- 1 file changed, 228 insertions(+), 2 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 85ce929..a9c080f 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -23,7 +23,7 @@ import { smartNumber, } from "../utils.js"; import { IDict, TGas, TAmount, IReward } from "../interfaces.js"; -import { _getQuote1inch } from "../external-api.js"; +import { _getQuote1inch, _getCalldata1inch } from "../external-api.js"; import ERC20Abi from '../constants/abis/ERC20.json' assert { type: 'json' }; @@ -162,6 +162,9 @@ export class OneWayMarketTemplate { createLoanPrices: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number) => Promise, createLoanPricesAllRanges: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise>, createLoanHealth: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, full?: boolean) => Promise, + createLoanIsApproved: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + createLoanApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + createLoan: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, slippage: number) => Promise, borrowMoreMaxRecv: (userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise<{ maxBorrowable: string, maxTotalCollateral: string, collateralAvgPrice: string }>, @@ -169,6 +172,9 @@ export class OneWayMarketTemplate { borrowMoreBands: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise<[number, number]>, borrowMorePrices: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise, borrowMoreHealth: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, full?: boolean, address?: string) => Promise, + borrowMoreIsApproved: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + borrowMoreApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + borrowMore: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, slippage?: number) => Promise, repayExpected: (repayCollateral: TAmount, userBorrowed: TAmount) => Promise, repayIsFull: (repayCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, @@ -176,6 +182,20 @@ export class OneWayMarketTemplate { repayBands: (repayCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise<[number, number]>, repayPrices: (repayCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, repayHealth: (repayCollateral: TAmount, userBorrowed: TAmount, full?: boolean, address?: string) => Promise, + repayIsApproved: (repayCollateral: TAmount, userBorrowed: TAmount) => Promise, + repayApprove: (repayCollateral: TAmount, userBorrowed: TAmount) => Promise, + repay: (repayCollateral: TAmount, userBorrowed: TAmount, slippage?: number) => Promise, + + estimateGas: { + createLoanApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + createLoan: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, slippage?: number) => Promise, + + borrowMoreApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + borrowMore: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, slippage?: number) => Promise, + + repayApprove: (repayCollateral: TAmount, userBorrowed: TAmount) => Promise, + repay: (repayCollateral: TAmount, userBorrowed: TAmount, slippage?: number) => Promise, + } }; constructor(id: string) { @@ -282,12 +302,18 @@ export class OneWayMarketTemplate { createLoanPrices: this.leverageCreateLoanPrices.bind(this), createLoanPricesAllRanges: this.leverageCreateLoanPricesAllRanges.bind(this), createLoanHealth: this.leverageCreateLoanHealth.bind(this), + createLoanIsApproved: this.leverageCreateLoanIsApproved.bind(this), + createLoanApprove: this.leverageCreateLoanApprove.bind(this), + createLoan: this.leverageCreateLoan.bind(this), borrowMoreMaxRecv: this.leverageBorrowMoreMaxRecv.bind(this), borrowMoreTotalCollateral: this.leverageBorrowMoreTotalCollateral.bind(this), borrowMoreBands: this.leverageBorrowMoreBands.bind(this), borrowMorePrices: this.leverageBorrowMorePrices.bind(this), borrowMoreHealth: this.leverageBorrowMoreHealth.bind(this), + borrowMoreIsApproved: this.leverageCreateLoanIsApproved.bind(this), + borrowMoreApprove: this.leverageCreateLoanApprove.bind(this), + borrowMore: this.leverageBorrowMore.bind(this), repayExpected: this.leverageRepayExpected.bind(this), repayIsFull: this.leverageRepayIsFull.bind(this), @@ -295,6 +321,20 @@ export class OneWayMarketTemplate { repayBands: this.leverageRepayBands.bind(this), repayPrices: this.leverageRepayPrices.bind(this), repayHealth: this.leverageRepayHealth.bind(this), + repayIsApproved: this.leverageRepayIsApproved.bind(this), + repayApprove: this.leverageRepayApprove.bind(this), + repay: this.leverageRepay.bind(this), + + estimateGas: { + createLoanApprove: this.leverageCreateLoanApproveEstimateGas.bind(this), + createLoan: this.leverageCreateLoanEstimateGas.bind(this), + + borrowMoreApprove: this.leverageCreateLoanApproveEstimateGas.bind(this), + borrowMore: this.leverageBorrowMoreEstimateGas.bind(this), + + repayApprove: this.leverageRepayApproveEstimateGas.bind(this), + repay: this.leverageRepayEstimateGas.bind(this), + }, } } @@ -1928,6 +1968,7 @@ export class OneWayMarketTemplate { maxBorrowablePrevBN = maxBorrowableBN; _userEffectiveCollateral = _userCollateral + fromBN(BN(userBorrowed).div(pAvgBN), this.collateral_token.decimals); let _maxBorrowable = await contract.max_borrowable(this.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral, range, fromBN(pAvgBN)); + if (_maxBorrowable === BigInt(0)) break; _maxBorrowable -= _stateDebt; maxBorrowableBN = toBN(_maxBorrowable, this.borrowed_token.decimals); @@ -1942,7 +1983,7 @@ export class OneWayMarketTemplate { _maxLeverageCollateral = _maxAdditionalCollateral - fromBN(BN(userBorrowed).div(pAvgBN), this.collateral_token.decimals); } - const userEffectiveCollateralBN = toBN(_userEffectiveCollateral, this.collateral_token.decimals); + const userEffectiveCollateralBN = maxBorrowableBN.gt(0) ? toBN(_userEffectiveCollateral, this.collateral_token.decimals) : BN(0); const maxLeverageCollateralBN = toBN(_maxLeverageCollateral, this.collateral_token.decimals); return { @@ -2191,6 +2232,89 @@ export class OneWayMarketTemplate { return await this._leverageHealth(userCollateral, userBorrowed, debt, range, full); } + private async leverageCreateLoanIsApproved(userCollateral: TAmount, userBorrowed: TAmount): Promise { + const collateralAllowance = await hasAllowance( + [this.collateral_token.address], [userCollateral], lending.signerAddress, this.addresses.controller); + const borrowedAllowance = await hasAllowance( + [this.borrowed_token.address], [userBorrowed], lending.signerAddress, lending.constants.ALIASES.leverage_zap); + + return collateralAllowance && borrowedAllowance + } + + private async leverageCreateLoanApproveEstimateGas (userCollateral: TAmount, userBorrowed: TAmount): Promise { + const collateralGas = await ensureAllowanceEstimateGas( + [this.collateral_token.address], [userCollateral], this.addresses.controller); + const borrowedGas = await ensureAllowanceEstimateGas( + [this.borrowed_token.address], [userBorrowed], lending.constants.ALIASES.leverage_zap); + + if(Array.isArray(collateralGas) && Array.isArray(borrowedGas)) { + return [collateralGas[0] + borrowedGas[0], collateralGas[1] + borrowedGas[1]] + } else { + return (collateralGas as number) + (borrowedGas as number) + } + } + + private async leverageCreateLoanApprove(userCollateral: TAmount, userBorrowed: TAmount): Promise { + const collateralApproveTx = await ensureAllowance( + [this.collateral_token.address], [userCollateral], this.addresses.controller); + const borrowedApproveTx = await ensureAllowance( + [this.borrowed_token.address], [userBorrowed], lending.constants.ALIASES.leverage_zap); + + return [...collateralApproveTx, ...borrowedApproveTx] + } + + private async _leverageCreateLoan( + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + range: number, + slippage: number, + estimateGas: boolean + ): Promise { + if (await this.userLoanExists()) throw Error("Loan already created"); + this._checkRange(range); + + const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); + const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); + const _debt = parseUnits(debt, this.borrowed_token.decimals); + const calldata = await _getCalldata1inch(this.addresses.borrowed_token, this.addresses.collateral_token, _debt + _userBorrowed, slippage); + const contract = lending.contracts[this.addresses.controller].contract; + const gas = await contract.create_loan_extended.estimateGas( + _userCollateral, + _debt, + range, + lending.constants.ALIASES.leverage_zap, + [0, parseUnits(this.id.split("-").slice(-1)[0], 0), _userBorrowed], + calldata, + { ...lending.constantOptions } + ); + if (estimateGas) return smartNumber(gas); + + await lending.updateFeeData(); + const gasLimit = _mulBy1_3(DIGas(gas)); + return (await contract.create_loan_extended( + _userCollateral, + _debt, + range, + lending.constants.ALIASES.leverage_zap, + [0, parseUnits(this.id.split("-").slice(-1)[0], 0), _userBorrowed], + calldata, + { ...lending.options, gasLimit } + )).hash + } + + private async leverageCreateLoanEstimateGas(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, slippage = 0.1): 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, slippage, true) as number; + } + + private async leverageCreateLoan(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, slippage = 0.1): Promise { + this._checkLeverageZap(); + await this.leverageCreateLoanApprove(userCollateral, userBorrowed); + return await this._leverageCreateLoan(userCollateral, userBorrowed, debt, range, slippage, false) as string; + } + // ---------------- LEVERAGE BORROW MORE ---------------- private async leverageBorrowMoreMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, address = ""): @@ -2227,6 +2351,54 @@ export class OneWayMarketTemplate { return await this._leverageHealth(userCollateral, userBorrowed, dDebt, -1, full, address); } + private async _leverageBorrowMore( + userCollateral: TAmount, + userBorrowed: TAmount, + debt: TAmount, + slippage: number, + estimateGas: boolean + ): Promise { + if (!(await this.userLoanExists())) throw Error("Loan does not exist"); + const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); + const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); + const _debt = parseUnits(debt, this.borrowed_token.decimals); + const calldata = await _getCalldata1inch(this.addresses.borrowed_token, this.addresses.collateral_token, _debt + _userBorrowed, slippage); + const contract = lending.contracts[this.addresses.controller].contract; + const gas = await contract.borrow_more_extended.estimateGas( + _userCollateral, + _debt, + lending.constants.ALIASES.leverage_zap, + [0, parseUnits(this.id.split("-").slice(-1)[0], 0), _userBorrowed], + calldata, + { ...lending.constantOptions } + ); + if (estimateGas) return smartNumber(gas); + + await lending.updateFeeData(); + const gasLimit = _mulBy1_3(DIGas(gas)); + + return (await contract.borrow_more_extended( + _userCollateral, + _debt, + lending.constants.ALIASES.leverage_zap, + [0, parseUnits(this.id.split("-").slice(-1)[0], 0), _userBorrowed], + calldata, + { ...lending.options, gasLimit } + )).hash + } + + private async leverageBorrowMoreEstimateGas(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, slippage = 0.1): Promise { + this._checkLeverageZap(); + if (!(await this.leverageCreateLoanIsApproved(userCollateral, userBorrowed))) throw Error("Approval is needed for gas estimation"); + return await this._leverageBorrowMore(userCollateral, userBorrowed, debt, slippage, true) as number; + } + + private async leverageBorrowMore(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, slippage = 0.1): Promise { + this._checkLeverageZap(); + await this.leverageCreateLoanApprove(userCollateral, userBorrowed); + return await this._leverageBorrowMore(userCollateral, userBorrowed, debt, slippage, false) as string; + } + // ---------------- LEVERAGE REPAY ---------------- private leverageRepayExpected = memoize( async (repayCollateral: TAmount, userBorrowed: TAmount): Promise => { @@ -2328,4 +2500,58 @@ export class OneWayMarketTemplate { return lending.formatUnits(_health); } + + private async leverageRepayIsApproved(repayCollateral: TAmount, userBorrowed: TAmount): Promise { + return await hasAllowance([this.borrowed_token.address], [userBorrowed], lending.signerAddress, lending.constants.ALIASES.leverage_zap); + } + + private async leverageRepayApproveEstimateGas (repayCollateral: TAmount, userBorrowed: TAmount): Promise { + return await ensureAllowanceEstimateGas([this.borrowed_token.address], [userBorrowed], lending.constants.ALIASES.leverage_zap); + } + + private async leverageRepayApprove(repayCollateral: TAmount, userBorrowed: TAmount): Promise { + return await ensureAllowance([this.borrowed_token.address], [userBorrowed], lending.constants.ALIASES.leverage_zap); + } + + private async _leverageRepay( + repayCollateral: TAmount, + userBorrowed: TAmount, + slippage: number, + estimateGas: boolean + ): Promise { + if (!(await this.userLoanExists())) throw Error("Loan does not exist"); + const _repayCollateral = parseUnits(repayCollateral, this.collateral_token.decimals); + const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); + const calldata = await _getCalldata1inch(this.addresses.collateral_token, this.addresses.borrowed_token, _repayCollateral, slippage); + const contract = lending.contracts[this.addresses.controller].contract; + const gas = await contract.repay_extended.estimateGas( + lending.constants.ALIASES.leverage_zap, + [0, parseUnits(this.id.split("-").slice(-1)[0], 0), _userBorrowed], + calldata, + { ...lending.constantOptions } + ); + if (estimateGas) return smartNumber(gas); + + await lending.updateFeeData(); + const gasLimit = _mulBy1_3(DIGas(gas)); + + return (await contract.repay_extended( + lending.constants.ALIASES.leverage_zap, + [0, parseUnits(this.id.split("-").slice(-1)[0], 0), _userBorrowed], + calldata, + { ...lending.options, gasLimit } + )).hash + } + + private async leverageRepayEstimateGas(repayCollateral: TAmount, userBorrowed: TAmount, slippage = 0.1): Promise { + this._checkLeverageZap(); + if (!(await this.leverageRepayIsApproved(repayCollateral, userBorrowed))) throw Error("Approval is needed for gas estimation"); + return await this._leverageRepay(repayCollateral, userBorrowed, slippage, true) as number; + } + + private async leverageRepay(repayCollateral: TAmount, userBorrowed: TAmount, slippage = 0.1): Promise { + this._checkLeverageZap(); + await this.leverageCreateLoanApprove(repayCollateral, userBorrowed); + return await this._leverageRepay(repayCollateral, userBorrowed, slippage, false) as string; + } } From 48769cfdb9eddcc6b93c3fee9ec60ccf0b19e2e4 Mon Sep 17 00:00:00 2001 From: macket Date: Thu, 11 Apr 2024 12:47:46 +0400 Subject: [PATCH 13/36] fix: leverageCreateLoanTotalCollateral debt arg --- src/markets/OneWayMarketTemplate.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index a9c080f..9cf40dd 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -155,7 +155,7 @@ export class OneWayMarketTemplate { Promise<{ maxBorrowable: string, maxTotalCollateral: string, maxLeverage: string, collateralAvgPrice: string }>, createLoanMaxRecvAllRanges: (userCollateral: TAmount, userBorrowed: TAmount) => Promise>, - createLoanTotalCollateral: (userCollateral: TAmount, userBorrowed: TAmount, range: number) => Promise, + createLoanTotalCollateral: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise, createLoanMaxRange: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise, createLoanBands: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number) => Promise<[number, number]>, createLoanBandsAllRanges: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise>, @@ -164,7 +164,7 @@ export class OneWayMarketTemplate { createLoanHealth: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, full?: boolean) => Promise, createLoanIsApproved: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, createLoanApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, - createLoan: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, slippage: number) => Promise, + createLoan: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, slippage?: number) => Promise, borrowMoreMaxRecv: (userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise<{ maxBorrowable: string, maxTotalCollateral: string, collateralAvgPrice: string }>, @@ -2084,8 +2084,8 @@ export class OneWayMarketTemplate { maxAge: 60 * 1000, // 1m }); - private async leverageCreateLoanTotalCollateral(userCollateral: TAmount, userBorrowed: TAmount, range: number): Promise { - return await this._leverageTotalCollateral(userCollateral, userBorrowed, range); + private async leverageCreateLoanTotalCollateral(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise { + return await this._leverageTotalCollateral(userCollateral, userBorrowed, debt); } private async leverageCreateLoanMaxRange(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise { From 185a99887b1e40ca5d968f203dd6d2fe7ec38ea0 Mon Sep 17 00:00:00 2001 From: macket Date: Thu, 11 Apr 2024 14:43:39 +0400 Subject: [PATCH 14/36] fix: leverageBorrowMoreMaxRecv --- src/markets/OneWayMarketTemplate.ts | 91 +++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 23 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 9cf40dd..86ad173 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -167,7 +167,14 @@ export class OneWayMarketTemplate { createLoan: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, slippage?: number) => Promise, borrowMoreMaxRecv: (userCollateral: TAmount, userBorrowed: TAmount, address?: string) => - Promise<{ maxBorrowable: string, maxTotalCollateral: string, collateralAvgPrice: string }>, + Promise<{ + currentCollateral: string, + currentDebt: string, + maxBorrowable: string, + maxAdditionalCollateral: string, + maxTotalCollateral: string, + collateralAvgPrice: string, + }>, borrowMoreTotalCollateral: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise, borrowMoreBands: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise<[number, number]>, borrowMorePrices: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise, @@ -1939,23 +1946,13 @@ export class OneWayMarketTemplate { return BN(1).div(BN(1).minus(k_effective_BN)).toString() } - private async _leverageMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, range: number, user?: string): + private async leverageCreateLoanMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, range: number): Promise<{ maxBorrowable: string, maxTotalCollateral: string, maxLeverage: string, collateralAvgPrice: string }> { // max_borrowable = userCollateral / (1 / (k_effective * max_p_base) - 1 / p_avg) this._checkLeverageZap(); if (range > 0) this._checkRange(range); - let _stateCollateral = BigInt(0); - let _stateDebt = BigInt(0); - if (user) { - const { _collateral, _borrowed, _debt, _N } = await this._userState(user); - if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); - _stateCollateral = _collateral; - _stateDebt = _debt; - if (range < 0) range = Number(lending.formatUnits(_N, 0)); - } - const _userCollateral = _stateCollateral + parseUnits(userCollateral, this.collateral_token.decimals); + const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); - const contract = lending.contracts[lending.constants.ALIASES.leverage_zap].contract; const oraclePriceBand = await this.oraclePriceBand(); let pAvgBN = BN(await this.calcTickPrice(oraclePriceBand)); // upper tick of oracle price band @@ -1964,12 +1961,12 @@ export class OneWayMarketTemplate { let _userEffectiveCollateral = BigInt(0); let _maxLeverageCollateral = BigInt(0); + const contract = lending.contracts[lending.constants.ALIASES.leverage_zap].contract; for (let i = 0; i < 5; i++) { maxBorrowablePrevBN = maxBorrowableBN; _userEffectiveCollateral = _userCollateral + fromBN(BN(userBorrowed).div(pAvgBN), this.collateral_token.decimals); - let _maxBorrowable = await contract.max_borrowable(this.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral, range, fromBN(pAvgBN)); + const _maxBorrowable = await contract.max_borrowable(this.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral, range, fromBN(pAvgBN)); if (_maxBorrowable === BigInt(0)) break; - _maxBorrowable -= _stateDebt; maxBorrowableBN = toBN(_maxBorrowable, this.borrowed_token.decimals); if (maxBorrowableBN.minus(maxBorrowablePrevBN).abs().div(maxBorrowablePrevBN).lt(0.0005)) { @@ -1994,11 +1991,6 @@ export class OneWayMarketTemplate { }; } - private async leverageCreateLoanMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, range: number): - Promise<{ maxBorrowable: string, maxTotalCollateral: string, maxLeverage: string, collateralAvgPrice: string }> { - return await this._leverageMaxRecv(userCollateral, userBorrowed, range); - } - private leverageCreateLoanMaxRecvAllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount): Promise> => { this._checkLeverageZap(); @@ -2318,11 +2310,64 @@ export class OneWayMarketTemplate { // ---------------- LEVERAGE BORROW MORE ---------------- private async leverageBorrowMoreMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, address = ""): - Promise<{ maxBorrowable: string, maxTotalCollateral: string, collateralAvgPrice: string }> { + Promise<{ + currentCollateral: string, + currentDebt: string, + maxBorrowable: string, + maxAdditionalCollateral: string, + maxTotalCollateral: string, + collateralAvgPrice: string, + }> { + // max_borrowable = userCollateral / (1 / (k_effective * max_p_base) - 1 / p_avg) + this._checkLeverageZap(); address = _getAddress(address); - const { maxBorrowable, maxTotalCollateral, collateralAvgPrice } = await this._leverageMaxRecv(userCollateral, userBorrowed, -1, address); + const { _collateral: _stateCollateral, _borrowed: _stateBorrowed, _debt: _stateDebt, _N } = await this._userState(address); + if (_stateBorrowed > BigInt(0)) throw Error(`User ${address} is already in liquidation mode`); + const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); + const controllerContract = lending.contracts[this.addresses.controller].contract; + const _borrowedFromStateCollateral = await controllerContract.max_borrowable(_stateCollateral, _N, _stateDebt, lending.constantOptions) - _stateDebt; + const _userBorrowed = _borrowedFromStateCollateral + parseUnits(userBorrowed, this.borrowed_token.decimals); + userBorrowed = formatUnits(_userBorrowed, this.borrowed_token.decimals); + + const oraclePriceBand = await this.oraclePriceBand(); + let pAvgBN = BN(await this.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 = lending.contracts[lending.constants.ALIASES.leverage_zap].contract; + for (let i = 0; i < 5; i++) { + maxBorrowablePrevBN = maxBorrowableBN; + _userEffectiveCollateral = _userCollateral + fromBN(BN(userBorrowed).div(pAvgBN), this.collateral_token.decimals); + const _maxBorrowable = await contract.max_borrowable(this.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral, _N, fromBN(pAvgBN)); + if (_maxBorrowable === BigInt(0)) break; + maxBorrowableBN = toBN(_maxBorrowable, this.borrowed_token.decimals); + + if (maxBorrowableBN.minus(maxBorrowablePrevBN).abs().div(maxBorrowablePrevBN).lt(0.0005)) { + maxBorrowableBN = maxBorrowablePrevBN; + break; + } + + // additionalCollateral = (userBorrowed / p) + leverageCollateral + const _maxAdditionalCollateral = BigInt(await _getQuote1inch(this.addresses.borrowed_token, this.addresses.collateral_token, _maxBorrowable + _userBorrowed)); + pAvgBN = maxBorrowableBN.plus(userBorrowed).div(toBN(_maxAdditionalCollateral, this.collateral_token.decimals)); + _maxLeverageCollateral = _maxAdditionalCollateral - fromBN(BN(userBorrowed).div(pAvgBN), this.collateral_token.decimals); + } + + if (maxBorrowableBN.eq(0)) _userEffectiveCollateral = BigInt(0); + const _maxAdditionalCollateral = _userEffectiveCollateral + _maxLeverageCollateral; + const _maxTotalCollateral = _stateCollateral + _userEffectiveCollateral + _maxLeverageCollateral + const _maxBorrowable = await controllerContract.max_borrowable(_maxTotalCollateral, _N, _stateDebt, lending.constantOptions) - _stateDebt; - return { maxBorrowable, maxTotalCollateral, collateralAvgPrice } + return { + currentCollateral: formatUnits(_stateCollateral, this.collateral_token.decimals), + currentDebt: formatUnits(_stateDebt, this.borrowed_token.decimals), + maxBorrowable: formatUnits(_maxBorrowable, this.borrowed_token.decimals), + maxAdditionalCollateral: formatUnits(_maxAdditionalCollateral, this.collateral_token.decimals), + maxTotalCollateral: formatUnits(_maxTotalCollateral, this.collateral_token.decimals), + collateralAvgPrice: pAvgBN.toString(), + }; } private async leverageBorrowMoreTotalCollateral(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address = ""): Promise { From 7c1ef9e91ffccb925ef8a9483fc6f5b34de455dc Mon Sep 17 00:00:00 2001 From: macket Date: Thu, 11 Apr 2024 18:04:39 +0400 Subject: [PATCH 15/36] fix: _leverageHealth --- src/markets/OneWayMarketTemplate.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 86ad173..e00826d 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -2197,24 +2197,23 @@ export class OneWayMarketTemplate { private async _leverageHealth( userCollateral: TAmount, userBorrowed: TAmount, - debt: TAmount, + dDebt: TAmount, range: number, full: boolean, user = lending.constants.ZERO_ADDRESS ): Promise { this._checkLeverageZap(); if (range > 0) this._checkRange(range); - const totalCollateral = await this._leverageTotalCollateral(userCollateral, userBorrowed, debt, user); + const totalCollateral = await this._leverageTotalCollateral(userCollateral, userBorrowed, dDebt, user); const _totalCollateral = parseUnits(totalCollateral, this.collateral_token.decimals); - if (user) { - const { _borrowed, _N } = await this._userState(user); - if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); - if (range < 0) range = Number(lending.formatUnits(_N, 0)); - } - const _debt = parseUnits(debt); + const { _borrowed, _collateral: _stateCollateral, _N } = await this._userState(user); + if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); + if (range < 0) range = Number(lending.formatUnits(_N, 0)); + const _dCollateral = _totalCollateral - _stateCollateral; + const _dDebt = parseUnits(dDebt, this.collateral_token.decimals); const contract = lending.contracts[this.addresses.controller].contract; - let _health = await contract.health_calculator(user, _totalCollateral, _debt, full, range, lending.constantOptions) as bigint; + let _health = await contract.health_calculator(user, _dCollateral, _dDebt, full, range, lending.constantOptions) as bigint; _health = _health * BigInt(100); return formatUnits(_health); From c42d275be8a8bd0bbe07826b69aa9d98f25d6381 Mon Sep 17 00:00:00 2001 From: macket Date: Thu, 11 Apr 2024 18:30:00 +0400 Subject: [PATCH 16/36] fix: _leverageTotalCollateral --- src/markets/OneWayMarketTemplate.ts | 59 ++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index e00826d..d459766 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -155,7 +155,8 @@ export class OneWayMarketTemplate { Promise<{ maxBorrowable: string, maxTotalCollateral: string, maxLeverage: string, collateralAvgPrice: string }>, createLoanMaxRecvAllRanges: (userCollateral: TAmount, userBorrowed: TAmount) => Promise>, - createLoanTotalCollateral: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise, + createLoanTotalCollateral: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => + Promise<{ stateCollateral: string, userCollateral: string, userCollateralFromBorrowed: string, leverageCollateral: string, totalCollateral: string }>, createLoanMaxRange: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise, createLoanBands: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number) => Promise<[number, number]>, createLoanBandsAllRanges: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise>, @@ -168,14 +169,15 @@ export class OneWayMarketTemplate { borrowMoreMaxRecv: (userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise<{ - currentCollateral: string, - currentDebt: string, + stateCollateral: string, + stateDebt: string, maxBorrowable: string, maxAdditionalCollateral: string, maxTotalCollateral: string, collateralAvgPrice: string, }>, - borrowMoreTotalCollateral: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise, + borrowMoreTotalCollateral: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => + Promise<{ stateCollateral: string, userCollateral: string, userCollateralFromBorrowed: string, leverageCollateral: string, totalCollateral: string }>, borrowMoreBands: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise<[number, number]>, borrowMorePrices: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise, borrowMoreHealth: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, full?: boolean, address?: string) => Promise, @@ -2056,28 +2058,41 @@ export class OneWayMarketTemplate { maxAge: 60 * 1000, // 1m }); - private _leverageTotalCollateral = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, user?: string): Promise => { + private _leverageTotalCollateral = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, user?: string): + Promise<{ _stateCollateral: bigint, _userCollateral: bigint, _userCollateralFromBorrowed: bigint, _leverageCollateral: bigint, _totalCollateral: bigint }> => { const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); const _debt = parseUnits(debt, this.borrowed_token.decimals); const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); // additionalCollateral = (userBorrowed / p) + leverageCollateral const _additionalCollateral = BigInt(await _getQuote1inch(this.addresses.borrowed_token, this.addresses.collateral_token, _debt + _userBorrowed)); + const _leverageCollateral = _debt * BigInt(10**18) / (_debt + _userBorrowed) * _additionalCollateral / BigInt(10**18); + const _userCollateralFromBorrowed = _additionalCollateral - _leverageCollateral; let _stateCollateral = BigInt(0); if (user) { const { _collateral, _borrowed } = await this._userState(user); if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); _stateCollateral = _collateral; } + const _totalCollateral = _stateCollateral + _userCollateral + _additionalCollateral; - return formatUnits(_stateCollateral + _userCollateral + _additionalCollateral, this.collateral_token.decimals); + return { _stateCollateral, _userCollateral, _userCollateralFromBorrowed, _leverageCollateral, _totalCollateral }; }, { promise: true, maxAge: 60 * 1000, // 1m }); - private async leverageCreateLoanTotalCollateral(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise { - return await this._leverageTotalCollateral(userCollateral, userBorrowed, debt); + private async leverageCreateLoanTotalCollateral(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): + Promise<{ stateCollateral: string, userCollateral: string, userCollateralFromBorrowed: string, leverageCollateral: string, totalCollateral: string }> { + const { _stateCollateral, _userCollateral, _userCollateralFromBorrowed, _leverageCollateral, _totalCollateral } = + await this._leverageTotalCollateral(userCollateral, userBorrowed, debt); + return { + stateCollateral: formatUnits(_stateCollateral, this.collateral_token.decimals), + userCollateral: formatUnits(_userCollateral, this.collateral_token.decimals), + userCollateralFromBorrowed: formatUnits(_userCollateralFromBorrowed, this.collateral_token.decimals), + leverageCollateral: formatUnits(_leverageCollateral, this.collateral_token.decimals), + totalCollateral: formatUnits(_totalCollateral, this.collateral_token.decimals), + } } private async leverageCreateLoanMaxRange(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise { @@ -2098,7 +2113,7 @@ export class OneWayMarketTemplate { _stateDebt = _debt; if (range < 0) range = Number(lending.formatUnits(_N, 0)); } - const _totalCollateral = parseUnits(await this._leverageTotalCollateral(userCollateral, userBorrowed, debt, user), this.collateral_token.decimals); + const { _totalCollateral } = await this._leverageTotalCollateral(userCollateral, userBorrowed, debt, user); const _debt = _stateDebt + parseUnits(debt, this.borrowed_token.decimals); return await lending.contracts[this.addresses.controller].contract.calculate_debt_n1(_totalCollateral, _debt, range, lending.constantOptions); }, @@ -2108,7 +2123,7 @@ export class OneWayMarketTemplate { }); private _leverageCalcN1AllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, maxN: number): Promise => { - const _totalCollateral = parseUnits(await this._leverageTotalCollateral(userCollateral, userBorrowed, debt), this.collateral_token.decimals); + const { _totalCollateral } = await this._leverageTotalCollateral(userCollateral, userBorrowed, debt); const _debt = parseUnits(debt, this.borrowed_token.decimals); const calls = []; for (let N = this.minBands; N <= maxN; N++) { @@ -2204,8 +2219,7 @@ export class OneWayMarketTemplate { ): Promise { this._checkLeverageZap(); if (range > 0) this._checkRange(range); - const totalCollateral = await this._leverageTotalCollateral(userCollateral, userBorrowed, dDebt, user); - const _totalCollateral = parseUnits(totalCollateral, this.collateral_token.decimals); + const { _totalCollateral } = await this._leverageTotalCollateral(userCollateral, userBorrowed, dDebt, user); const { _borrowed, _collateral: _stateCollateral, _N } = await this._userState(user); if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); if (range < 0) range = Number(lending.formatUnits(_N, 0)); @@ -2310,8 +2324,8 @@ export class OneWayMarketTemplate { private async leverageBorrowMoreMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise<{ - currentCollateral: string, - currentDebt: string, + stateCollateral: string, + stateDebt: string, maxBorrowable: string, maxAdditionalCollateral: string, maxTotalCollateral: string, @@ -2360,8 +2374,8 @@ export class OneWayMarketTemplate { const _maxBorrowable = await controllerContract.max_borrowable(_maxTotalCollateral, _N, _stateDebt, lending.constantOptions) - _stateDebt; return { - currentCollateral: formatUnits(_stateCollateral, this.collateral_token.decimals), - currentDebt: formatUnits(_stateDebt, this.borrowed_token.decimals), + stateCollateral: formatUnits(_stateCollateral, this.collateral_token.decimals), + stateDebt: formatUnits(_stateDebt, this.borrowed_token.decimals), maxBorrowable: formatUnits(_maxBorrowable, this.borrowed_token.decimals), maxAdditionalCollateral: formatUnits(_maxAdditionalCollateral, this.collateral_token.decimals), maxTotalCollateral: formatUnits(_maxTotalCollateral, this.collateral_token.decimals), @@ -2369,9 +2383,18 @@ export class OneWayMarketTemplate { }; } - private async leverageBorrowMoreTotalCollateral(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address = ""): Promise { + private async leverageBorrowMoreTotalCollateral(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address = ""): + Promise<{ stateCollateral: string, userCollateral: string, userCollateralFromBorrowed: string, leverageCollateral: string, totalCollateral: string }> { address = _getAddress(address); - return await this._leverageTotalCollateral(userCollateral, userBorrowed, dDebt, address); + const { _stateCollateral, _userCollateral, _userCollateralFromBorrowed, _leverageCollateral, _totalCollateral } = + await this._leverageTotalCollateral(userCollateral, userBorrowed, dDebt, address); + return { + stateCollateral: formatUnits(_stateCollateral, this.collateral_token.decimals), + userCollateral: formatUnits(_userCollateral, this.collateral_token.decimals), + userCollateralFromBorrowed: formatUnits(_userCollateralFromBorrowed, this.collateral_token.decimals), + leverageCollateral: formatUnits(_leverageCollateral, this.collateral_token.decimals), + totalCollateral: formatUnits(_totalCollateral, this.collateral_token.decimals), + } } private async leverageBorrowMoreBands(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address = ""): Promise<[number, number]> { From b783893494fcb7dfd061c2dc596207b714c5583d Mon Sep 17 00:00:00 2001 From: macket Date: Thu, 11 Apr 2024 20:58:46 +0400 Subject: [PATCH 17/36] feat: userCollateral in leverageRepay --- src/markets/OneWayMarketTemplate.ts | 137 ++++++++++++++++------------ 1 file changed, 81 insertions(+), 56 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index d459766..1930f73 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -185,15 +185,16 @@ export class OneWayMarketTemplate { borrowMoreApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, borrowMore: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, slippage?: number) => Promise, - repayExpected: (repayCollateral: TAmount, userBorrowed: TAmount) => Promise, - repayIsFull: (repayCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, - repayIsAvailable: (repayCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, - repayBands: (repayCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise<[number, number]>, - repayPrices: (repayCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, - repayHealth: (repayCollateral: TAmount, userBorrowed: TAmount, full?: boolean, address?: string) => Promise, - repayIsApproved: (repayCollateral: TAmount, userBorrowed: TAmount) => Promise, - repayApprove: (repayCollateral: TAmount, userBorrowed: TAmount) => Promise, - repay: (repayCollateral: TAmount, userBorrowed: TAmount, slippage?: number) => Promise, + repayTotalBorrowed: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount) => + Promise<{ borrowedFromStateCollateral: string, userBorrowedFromCollateral: string, userBorrowed: string, totalBorrowed: string }>, + repayIsFull: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, + repayIsAvailable: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, + repayBands: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise<[number, number]>, + repayPrices: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, + repayHealth: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, full?: boolean, address?: string) => Promise, + repayIsApproved: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + repayApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + repay: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, slippage?: number) => Promise, estimateGas: { createLoanApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, @@ -202,8 +203,8 @@ export class OneWayMarketTemplate { borrowMoreApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, borrowMore: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, slippage?: number) => Promise, - repayApprove: (repayCollateral: TAmount, userBorrowed: TAmount) => Promise, - repay: (repayCollateral: TAmount, userBorrowed: TAmount, slippage?: number) => Promise, + repayApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + repay: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, slippage?: number) => Promise, } }; @@ -324,7 +325,7 @@ export class OneWayMarketTemplate { borrowMoreApprove: this.leverageCreateLoanApprove.bind(this), borrowMore: this.leverageBorrowMore.bind(this), - repayExpected: this.leverageRepayExpected.bind(this), + repayTotalBorrowed: this.leverageRepayTotalBorrowed.bind(this), repayIsFull: this.leverageRepayIsFull.bind(this), repayIsAvailable: this.leverageRepayIsAvailable.bind(this), repayBands: this.leverageRepayBands.bind(this), @@ -2468,29 +2469,38 @@ export class OneWayMarketTemplate { // ---------------- LEVERAGE REPAY ---------------- - private leverageRepayExpected = memoize( async (repayCollateral: TAmount, userBorrowed: TAmount): Promise => { + private leverageRepayTotalBorrowed = memoize( async (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount): + Promise<{ borrowedFromStateCollateral: string, userBorrowedFromCollateral: string, userBorrowed: string, totalBorrowed: string }> => { this._checkLeverageZap(); - const _repayCollateral = parseUnits(repayCollateral, this.collateral_token.decimals); - let _borrowedExpected = BigInt(await _getQuote1inch(this.addresses.collateral_token, this.addresses.borrowed_token, _repayCollateral)); - _borrowedExpected = _borrowedExpected + parseUnits(userBorrowed, this.borrowed_token.decimals); + const _stateCollateral = parseUnits(stateCollateral, this.collateral_token.decimals); + const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); + const _borrowedExpected = BigInt(await _getQuote1inch(this.addresses.collateral_token, this.addresses.borrowed_token, _stateCollateral + _userCollateral)); + const _borrowedFromStateCollateral = _stateCollateral * BigInt(10**18) / (_stateCollateral + _userCollateral) * _borrowedExpected / BigInt(10**18); + const _userBorrowedFromCollateral = _borrowedExpected - _borrowedFromStateCollateral; + const _totalBorrowed = _borrowedExpected + parseUnits(userBorrowed, this.borrowed_token.decimals); - return lending.formatUnits(_borrowedExpected, this.borrowed_token.decimals) + return { + borrowedFromStateCollateral: formatUnits(_borrowedFromStateCollateral, this.borrowed_token.decimals), + userBorrowedFromCollateral: formatUnits(_userBorrowedFromCollateral, this.borrowed_token.decimals), + userBorrowed: String(userBorrowed), + totalBorrowed: formatUnits(_totalBorrowed, this.borrowed_token.decimals), + } }, { promise: true, maxAge: 5 * 60 * 1000, // 5m }); - private async leverageRepayIsFull(repayCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise { + private async leverageRepayIsFull(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise { this._checkLeverageZap(); address = _getAddress(address); const { borrowed: stateBorrowed, debt } = await this.userState(address); - const repayExpected = await this.leverageRepayExpected(repayCollateral, userBorrowed); + const { totalBorrowed } = await this.leverageRepayTotalBorrowed(stateCollateral, userCollateral, userBorrowed); - return BN(stateBorrowed).plus(repayExpected).gt(debt); + return BN(stateBorrowed).plus(totalBorrowed).gt(debt); } - private async leverageRepayIsAvailable(repayCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise { + private async leverageRepayIsAvailable(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise { // 0. const { collateral, stablecoin, debt } = await this.userState(address); // 1. maxCollateral for deleverage is collateral from line above. // 2. If user is underwater (stablecoin > 0), only full repayment is available: @@ -2501,28 +2511,28 @@ export class OneWayMarketTemplate { // Loan does not exist if (BN(debt).eq(0)) return false; // Can't spend more than user has - if (BN(repayCollateral).gt(collateral)) return false; + 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(repayCollateral, userBorrowed, address); + if (BN(borrowed).gt(0)) return await this.leverageRepayIsFull(stateCollateral, userCollateral, userBorrowed, address); return true; } - private _leverageRepayBands = memoize( async (repayCollateral: TAmount, userBorrowed: TAmount, address: string): Promise<[bigint, bigint]> => { + private _leverageRepayBands = memoize( async (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address: string): Promise<[bigint, bigint]> => { address = _getAddress(address); - if (!(await this.leverageRepayIsAvailable(repayCollateral, userBorrowed, address))) return [parseUnits(0, 0), parseUnits(0, 0)]; + if (!(await this.leverageRepayIsAvailable(stateCollateral, userCollateral, userBorrowed, address))) return [parseUnits(0, 0), parseUnits(0, 0)]; - const _repayCollateral = parseUnits(repayCollateral, this.collateral_token.decimals); + 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 < _repayCollateral) throw Error(`Can't use more collateral than user's position has (${_repayCollateral}) > ${_stateCollateral})`); + 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 repayExpected = await this.leverageRepayExpected(repayCollateral, userBorrowed); - const _repayExpected = parseUnits(repayExpected, this.borrowed_token.decimals); + const { totalBorrowed } = await this.leverageRepayTotalBorrowed(stateCollateral, userCollateral, userBorrowed); + const _repayExpected = parseUnits(totalBorrowed, this.borrowed_token.decimals); try { - _n1 = await lending.contracts[this.addresses.controller].contract.calculate_debt_n1(_stateCollateral - _repayCollateral, _stateDebt - _repayExpected, _N); + _n1 = await lending.contracts[this.addresses.controller].contract.calculate_debt_n1(_stateCollateral - _stateRepayCollateral, _stateDebt - _repayExpected, _N); _n2 = _n1 + (_N - BigInt(1)); } catch (e) { console.log("Full repayment"); @@ -2535,30 +2545,30 @@ export class OneWayMarketTemplate { maxAge: 5 * 60 * 1000, // 5m }); - private async leverageRepayBands(repayCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise<[number, number]> { + private async leverageRepayBands(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise<[number, number]> { this._checkLeverageZap(); - const [_n2, _n1] = await this._leverageRepayBands(repayCollateral, userBorrowed, address); + const [_n2, _n1] = await this._leverageRepayBands(stateCollateral, userCollateral, userBorrowed, address); return [Number(_n2), Number(_n1)]; } - private async leverageRepayPrices(repayCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise { + private async leverageRepayPrices(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise { this._checkLeverageZap(); - const [_n2, _n1] = await this._leverageRepayBands(repayCollateral, userBorrowed, address); + const [_n2, _n1] = await this._leverageRepayBands(stateCollateral, userCollateral, userBorrowed, address); return await this._getPrices(_n2, _n1); } - private async leverageRepayHealth(repayCollateral: TAmount, userBorrowed: TAmount, full = true, address = ""): Promise { + private async leverageRepayHealth(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, full = true, address = ""): Promise { this._checkLeverageZap(); address = _getAddress(address); const { _borrowed: _stateBorrowed, _debt, _N } = await this._userState(address); if (_stateBorrowed > BigInt(0)) return "0.0"; - if (!(await this.leverageRepayIsAvailable(repayCollateral, userBorrowed, address))) return "0.0"; + if (!(await this.leverageRepayIsAvailable(stateCollateral, userCollateral, userBorrowed, address))) return "0.0"; - const repayExpected = await this.leverageRepayExpected(repayCollateral, userBorrowed); - const _dCollateral = parseUnits(repayCollateral, this.collateral_token.decimals) * BigInt(-1); - const _dDebt = parseUnits(repayExpected, this.borrowed_token.decimals) * BigInt(-1); + const { totalBorrowed } = await this.leverageRepayTotalBorrowed(stateCollateral, userCollateral, userBorrowed); + const _dCollateral = parseUnits(stateCollateral, this.collateral_token.decimals) * BigInt(-1); + const _dDebt = parseUnits(totalBorrowed, this.borrowed_token.decimals) * BigInt(-1); if (_debt + _dDebt <= BigInt(0)) return "0.0"; const contract = lending.contracts[this.addresses.controller].contract; @@ -2568,32 +2578,47 @@ export class OneWayMarketTemplate { return lending.formatUnits(_health); } - private async leverageRepayIsApproved(repayCollateral: TAmount, userBorrowed: TAmount): Promise { - return await hasAllowance([this.borrowed_token.address], [userBorrowed], lending.signerAddress, lending.constants.ALIASES.leverage_zap); + private async leverageRepayIsApproved(userCollateral: TAmount, userBorrowed: TAmount): Promise { + return await hasAllowance( + [this.collateral_token.address, this.borrowed_token.address], + [userCollateral, userBorrowed], + lending.signerAddress, + lending.constants.ALIASES.leverage_zap + ); } - private async leverageRepayApproveEstimateGas (repayCollateral: TAmount, userBorrowed: TAmount): Promise { - return await ensureAllowanceEstimateGas([this.borrowed_token.address], [userBorrowed], lending.constants.ALIASES.leverage_zap); + private async leverageRepayApproveEstimateGas (userCollateral: TAmount, userBorrowed: TAmount): Promise { + return await ensureAllowanceEstimateGas( + [this.collateral_token.address, this.borrowed_token.address], + [userCollateral, userBorrowed], + lending.constants.ALIASES.leverage_zap + ); } - private async leverageRepayApprove(repayCollateral: TAmount, userBorrowed: TAmount): Promise { - return await ensureAllowance([this.borrowed_token.address], [userBorrowed], lending.constants.ALIASES.leverage_zap); + private async leverageRepayApprove(userCollateral: TAmount, userBorrowed: TAmount): Promise { + return await ensureAllowance( + [this.collateral_token.address, this.borrowed_token.address], + [userCollateral, userBorrowed], + lending.constants.ALIASES.leverage_zap + ); } private async _leverageRepay( - repayCollateral: TAmount, + stateCollateral: TAmount, + userCollateral: TAmount, userBorrowed: TAmount, slippage: number, estimateGas: boolean ): Promise { if (!(await this.userLoanExists())) throw Error("Loan does not exist"); - const _repayCollateral = parseUnits(repayCollateral, this.collateral_token.decimals); + const _stateCollateral = parseUnits(stateCollateral, this.collateral_token.decimals); + const _userCollateral = parseUnits(userCollateral, this.borrowed_token.decimals); const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); - const calldata = await _getCalldata1inch(this.addresses.collateral_token, this.addresses.borrowed_token, _repayCollateral, slippage); + const calldata = await _getCalldata1inch(this.addresses.collateral_token, this.addresses.borrowed_token, _stateCollateral + _userCollateral, slippage); const contract = lending.contracts[this.addresses.controller].contract; const gas = await contract.repay_extended.estimateGas( lending.constants.ALIASES.leverage_zap, - [0, parseUnits(this.id.split("-").slice(-1)[0], 0), _userBorrowed], + [0, parseUnits(this.id.split("-").slice(-1)[0], 0), _userCollateral, _userBorrowed], calldata, { ...lending.constantOptions } ); @@ -2604,21 +2629,21 @@ export class OneWayMarketTemplate { return (await contract.repay_extended( lending.constants.ALIASES.leverage_zap, - [0, parseUnits(this.id.split("-").slice(-1)[0], 0), _userBorrowed], + [0, parseUnits(this.id.split("-").slice(-1)[0], 0), _userCollateral, _userBorrowed], calldata, { ...lending.options, gasLimit } )).hash } - private async leverageRepayEstimateGas(repayCollateral: TAmount, userBorrowed: TAmount, slippage = 0.1): Promise { + private async leverageRepayEstimateGas(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, slippage = 0.1): Promise { this._checkLeverageZap(); - if (!(await this.leverageRepayIsApproved(repayCollateral, userBorrowed))) throw Error("Approval is needed for gas estimation"); - return await this._leverageRepay(repayCollateral, userBorrowed, slippage, true) as number; + if (!(await this.leverageRepayIsApproved(userCollateral, userBorrowed))) throw Error("Approval is needed for gas estimation"); + return await this._leverageRepay(stateCollateral, userCollateral, userBorrowed, slippage, true) as number; } - private async leverageRepay(repayCollateral: TAmount, userBorrowed: TAmount, slippage = 0.1): Promise { + private async leverageRepay(stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, slippage = 0.1): Promise { this._checkLeverageZap(); - await this.leverageCreateLoanApprove(repayCollateral, userBorrowed); - return await this._leverageRepay(repayCollateral, userBorrowed, slippage, false) as string; + await this.leverageRepayApprove(userCollateral, userBorrowed); + return await this._leverageRepay(stateCollateral, userCollateral, userBorrowed, slippage, false) as string; } } From 0a10fc04beefcc93092e78de32e80649ce601b77 Mon Sep 17 00:00:00 2001 From: macket Date: Thu, 11 Apr 2024 21:16:01 +0400 Subject: [PATCH 18/36] test: leverage basic test --- test/leverage.test.ts | 154 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 test/leverage.test.ts diff --git a/test/leverage.test.ts b/test/leverage.test.ts new file mode 100644 index 0000000..8b215af --- /dev/null +++ b/test/leverage.test.ts @@ -0,0 +1,154 @@ +import { assert } from "chai"; +import lending from "../src/index.js"; +import { API_KEY_1INCH } from "./rpcUrls.test.js"; +import { getOneWayMarket, OneWayMarketTemplate } from "../src/markets/index.js"; + + +const ONE_WAY_MARKETS = ['one-way-market-0']; + +const generalTest = (id: string) => { + describe(`${id} leverage test`, function () { + let oneWayMarket: OneWayMarketTemplate; + + before(async function () { + oneWayMarket = getOneWayMarket(id); + if (Number(await oneWayMarket.vault.totalLiquidity()) === 0) { + const maxDeposit = Number(await oneWayMarket.vault.maxDeposit()) * 0.7; + await oneWayMarket.vault.deposit(maxDeposit); + } + }); + + it('Leverage createLoan', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isFalse(loanExists); + assert.isAbove(Number(initialBalances.collateral), 0); + assert.isAbove(Number(initialBalances.borrowed), 0); + + const collateralAmount = 0.5; + const borrowedAmount = 1000; + const N = 10; + const maxRecv = await oneWayMarket.leverage.createLoanMaxRecv(collateralAmount, borrowedAmount, N); + const debtAmount = (Number(maxRecv.maxBorrowable) / 2).toFixed(18); + const createLoanBands = await oneWayMarket.leverage.createLoanBands(collateralAmount, borrowedAmount, debtAmount, N); + const createLoanPrices = await oneWayMarket.leverage.createLoanPrices(collateralAmount, borrowedAmount, debtAmount, N); + const createLoanFullHealth = await oneWayMarket.leverage.createLoanHealth(collateralAmount, borrowedAmount, debtAmount, N); + const createLoanHealth = await oneWayMarket.leverage.createLoanHealth(collateralAmount, borrowedAmount, debtAmount, N, false); + const { totalCollateral } = await oneWayMarket.leverage.createLoanTotalCollateral(collateralAmount, borrowedAmount, debtAmount); + + await oneWayMarket.leverage.createLoan(collateralAmount, borrowedAmount, debtAmount, N, 1); + + const balances = await oneWayMarket.wallet.balances(); + const state = await oneWayMarket.userState(); + const userBands = await oneWayMarket.userBands(); + const userPrices = await oneWayMarket.userPrices(); + const fullHealth = await oneWayMarket.userHealth(); + const health = await oneWayMarket.userHealth(false); + + assert.equal(Number(createLoanBands[0]), Number(userBands[0]), 'band 0'); + assert.equal(Number(createLoanBands[1]), Number(userBands[1]), 'band 1'); + assert.approximately(Number(createLoanPrices[0]), Number(userPrices[0]), 1e-2, 'price 0'); + assert.approximately(Number(createLoanPrices[1]), Number(userPrices[1]), 1e-2, 'price 1'); + assert.approximately(Number(createLoanFullHealth), Number(fullHealth), 0.3, 'full health'); + assert.approximately(Number(createLoanHealth), Number(health), 0.1, 'health'); + assert.equal(Number(balances.collateral), Number(initialBalances.collateral) - Number(collateralAmount), 'wallet collateral'); + assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet stablecoin'); + assert.isAtMost(Math.abs(Number(state.collateral) - Number(totalCollateral)) / Number(totalCollateral), 1e-3, 'state collateral'); + assert.equal(Number(state.debt), Number(debtAmount), 'state debt'); + }); + + it('Leverage borrowMore', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const initialState = await oneWayMarket.userState(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isTrue(loanExists); + assert.isAbove(Number(initialBalances.collateral), 0); + assert.isAbove(Number(initialBalances.borrowed), 0); + + const collateralAmount = 0.5; + const borrowedAmount = 1000; + const maxRecv = await oneWayMarket.leverage.borrowMoreMaxRecv(collateralAmount, borrowedAmount); + const debtAmount = (Number(maxRecv.maxBorrowable) / 2).toFixed(18); + const borrowMoreBands = await oneWayMarket.leverage.borrowMoreBands(collateralAmount, borrowedAmount, debtAmount); + const borrowMorePrices = await oneWayMarket.leverage.borrowMorePrices(collateralAmount, borrowedAmount, debtAmount); + const borrowMoreFullHealth = await oneWayMarket.leverage.borrowMoreHealth(collateralAmount, borrowedAmount, debtAmount); + const borrowMoreHealth = await oneWayMarket.leverage.borrowMoreHealth(collateralAmount, borrowedAmount, debtAmount, false); + const { totalCollateral } = await oneWayMarket.leverage.borrowMoreTotalCollateral(collateralAmount, borrowedAmount, debtAmount); + + await oneWayMarket.leverage.borrowMore(collateralAmount, borrowedAmount, debtAmount,1); + + const balances = await oneWayMarket.wallet.balances(); + const state = await oneWayMarket.userState(); + const userBands = await oneWayMarket.userBands(); + const userPrices = await oneWayMarket.userPrices(); + const fullHealth = await oneWayMarket.userHealth(); + const health = await oneWayMarket.userHealth(false); + + assert.equal(Number(borrowMoreBands[0]), Number(userBands[0]), 'band 0'); + assert.equal(Number(borrowMoreBands[1]), Number(userBands[1]), 'band 1'); + assert.approximately(Number(borrowMorePrices[0]), Number(userPrices[0]), 1e-2, 'price 0'); + assert.approximately(Number(borrowMorePrices[1]), Number(userPrices[1]), 1e-2, 'price 1'); + assert.approximately(Number(borrowMoreFullHealth), Number(fullHealth), 0.2, 'full health'); + assert.approximately(Number(borrowMoreHealth), Number(health), 0.1, 'health'); + assert.equal(Number(balances.collateral), Number(initialBalances.collateral) - Number(collateralAmount), 'wallet collateral'); + assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet stablecoin'); + assert.isAtMost(Math.abs(Number(state.collateral) - Number(totalCollateral)) / Number(totalCollateral), 1e-3, 'state collateral'); + assert.approximately(Number(state.debt), Number(initialState.debt) + Number(debtAmount), 1e-3, 'state debt'); + }); + + it('Leverage repay', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const initialState = await oneWayMarket.userState(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isTrue(loanExists); + assert.isAbove(Number(initialBalances.collateral), 0); + assert.isAbove(Number(initialBalances.borrowed), 0); + + const stateCollateralAmount = 0.3; + const collateralAmount = 0.2; + const borrowedAmount = 500; + const repayBands = await oneWayMarket.leverage.repayBands(stateCollateralAmount, collateralAmount, borrowedAmount); + const repayPrices = await oneWayMarket.leverage.repayPrices(stateCollateralAmount, collateralAmount, borrowedAmount); + const repayFullHealth = await oneWayMarket.leverage.repayHealth(stateCollateralAmount, collateralAmount, borrowedAmount); + const repayHealth = await oneWayMarket.leverage.repayHealth(stateCollateralAmount, collateralAmount, borrowedAmount, false); + const { totalBorrowed } = await oneWayMarket.leverage.repayTotalBorrowed(stateCollateralAmount, collateralAmount, borrowedAmount); + + await oneWayMarket.leverage.repay(stateCollateralAmount, collateralAmount, borrowedAmount, 1); + + const balances = await oneWayMarket.wallet.balances(); + const state = await oneWayMarket.userState(); + const userBands = await oneWayMarket.userBands(); + const userPrices = await oneWayMarket.userPrices(); + const fullHealth = await oneWayMarket.userHealth(); + const health = await oneWayMarket.userHealth(false); + + assert.equal(Number(repayBands[0]), Number(userBands[0]), 'band 0'); + assert.equal(Number(repayBands[1]), Number(userBands[1]), 'band 1'); + assert.approximately(Number(repayPrices[0]), Number(userPrices[0]), 1e-2, 'price 0'); + assert.approximately(Number(repayPrices[1]), Number(userPrices[1]), 1e-2, 'price 1'); + assert.approximately(Number(repayFullHealth), Number(fullHealth), 0.1, 'full health'); + assert.approximately(Number(repayHealth), Number(health), 0.1, 'health'); + assert.equal(Number(balances.collateral), Number(initialBalances.collateral) - Number(collateralAmount), 'wallet collateral'); + assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet stablecoin'); + assert.equal(Number(state.collateral), Number(initialState.collateral) - Number(stateCollateralAmount), 'state collateral'); + const debtDiff = Number(initialState.debt) - Number(state.debt); + assert.isAtMost(Math.abs(debtDiff - Number(totalBorrowed)) / Number(totalBorrowed), 2e-3, 'state debt'); + }); + }) +} + +describe('Leverage test', async function () { + this.timeout(120000); + + before(async function () { + await lending.init('JsonRpc', {},{ gasPrice: 0 }, API_KEY_1INCH); + await lending.oneWayfactory.fetchMarkets(); + }); + + for (const oneWayMarketId of ONE_WAY_MARKETS) { + generalTest(oneWayMarketId); + } +}) From 4216d0f56d196b3b5cfa7f3374f16692fd2774b7 Mon Sep 17 00:00:00 2001 From: macket Date: Thu, 11 Apr 2024 21:17:25 +0400 Subject: [PATCH 19/36] chore: test one_way_factory and leverage_zap addresses --- src/constants/aliases.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/constants/aliases.ts b/src/constants/aliases.ts index be420f6..eb0f373 100644 --- a/src/constants/aliases.ts +++ b/src/constants/aliases.ts @@ -36,10 +36,11 @@ export const ALIASES_AVALANCHE = lowerCaseValues({ export const ALIASES_ARBITRUM = lowerCaseValues({ "crv": "0x11cDb42B0EB46D95f990BeDD4695A6e3fA034978", - "one_way_factory": "0xcaEC110C784c9DF37240a8Ce096D352A75922DeA", + // "one_way_factory": "0xcaEC110C784c9DF37240a8Ce096D352A75922DeA", // REAL + "one_way_factory": "0x19010d0f5D5a88aC609B568c91057679eed643d3", // TEST "gauge_controller": "0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB", "gauge_factory": "0xabC000d88f23Bb45525E447528DBF656A9D55bf5", - "leverage_zap": "0x0000000000000000000000000000000000000000", + "leverage_zap": "0xE1834AF57923059B4306B468013262D73F344D4E", // TEST }); export const ALIASES_OPTIMISM = lowerCaseValues({ From 0652253e364cf1770ef75957c5e562fa936637c7 Mon Sep 17 00:00:00 2001 From: macket Date: Fri, 12 Apr 2024 14:05:21 +0400 Subject: [PATCH 20/36] refactor: leverageCreateLoanMaxRecv --- src/markets/OneWayMarketTemplate.ts | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 1930f73..b0eaf5a 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -152,7 +152,15 @@ export class OneWayMarketTemplate { maxLeverage: (N: number) => Promise, createLoanMaxRecv: (userCollateral: TAmount, userBorrowed: TAmount, range: number) => - Promise<{ maxBorrowable: string, maxTotalCollateral: string, maxLeverage: string, collateralAvgPrice: string }>, + Promise<{ + maxDebt: string, + collateralFromMaxDebt: string, + userCollateral: string, + collateralFromUserBorrowed: string, + maxTotalCollateral: string, + maxLeverage: string, + avgPrice: string, + }>, createLoanMaxRecvAllRanges: (userCollateral: TAmount, userBorrowed: TAmount) => Promise>, createLoanTotalCollateral: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => @@ -1950,7 +1958,15 @@ export class OneWayMarketTemplate { } private async leverageCreateLoanMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, range: number): - Promise<{ maxBorrowable: string, maxTotalCollateral: string, maxLeverage: string, collateralAvgPrice: string }> { + Promise<{ + maxDebt: string, + collateralFromMaxDebt: string, + userCollateral: string, + collateralFromUserBorrowed: string, + maxTotalCollateral: string, + maxLeverage: string, + avgPrice: string, + }> { // max_borrowable = userCollateral / (1 / (k_effective * max_p_base) - 1 / p_avg) this._checkLeverageZap(); if (range > 0) this._checkRange(range); @@ -1987,10 +2003,13 @@ export class OneWayMarketTemplate { const maxLeverageCollateralBN = toBN(_maxLeverageCollateral, this.collateral_token.decimals); return { - maxBorrowable: maxBorrowableBN.toString(), - maxTotalCollateral: maxLeverageCollateralBN.plus(userEffectiveCollateralBN).toString(), + maxDebt: maxBorrowableBN.toString(), + collateralFromMaxDebt: formatNumber(maxLeverageCollateralBN.toString(), this.collateral_token.decimals), + userCollateral: String(userCollateral), + collateralFromUserBorrowed: formatNumber(BN(userBorrowed).div(pAvgBN).toString(), this.collateral_token.decimals), + maxTotalCollateral: formatNumber(maxLeverageCollateralBN.plus(userEffectiveCollateralBN).toString(), this.collateral_token.decimals), maxLeverage: maxLeverageCollateralBN.plus(userEffectiveCollateralBN).div(userEffectiveCollateralBN).toString(), - collateralAvgPrice: pAvgBN.toString(), + avgPrice: pAvgBN.toString(), }; } From dfb129e272a89c52c28c56528ac6f72e69912f55 Mon Sep 17 00:00:00 2001 From: macket Date: Fri, 12 Apr 2024 16:46:10 +0400 Subject: [PATCH 21/36] refactor: leverageCreateLoanMaxRecvAllRanges --- src/markets/OneWayMarketTemplate.ts | 58 +++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index b0eaf5a..42b459d 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -154,15 +154,23 @@ export class OneWayMarketTemplate { createLoanMaxRecv: (userCollateral: TAmount, userBorrowed: TAmount, range: number) => Promise<{ maxDebt: string, - collateralFromMaxDebt: string, + maxTotalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, - maxTotalCollateral: string, + collateralFromMaxDebt: string, maxLeverage: string, avgPrice: string, }>, createLoanMaxRecvAllRanges: (userCollateral: TAmount, userBorrowed: TAmount) => - Promise>, + Promise>, createLoanTotalCollateral: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise<{ stateCollateral: string, userCollateral: string, userCollateralFromBorrowed: string, leverageCollateral: string, totalCollateral: string }>, createLoanMaxRange: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise, @@ -1960,10 +1968,10 @@ export class OneWayMarketTemplate { private async leverageCreateLoanMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, range: number): Promise<{ maxDebt: string, - collateralFromMaxDebt: string, + maxTotalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, - maxTotalCollateral: string, + collateralFromMaxDebt: string, maxLeverage: string, avgPrice: string, }> { @@ -2003,18 +2011,26 @@ export class OneWayMarketTemplate { const maxLeverageCollateralBN = toBN(_maxLeverageCollateral, this.collateral_token.decimals); return { - maxDebt: maxBorrowableBN.toString(), - collateralFromMaxDebt: formatNumber(maxLeverageCollateralBN.toString(), this.collateral_token.decimals), - userCollateral: String(userCollateral), - collateralFromUserBorrowed: formatNumber(BN(userBorrowed).div(pAvgBN).toString(), this.collateral_token.decimals), + maxDebt: formatNumber(maxBorrowableBN.toString(), this.borrowed_token.decimals), maxTotalCollateral: formatNumber(maxLeverageCollateralBN.plus(userEffectiveCollateralBN).toString(), this.collateral_token.decimals), + userCollateral: formatNumber(userCollateral, this.collateral_token.decimals), + collateralFromUserBorrowed: formatNumber(BN(userBorrowed).div(pAvgBN).toString(), this.collateral_token.decimals), + collateralFromMaxDebt: formatNumber(maxLeverageCollateralBN.toString(), this.collateral_token.decimals), maxLeverage: maxLeverageCollateralBN.plus(userEffectiveCollateralBN).div(userEffectiveCollateralBN).toString(), avgPrice: pAvgBN.toString(), }; } private leverageCreateLoanMaxRecvAllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount): - Promise> => { + Promise> => { this._checkLeverageZap(); const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); const contract = lending.contracts[lending.constants.ALIASES.leverage_zap].multicallContract; @@ -2029,7 +2045,6 @@ export class OneWayMarketTemplate { 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; @@ -2060,14 +2075,25 @@ export class OneWayMarketTemplate { const userEffectiveCollateralBN = BN(userCollateral).plus(BN(userBorrowed).div(pAvgBN as BigNumber)); - const res: IDict<{ maxBorrowable: string, maxTotalCollateral: string, maxLeverage: string, collateralAvgPrice: string }> = {}; + 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] = { - maxBorrowable: maxBorrowableBN[j].toString(), - maxTotalCollateral: maxLeverageCollateralBN[j].plus(userEffectiveCollateralBN).toString(), + 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(), - collateralAvgPrice: (pAvgBN as BigNumber).toString(), + avgPrice: (pAvgBN as BigNumber).toString(), }; } @@ -2118,7 +2144,7 @@ export class OneWayMarketTemplate { private async leverageCreateLoanMaxRange(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): Promise { const maxRecv = await this.leverageCreateLoanMaxRecvAllRanges(userCollateral, userBorrowed); for (let N = this.minBands; N <= this.maxBands; N++) { - if (BN(debt).gt(maxRecv[N].maxBorrowable)) return N - 1; + if (BN(debt).gt(maxRecv[N].maxDebt)) return N - 1; } return this.maxBands; From f7c21dd77dcf13a60fee350462c7532d5abc40b0 Mon Sep 17 00:00:00 2001 From: macket Date: Fri, 12 Apr 2024 18:17:06 +0400 Subject: [PATCH 22/36] refactor: createLoanExpectedCollateral and borrowMoreExpectedCollateral --- src/markets/OneWayMarketTemplate.ts | 67 ++++++++++++++--------------- test/leverage.test.ts | 11 ++--- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 42b459d..547bc43 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -171,8 +171,8 @@ export class OneWayMarketTemplate { maxLeverage: string, avgPrice: string, }>>, - createLoanTotalCollateral: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => - Promise<{ stateCollateral: string, userCollateral: string, userCollateralFromBorrowed: string, leverageCollateral: string, totalCollateral: string }>, + createLoanExpectedCollateral: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => + Promise<{ userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, totalCollateral: string, leverage: string }>, createLoanMaxRange: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise, createLoanBands: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number) => Promise<[number, number]>, createLoanBandsAllRanges: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise>, @@ -192,8 +192,8 @@ export class OneWayMarketTemplate { maxTotalCollateral: string, collateralAvgPrice: string, }>, - borrowMoreTotalCollateral: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => - Promise<{ stateCollateral: string, userCollateral: string, userCollateralFromBorrowed: string, leverageCollateral: string, totalCollateral: string }>, + borrowMoreExpectedCollateral: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => + Promise<{ userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, totalCollateral: string }>, borrowMoreBands: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise<[number, number]>, borrowMorePrices: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise, borrowMoreHealth: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, full?: boolean, address?: string) => Promise, @@ -321,7 +321,7 @@ export class OneWayMarketTemplate { createLoanMaxRecv: this.leverageCreateLoanMaxRecv.bind(this), createLoanMaxRecvAllRanges: this.leverageCreateLoanMaxRecvAllRanges.bind(this), - createLoanTotalCollateral: this.leverageCreateLoanTotalCollateral.bind(this), + createLoanExpectedCollateral: this.leverageCreateLoanExpectedCollateral.bind(this), createLoanMaxRange: this.leverageCreateLoanMaxRange.bind(this), createLoanBands: this.leverageCreateLoanBands.bind(this), createLoanBandsAllRanges: this.leverageCreateLoanBandsAllRanges.bind(this), @@ -333,7 +333,7 @@ export class OneWayMarketTemplate { createLoan: this.leverageCreateLoan.bind(this), borrowMoreMaxRecv: this.leverageBorrowMoreMaxRecv.bind(this), - borrowMoreTotalCollateral: this.leverageBorrowMoreTotalCollateral.bind(this), + borrowMoreExpectedCollateral: this.leverageBorrowMoreExpectedCollateral.bind(this), borrowMoreBands: this.leverageBorrowMoreBands.bind(this), borrowMorePrices: this.leverageBorrowMorePrices.bind(this), borrowMoreHealth: this.leverageBorrowMoreHealth.bind(this), @@ -2104,40 +2104,41 @@ export class OneWayMarketTemplate { maxAge: 60 * 1000, // 1m }); - private _leverageTotalCollateral = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, user?: string): - Promise<{ _stateCollateral: bigint, _userCollateral: bigint, _userCollateralFromBorrowed: bigint, _leverageCollateral: bigint, _totalCollateral: bigint }> => { + private _leverageExpectedCollateral = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, user?: string): + Promise<{ _userCollateral: bigint, _collateralFromUserBorrowed: bigint, _collateralFromDebt: bigint, _totalCollateral: bigint, _futureStateCollateral: bigint }> => { const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); const _debt = parseUnits(debt, this.borrowed_token.decimals); const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); // additionalCollateral = (userBorrowed / p) + leverageCollateral const _additionalCollateral = BigInt(await _getQuote1inch(this.addresses.borrowed_token, this.addresses.collateral_token, _debt + _userBorrowed)); - const _leverageCollateral = _debt * BigInt(10**18) / (_debt + _userBorrowed) * _additionalCollateral / BigInt(10**18); - const _userCollateralFromBorrowed = _additionalCollateral - _leverageCollateral; + 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._userState(user); if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); _stateCollateral = _collateral; } - const _totalCollateral = _stateCollateral + _userCollateral + _additionalCollateral; + const _totalCollateral = _userCollateral + _additionalCollateral; + const _futureStateCollateral = _stateCollateral + _totalCollateral; - return { _stateCollateral, _userCollateral, _userCollateralFromBorrowed, _leverageCollateral, _totalCollateral }; + return { _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, _totalCollateral, _futureStateCollateral }; }, { promise: true, maxAge: 60 * 1000, // 1m }); - private async leverageCreateLoanTotalCollateral(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): - Promise<{ stateCollateral: string, userCollateral: string, userCollateralFromBorrowed: string, leverageCollateral: string, totalCollateral: string }> { - const { _stateCollateral, _userCollateral, _userCollateralFromBorrowed, _leverageCollateral, _totalCollateral } = - await this._leverageTotalCollateral(userCollateral, userBorrowed, debt); + private async leverageCreateLoanExpectedCollateral(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): + Promise<{ userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, totalCollateral: string, leverage: string }> { + const { _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, _totalCollateral } = + await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt); return { - stateCollateral: formatUnits(_stateCollateral, this.collateral_token.decimals), userCollateral: formatUnits(_userCollateral, this.collateral_token.decimals), - userCollateralFromBorrowed: formatUnits(_userCollateralFromBorrowed, this.collateral_token.decimals), - leverageCollateral: formatUnits(_leverageCollateral, this.collateral_token.decimals), + collateralFromUserBorrowed: formatUnits(_collateralFromUserBorrowed, this.collateral_token.decimals), + collateralFromDebt: formatUnits(_collateralFromDebt, this.collateral_token.decimals), totalCollateral: formatUnits(_totalCollateral, this.collateral_token.decimals), + leverage: toBN(_collateralFromDebt, this.collateral_token.decimals).div(toBN(_userCollateral + _collateralFromUserBorrowed, this.collateral_token.decimals)).toString(), } } @@ -2159,9 +2160,9 @@ export class OneWayMarketTemplate { _stateDebt = _debt; if (range < 0) range = Number(lending.formatUnits(_N, 0)); } - const { _totalCollateral } = await this._leverageTotalCollateral(userCollateral, userBorrowed, debt, user); + const { _futureStateCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt, user); const _debt = _stateDebt + parseUnits(debt, this.borrowed_token.decimals); - return await lending.contracts[this.addresses.controller].contract.calculate_debt_n1(_totalCollateral, _debt, range, lending.constantOptions); + return await lending.contracts[this.addresses.controller].contract.calculate_debt_n1(_futureStateCollateral, _debt, range, lending.constantOptions); }, { promise: true, @@ -2169,11 +2170,11 @@ export class OneWayMarketTemplate { }); private _leverageCalcN1AllRanges = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, maxN: number): Promise => { - const { _totalCollateral } = await this._leverageTotalCollateral(userCollateral, userBorrowed, debt); + 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(lending.contracts[this.addresses.controller].multicallContract.calculate_debt_n1(_totalCollateral, _debt, N)); + calls.push(lending.contracts[this.addresses.controller].multicallContract.calculate_debt_n1(_futureStateCollateral, _debt, N)); } return await lending.multicallProvider.all(calls) as bigint[]; }, @@ -2265,15 +2266,14 @@ export class OneWayMarketTemplate { ): Promise { this._checkLeverageZap(); if (range > 0) this._checkRange(range); - const { _totalCollateral } = await this._leverageTotalCollateral(userCollateral, userBorrowed, dDebt, user); - const { _borrowed, _collateral: _stateCollateral, _N } = await this._userState(user); + const { _totalCollateral } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, dDebt, user); + const { _borrowed, _N } = await this._userState(user); if (_borrowed > BigInt(0)) throw Error(`User ${user} is already in liquidation mode`); if (range < 0) range = Number(lending.formatUnits(_N, 0)); - const _dCollateral = _totalCollateral - _stateCollateral; const _dDebt = parseUnits(dDebt, this.collateral_token.decimals); const contract = lending.contracts[this.addresses.controller].contract; - let _health = await contract.health_calculator(user, _dCollateral, _dDebt, full, range, lending.constantOptions) as bigint; + let _health = await contract.health_calculator(user, _totalCollateral, _dDebt, full, range, lending.constantOptions) as bigint; _health = _health * BigInt(100); return formatUnits(_health); @@ -2429,16 +2429,15 @@ export class OneWayMarketTemplate { }; } - private async leverageBorrowMoreTotalCollateral(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address = ""): - Promise<{ stateCollateral: string, userCollateral: string, userCollateralFromBorrowed: string, leverageCollateral: string, totalCollateral: string }> { + private async leverageBorrowMoreExpectedCollateral(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address = ""): + Promise<{ userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, totalCollateral: string }> { address = _getAddress(address); - const { _stateCollateral, _userCollateral, _userCollateralFromBorrowed, _leverageCollateral, _totalCollateral } = - await this._leverageTotalCollateral(userCollateral, userBorrowed, dDebt, address); + const { _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, _totalCollateral } = + await this._leverageExpectedCollateral(userCollateral, userBorrowed, dDebt, address); return { - stateCollateral: formatUnits(_stateCollateral, this.collateral_token.decimals), userCollateral: formatUnits(_userCollateral, this.collateral_token.decimals), - userCollateralFromBorrowed: formatUnits(_userCollateralFromBorrowed, this.collateral_token.decimals), - leverageCollateral: formatUnits(_leverageCollateral, this.collateral_token.decimals), + collateralFromUserBorrowed: formatUnits(_collateralFromUserBorrowed, this.collateral_token.decimals), + collateralFromDebt: formatUnits(_collateralFromDebt, this.collateral_token.decimals), totalCollateral: formatUnits(_totalCollateral, this.collateral_token.decimals), } } diff --git a/test/leverage.test.ts b/test/leverage.test.ts index 8b215af..60a75b3 100644 --- a/test/leverage.test.ts +++ b/test/leverage.test.ts @@ -29,13 +29,13 @@ const generalTest = (id: string) => { const collateralAmount = 0.5; const borrowedAmount = 1000; const N = 10; - const maxRecv = await oneWayMarket.leverage.createLoanMaxRecv(collateralAmount, borrowedAmount, N); - const debtAmount = (Number(maxRecv.maxBorrowable) / 2).toFixed(18); + const { maxDebt } = await oneWayMarket.leverage.createLoanMaxRecv(collateralAmount, borrowedAmount, N); + const debtAmount = (Number(maxDebt) / 2).toFixed(18); const createLoanBands = await oneWayMarket.leverage.createLoanBands(collateralAmount, borrowedAmount, debtAmount, N); const createLoanPrices = await oneWayMarket.leverage.createLoanPrices(collateralAmount, borrowedAmount, debtAmount, N); const createLoanFullHealth = await oneWayMarket.leverage.createLoanHealth(collateralAmount, borrowedAmount, debtAmount, N); const createLoanHealth = await oneWayMarket.leverage.createLoanHealth(collateralAmount, borrowedAmount, debtAmount, N, false); - const { totalCollateral } = await oneWayMarket.leverage.createLoanTotalCollateral(collateralAmount, borrowedAmount, debtAmount); + const { totalCollateral } = await oneWayMarket.leverage.createLoanExpectedCollateral(collateralAmount, borrowedAmount, debtAmount); await oneWayMarket.leverage.createLoan(collateralAmount, borrowedAmount, debtAmount, N, 1); @@ -75,7 +75,7 @@ const generalTest = (id: string) => { const borrowMorePrices = await oneWayMarket.leverage.borrowMorePrices(collateralAmount, borrowedAmount, debtAmount); const borrowMoreFullHealth = await oneWayMarket.leverage.borrowMoreHealth(collateralAmount, borrowedAmount, debtAmount); const borrowMoreHealth = await oneWayMarket.leverage.borrowMoreHealth(collateralAmount, borrowedAmount, debtAmount, false); - const { totalCollateral } = await oneWayMarket.leverage.borrowMoreTotalCollateral(collateralAmount, borrowedAmount, debtAmount); + const { totalCollateral } = await oneWayMarket.leverage.borrowMoreExpectedCollateral(collateralAmount, borrowedAmount, debtAmount); await oneWayMarket.leverage.borrowMore(collateralAmount, borrowedAmount, debtAmount,1); @@ -94,7 +94,8 @@ const generalTest = (id: string) => { assert.approximately(Number(borrowMoreHealth), Number(health), 0.1, 'health'); assert.equal(Number(balances.collateral), Number(initialBalances.collateral) - Number(collateralAmount), 'wallet collateral'); assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet stablecoin'); - assert.isAtMost(Math.abs(Number(state.collateral) - Number(totalCollateral)) / Number(totalCollateral), 1e-3, 'state collateral'); + const collateralDiff = Number(state.collateral) - Number(initialState.collateral); + assert.isAtMost(Math.abs(collateralDiff - Number(totalCollateral)) / Number(totalCollateral), 1e-3, 'state collateral'); assert.approximately(Number(state.debt), Number(initialState.debt) + Number(debtAmount), 1e-3, 'state debt'); }); From afb4970fe466c8fcd42dbfaad1cb19dd4bd4187c Mon Sep 17 00:00:00 2001 From: macket Date: Fri, 12 Apr 2024 19:00:29 +0400 Subject: [PATCH 23/36] refactor: leverageBorrowMoreMaxRecv --- src/markets/OneWayMarketTemplate.ts | 37 ++++++++++++++--------------- test/leverage.test.ts | 4 ++-- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 547bc43..527d3c4 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -172,7 +172,7 @@ export class OneWayMarketTemplate { avgPrice: string, }>>, createLoanExpectedCollateral: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => - Promise<{ userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, totalCollateral: string, leverage: string }>, + Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, leverage: string }>, createLoanMaxRange: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise, createLoanBands: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number) => Promise<[number, number]>, createLoanBandsAllRanges: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise>, @@ -185,15 +185,15 @@ export class OneWayMarketTemplate { borrowMoreMaxRecv: (userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise<{ - stateCollateral: string, - stateDebt: string, - maxBorrowable: string, - maxAdditionalCollateral: string, + maxDebt: string, maxTotalCollateral: string, - collateralAvgPrice: string, + userCollateral: string, + collateralFromUserBorrowed: string, + collateralFromMaxDebt: string, + avgPrice: string, }>, borrowMoreExpectedCollateral: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => - Promise<{ userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, totalCollateral: string }>, + Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string }>, borrowMoreBands: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise<[number, number]>, borrowMorePrices: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise, borrowMoreHealth: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, full?: boolean, address?: string) => Promise, @@ -2370,12 +2370,12 @@ export class OneWayMarketTemplate { private async leverageBorrowMoreMaxRecv(userCollateral: TAmount, userBorrowed: TAmount, address = ""): Promise<{ - stateCollateral: string, - stateDebt: string, - maxBorrowable: string, - maxAdditionalCollateral: string, + maxDebt: string, maxTotalCollateral: string, - collateralAvgPrice: string, + userCollateral: string, + collateralFromUserBorrowed: string, + collateralFromMaxDebt: string, + avgPrice: string, }> { // max_borrowable = userCollateral / (1 / (k_effective * max_p_base) - 1 / p_avg) this._checkLeverageZap(); @@ -2415,17 +2415,16 @@ export class OneWayMarketTemplate { } if (maxBorrowableBN.eq(0)) _userEffectiveCollateral = BigInt(0); - const _maxAdditionalCollateral = _userEffectiveCollateral + _maxLeverageCollateral; - const _maxTotalCollateral = _stateCollateral + _userEffectiveCollateral + _maxLeverageCollateral + const _maxTotalCollateral = _userEffectiveCollateral + _maxLeverageCollateral const _maxBorrowable = await controllerContract.max_borrowable(_maxTotalCollateral, _N, _stateDebt, lending.constantOptions) - _stateDebt; return { - stateCollateral: formatUnits(_stateCollateral, this.collateral_token.decimals), - stateDebt: formatUnits(_stateDebt, this.borrowed_token.decimals), - maxBorrowable: formatUnits(_maxBorrowable, this.borrowed_token.decimals), - maxAdditionalCollateral: formatUnits(_maxAdditionalCollateral, this.collateral_token.decimals), + maxDebt: formatUnits(_maxBorrowable, this.borrowed_token.decimals), maxTotalCollateral: formatUnits(_maxTotalCollateral, this.collateral_token.decimals), - collateralAvgPrice: pAvgBN.toString(), + userCollateral: formatNumber(userCollateral, this.collateral_token.decimals), + collateralFromUserBorrowed: formatNumber(BN(userBorrowed).div(pAvgBN).toString(), this.collateral_token.decimals), + collateralFromMaxDebt: formatUnits(_maxLeverageCollateral, this.collateral_token.decimals), + avgPrice: pAvgBN.toString(), }; } diff --git a/test/leverage.test.ts b/test/leverage.test.ts index 60a75b3..9068429 100644 --- a/test/leverage.test.ts +++ b/test/leverage.test.ts @@ -69,8 +69,8 @@ const generalTest = (id: string) => { const collateralAmount = 0.5; const borrowedAmount = 1000; - const maxRecv = await oneWayMarket.leverage.borrowMoreMaxRecv(collateralAmount, borrowedAmount); - const debtAmount = (Number(maxRecv.maxBorrowable) / 2).toFixed(18); + const { maxDebt } = await oneWayMarket.leverage.borrowMoreMaxRecv(collateralAmount, borrowedAmount); + const debtAmount = (Number(maxDebt) / 2).toFixed(18); const borrowMoreBands = await oneWayMarket.leverage.borrowMoreBands(collateralAmount, borrowedAmount, debtAmount); const borrowMorePrices = await oneWayMarket.leverage.borrowMorePrices(collateralAmount, borrowedAmount, debtAmount); const borrowMoreFullHealth = await oneWayMarket.leverage.borrowMoreHealth(collateralAmount, borrowedAmount, debtAmount); From 9ac276800958947215913a8c372ab38032a0515b Mon Sep 17 00:00:00 2001 From: macket Date: Fri, 12 Apr 2024 19:28:06 +0400 Subject: [PATCH 24/36] fix: leverageBorrowMoreMaxRecv --- src/markets/OneWayMarketTemplate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 527d3c4..5195d55 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -2416,7 +2416,7 @@ export class OneWayMarketTemplate { if (maxBorrowableBN.eq(0)) _userEffectiveCollateral = BigInt(0); const _maxTotalCollateral = _userEffectiveCollateral + _maxLeverageCollateral - const _maxBorrowable = await controllerContract.max_borrowable(_maxTotalCollateral, _N, _stateDebt, lending.constantOptions) - _stateDebt; + const _maxBorrowable = await controllerContract.max_borrowable(_stateCollateral + _maxTotalCollateral, _N, _stateDebt, lending.constantOptions) - _stateDebt; return { maxDebt: formatUnits(_maxBorrowable, this.borrowed_token.decimals), From 9c8a445cc26045c350be7b9accef6e24b67a8ecb Mon Sep 17 00:00:00 2001 From: macket Date: Fri, 12 Apr 2024 19:40:20 +0400 Subject: [PATCH 25/36] refactor: leverageRepayExpectedBorrowed --- src/markets/OneWayMarketTemplate.ts | 40 ++++++++++++++--------------- test/leverage.test.ts | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 5195d55..c57e83a 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -201,8 +201,8 @@ export class OneWayMarketTemplate { borrowMoreApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, borrowMore: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, slippage?: number) => Promise, - repayTotalBorrowed: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount) => - Promise<{ borrowedFromStateCollateral: string, userBorrowedFromCollateral: string, userBorrowed: string, totalBorrowed: string }>, + repayExpectedBorrowed: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount) => + Promise<{ totalBorrowed: string, borrowedFromStateCollateral: string, borrowedFromUserCollateral: string, userBorrowed: string }>, repayIsFull: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, repayIsAvailable: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, repayBands: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise<[number, number]>, @@ -341,7 +341,7 @@ export class OneWayMarketTemplate { borrowMoreApprove: this.leverageCreateLoanApprove.bind(this), borrowMore: this.leverageBorrowMore.bind(this), - repayTotalBorrowed: this.leverageRepayTotalBorrowed.bind(this), + repayExpectedBorrowed: this.leverageRepayExpectedBorrowed.bind(this), repayIsFull: this.leverageRepayIsFull.bind(this), repayIsAvailable: this.leverageRepayIsAvailable.bind(this), repayBands: this.leverageRepayBands.bind(this), @@ -2105,7 +2105,7 @@ export class OneWayMarketTemplate { }); private _leverageExpectedCollateral = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, user?: string): - Promise<{ _userCollateral: bigint, _collateralFromUserBorrowed: bigint, _collateralFromDebt: bigint, _totalCollateral: bigint, _futureStateCollateral: bigint }> => { + Promise<{ _futureStateCollateral: bigint, _totalCollateral: bigint, _userCollateral: bigint, _collateralFromUserBorrowed: bigint, _collateralFromDebt: bigint }> => { const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); const _debt = parseUnits(debt, this.borrowed_token.decimals); const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); @@ -2122,7 +2122,7 @@ export class OneWayMarketTemplate { const _totalCollateral = _userCollateral + _additionalCollateral; const _futureStateCollateral = _stateCollateral + _totalCollateral; - return { _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, _totalCollateral, _futureStateCollateral }; + return { _futureStateCollateral, _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt }; }, { promise: true, @@ -2130,14 +2130,14 @@ export class OneWayMarketTemplate { }); private async leverageCreateLoanExpectedCollateral(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): - Promise<{ userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, totalCollateral: string, leverage: string }> { - const { _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, _totalCollateral } = + Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, leverage: string }> { + const { _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt); return { + totalCollateral: formatUnits(_totalCollateral, this.collateral_token.decimals), userCollateral: formatUnits(_userCollateral, this.collateral_token.decimals), collateralFromUserBorrowed: formatUnits(_collateralFromUserBorrowed, this.collateral_token.decimals), collateralFromDebt: formatUnits(_collateralFromDebt, this.collateral_token.decimals), - totalCollateral: formatUnits(_totalCollateral, this.collateral_token.decimals), leverage: toBN(_collateralFromDebt, this.collateral_token.decimals).div(toBN(_userCollateral + _collateralFromUserBorrowed, this.collateral_token.decimals)).toString(), } } @@ -2429,15 +2429,15 @@ export class OneWayMarketTemplate { } private async leverageBorrowMoreExpectedCollateral(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address = ""): - Promise<{ userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, totalCollateral: string }> { + Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string }> { address = _getAddress(address); - const { _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, _totalCollateral } = + const { _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, dDebt, address); return { + totalCollateral: formatUnits(_totalCollateral, this.collateral_token.decimals), userCollateral: formatUnits(_userCollateral, this.collateral_token.decimals), collateralFromUserBorrowed: formatUnits(_collateralFromUserBorrowed, this.collateral_token.decimals), collateralFromDebt: formatUnits(_collateralFromDebt, this.collateral_token.decimals), - totalCollateral: formatUnits(_totalCollateral, this.collateral_token.decimals), } } @@ -2512,21 +2512,21 @@ export class OneWayMarketTemplate { // ---------------- LEVERAGE REPAY ---------------- - private leverageRepayTotalBorrowed = memoize( async (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount): - Promise<{ borrowedFromStateCollateral: string, userBorrowedFromCollateral: string, userBorrowed: string, totalBorrowed: string }> => { + private leverageRepayExpectedBorrowed = memoize( async (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount): + Promise<{ totalBorrowed: string, borrowedFromStateCollateral: string, borrowedFromUserCollateral: string, userBorrowed: string }> => { this._checkLeverageZap(); const _stateCollateral = parseUnits(stateCollateral, this.collateral_token.decimals); const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); const _borrowedExpected = BigInt(await _getQuote1inch(this.addresses.collateral_token, this.addresses.borrowed_token, _stateCollateral + _userCollateral)); const _borrowedFromStateCollateral = _stateCollateral * BigInt(10**18) / (_stateCollateral + _userCollateral) * _borrowedExpected / BigInt(10**18); - const _userBorrowedFromCollateral = _borrowedExpected - _borrowedFromStateCollateral; + const _borrowedFromUserCollateral = _borrowedExpected - _borrowedFromStateCollateral; const _totalBorrowed = _borrowedExpected + parseUnits(userBorrowed, this.borrowed_token.decimals); return { - borrowedFromStateCollateral: formatUnits(_borrowedFromStateCollateral, this.borrowed_token.decimals), - userBorrowedFromCollateral: formatUnits(_userBorrowedFromCollateral, this.borrowed_token.decimals), - userBorrowed: String(userBorrowed), totalBorrowed: formatUnits(_totalBorrowed, this.borrowed_token.decimals), + borrowedFromStateCollateral: formatUnits(_borrowedFromStateCollateral, this.borrowed_token.decimals), + borrowedFromUserCollateral: formatUnits(_borrowedFromUserCollateral, this.borrowed_token.decimals), + userBorrowed: formatNumber(userBorrowed, this.borrowed_token.decimals), } }, { @@ -2538,7 +2538,7 @@ export class OneWayMarketTemplate { this._checkLeverageZap(); address = _getAddress(address); const { borrowed: stateBorrowed, debt } = await this.userState(address); - const { totalBorrowed } = await this.leverageRepayTotalBorrowed(stateCollateral, userCollateral, userBorrowed); + const { totalBorrowed } = await this.leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed); return BN(stateBorrowed).plus(totalBorrowed).gt(debt); } @@ -2572,7 +2572,7 @@ export class OneWayMarketTemplate { let _n1 = parseUnits(0, 0); let _n2 = parseUnits(0, 0); - const { totalBorrowed } = await this.leverageRepayTotalBorrowed(stateCollateral, userCollateral, userBorrowed); + const { totalBorrowed } = await this.leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed); const _repayExpected = parseUnits(totalBorrowed, this.borrowed_token.decimals); try { _n1 = await lending.contracts[this.addresses.controller].contract.calculate_debt_n1(_stateCollateral - _stateRepayCollateral, _stateDebt - _repayExpected, _N); @@ -2609,7 +2609,7 @@ export class OneWayMarketTemplate { if (_stateBorrowed > BigInt(0)) return "0.0"; if (!(await this.leverageRepayIsAvailable(stateCollateral, userCollateral, userBorrowed, address))) return "0.0"; - const { totalBorrowed } = await this.leverageRepayTotalBorrowed(stateCollateral, userCollateral, userBorrowed); + const { totalBorrowed } = await this.leverageRepayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed); const _dCollateral = parseUnits(stateCollateral, this.collateral_token.decimals) * BigInt(-1); const _dDebt = parseUnits(totalBorrowed, this.borrowed_token.decimals) * BigInt(-1); diff --git a/test/leverage.test.ts b/test/leverage.test.ts index 9068429..bda0f50 100644 --- a/test/leverage.test.ts +++ b/test/leverage.test.ts @@ -115,7 +115,7 @@ const generalTest = (id: string) => { const repayPrices = await oneWayMarket.leverage.repayPrices(stateCollateralAmount, collateralAmount, borrowedAmount); const repayFullHealth = await oneWayMarket.leverage.repayHealth(stateCollateralAmount, collateralAmount, borrowedAmount); const repayHealth = await oneWayMarket.leverage.repayHealth(stateCollateralAmount, collateralAmount, borrowedAmount, false); - const { totalBorrowed } = await oneWayMarket.leverage.repayTotalBorrowed(stateCollateralAmount, collateralAmount, borrowedAmount); + const { totalBorrowed } = await oneWayMarket.leverage.repayExpectedBorrowed(stateCollateralAmount, collateralAmount, borrowedAmount); await oneWayMarket.leverage.repay(stateCollateralAmount, collateralAmount, borrowedAmount, 1); From c6ce03c5266093f2446ff958c4f5b6c13188b291 Mon Sep 17 00:00:00 2001 From: macket Date: Tue, 23 Apr 2024 13:14:27 +0400 Subject: [PATCH 26/36] test: leverage createLoan, borrowMore and repay tests --- test/leverage.test.ts | 155 -------------------- test/leverageBorrowMore.test.ts | 242 ++++++++++++++++++++++++++++++++ test/leverageCreateLoan.test.ts | 232 ++++++++++++++++++++++++++++++ test/leverageRepay.test.ts | 235 +++++++++++++++++++++++++++++++ 4 files changed, 709 insertions(+), 155 deletions(-) delete mode 100644 test/leverage.test.ts create mode 100644 test/leverageBorrowMore.test.ts create mode 100644 test/leverageCreateLoan.test.ts create mode 100644 test/leverageRepay.test.ts diff --git a/test/leverage.test.ts b/test/leverage.test.ts deleted file mode 100644 index bda0f50..0000000 --- a/test/leverage.test.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { assert } from "chai"; -import lending from "../src/index.js"; -import { API_KEY_1INCH } from "./rpcUrls.test.js"; -import { getOneWayMarket, OneWayMarketTemplate } from "../src/markets/index.js"; - - -const ONE_WAY_MARKETS = ['one-way-market-0']; - -const generalTest = (id: string) => { - describe(`${id} leverage test`, function () { - let oneWayMarket: OneWayMarketTemplate; - - before(async function () { - oneWayMarket = getOneWayMarket(id); - if (Number(await oneWayMarket.vault.totalLiquidity()) === 0) { - const maxDeposit = Number(await oneWayMarket.vault.maxDeposit()) * 0.7; - await oneWayMarket.vault.deposit(maxDeposit); - } - }); - - it('Leverage createLoan', async function () { - const initialBalances = await oneWayMarket.wallet.balances(); - const loanExists = await oneWayMarket.userLoanExists(); - - assert.isFalse(loanExists); - assert.isAbove(Number(initialBalances.collateral), 0); - assert.isAbove(Number(initialBalances.borrowed), 0); - - const collateralAmount = 0.5; - const borrowedAmount = 1000; - const N = 10; - const { maxDebt } = await oneWayMarket.leverage.createLoanMaxRecv(collateralAmount, borrowedAmount, N); - const debtAmount = (Number(maxDebt) / 2).toFixed(18); - const createLoanBands = await oneWayMarket.leverage.createLoanBands(collateralAmount, borrowedAmount, debtAmount, N); - const createLoanPrices = await oneWayMarket.leverage.createLoanPrices(collateralAmount, borrowedAmount, debtAmount, N); - const createLoanFullHealth = await oneWayMarket.leverage.createLoanHealth(collateralAmount, borrowedAmount, debtAmount, N); - const createLoanHealth = await oneWayMarket.leverage.createLoanHealth(collateralAmount, borrowedAmount, debtAmount, N, false); - const { totalCollateral } = await oneWayMarket.leverage.createLoanExpectedCollateral(collateralAmount, borrowedAmount, debtAmount); - - await oneWayMarket.leverage.createLoan(collateralAmount, borrowedAmount, debtAmount, N, 1); - - const balances = await oneWayMarket.wallet.balances(); - const state = await oneWayMarket.userState(); - const userBands = await oneWayMarket.userBands(); - const userPrices = await oneWayMarket.userPrices(); - const fullHealth = await oneWayMarket.userHealth(); - const health = await oneWayMarket.userHealth(false); - - assert.equal(Number(createLoanBands[0]), Number(userBands[0]), 'band 0'); - assert.equal(Number(createLoanBands[1]), Number(userBands[1]), 'band 1'); - assert.approximately(Number(createLoanPrices[0]), Number(userPrices[0]), 1e-2, 'price 0'); - assert.approximately(Number(createLoanPrices[1]), Number(userPrices[1]), 1e-2, 'price 1'); - assert.approximately(Number(createLoanFullHealth), Number(fullHealth), 0.3, 'full health'); - assert.approximately(Number(createLoanHealth), Number(health), 0.1, 'health'); - assert.equal(Number(balances.collateral), Number(initialBalances.collateral) - Number(collateralAmount), 'wallet collateral'); - assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet stablecoin'); - assert.isAtMost(Math.abs(Number(state.collateral) - Number(totalCollateral)) / Number(totalCollateral), 1e-3, 'state collateral'); - assert.equal(Number(state.debt), Number(debtAmount), 'state debt'); - }); - - it('Leverage borrowMore', async function () { - const initialBalances = await oneWayMarket.wallet.balances(); - const initialState = await oneWayMarket.userState(); - const loanExists = await oneWayMarket.userLoanExists(); - - assert.isTrue(loanExists); - assert.isAbove(Number(initialBalances.collateral), 0); - assert.isAbove(Number(initialBalances.borrowed), 0); - - const collateralAmount = 0.5; - const borrowedAmount = 1000; - const { maxDebt } = await oneWayMarket.leverage.borrowMoreMaxRecv(collateralAmount, borrowedAmount); - const debtAmount = (Number(maxDebt) / 2).toFixed(18); - const borrowMoreBands = await oneWayMarket.leverage.borrowMoreBands(collateralAmount, borrowedAmount, debtAmount); - const borrowMorePrices = await oneWayMarket.leverage.borrowMorePrices(collateralAmount, borrowedAmount, debtAmount); - const borrowMoreFullHealth = await oneWayMarket.leverage.borrowMoreHealth(collateralAmount, borrowedAmount, debtAmount); - const borrowMoreHealth = await oneWayMarket.leverage.borrowMoreHealth(collateralAmount, borrowedAmount, debtAmount, false); - const { totalCollateral } = await oneWayMarket.leverage.borrowMoreExpectedCollateral(collateralAmount, borrowedAmount, debtAmount); - - await oneWayMarket.leverage.borrowMore(collateralAmount, borrowedAmount, debtAmount,1); - - const balances = await oneWayMarket.wallet.balances(); - const state = await oneWayMarket.userState(); - const userBands = await oneWayMarket.userBands(); - const userPrices = await oneWayMarket.userPrices(); - const fullHealth = await oneWayMarket.userHealth(); - const health = await oneWayMarket.userHealth(false); - - assert.equal(Number(borrowMoreBands[0]), Number(userBands[0]), 'band 0'); - assert.equal(Number(borrowMoreBands[1]), Number(userBands[1]), 'band 1'); - assert.approximately(Number(borrowMorePrices[0]), Number(userPrices[0]), 1e-2, 'price 0'); - assert.approximately(Number(borrowMorePrices[1]), Number(userPrices[1]), 1e-2, 'price 1'); - assert.approximately(Number(borrowMoreFullHealth), Number(fullHealth), 0.2, 'full health'); - assert.approximately(Number(borrowMoreHealth), Number(health), 0.1, 'health'); - assert.equal(Number(balances.collateral), Number(initialBalances.collateral) - Number(collateralAmount), 'wallet collateral'); - assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet stablecoin'); - const collateralDiff = Number(state.collateral) - Number(initialState.collateral); - assert.isAtMost(Math.abs(collateralDiff - Number(totalCollateral)) / Number(totalCollateral), 1e-3, 'state collateral'); - assert.approximately(Number(state.debt), Number(initialState.debt) + Number(debtAmount), 1e-3, 'state debt'); - }); - - it('Leverage repay', async function () { - const initialBalances = await oneWayMarket.wallet.balances(); - const initialState = await oneWayMarket.userState(); - const loanExists = await oneWayMarket.userLoanExists(); - - assert.isTrue(loanExists); - assert.isAbove(Number(initialBalances.collateral), 0); - assert.isAbove(Number(initialBalances.borrowed), 0); - - const stateCollateralAmount = 0.3; - const collateralAmount = 0.2; - const borrowedAmount = 500; - const repayBands = await oneWayMarket.leverage.repayBands(stateCollateralAmount, collateralAmount, borrowedAmount); - const repayPrices = await oneWayMarket.leverage.repayPrices(stateCollateralAmount, collateralAmount, borrowedAmount); - const repayFullHealth = await oneWayMarket.leverage.repayHealth(stateCollateralAmount, collateralAmount, borrowedAmount); - const repayHealth = await oneWayMarket.leverage.repayHealth(stateCollateralAmount, collateralAmount, borrowedAmount, false); - const { totalBorrowed } = await oneWayMarket.leverage.repayExpectedBorrowed(stateCollateralAmount, collateralAmount, borrowedAmount); - - await oneWayMarket.leverage.repay(stateCollateralAmount, collateralAmount, borrowedAmount, 1); - - const balances = await oneWayMarket.wallet.balances(); - const state = await oneWayMarket.userState(); - const userBands = await oneWayMarket.userBands(); - const userPrices = await oneWayMarket.userPrices(); - const fullHealth = await oneWayMarket.userHealth(); - const health = await oneWayMarket.userHealth(false); - - assert.equal(Number(repayBands[0]), Number(userBands[0]), 'band 0'); - assert.equal(Number(repayBands[1]), Number(userBands[1]), 'band 1'); - assert.approximately(Number(repayPrices[0]), Number(userPrices[0]), 1e-2, 'price 0'); - assert.approximately(Number(repayPrices[1]), Number(userPrices[1]), 1e-2, 'price 1'); - assert.approximately(Number(repayFullHealth), Number(fullHealth), 0.1, 'full health'); - assert.approximately(Number(repayHealth), Number(health), 0.1, 'health'); - assert.equal(Number(balances.collateral), Number(initialBalances.collateral) - Number(collateralAmount), 'wallet collateral'); - assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet stablecoin'); - assert.equal(Number(state.collateral), Number(initialState.collateral) - Number(stateCollateralAmount), 'state collateral'); - const debtDiff = Number(initialState.debt) - Number(state.debt); - assert.isAtMost(Math.abs(debtDiff - Number(totalBorrowed)) / Number(totalBorrowed), 2e-3, 'state debt'); - }); - }) -} - -describe('Leverage test', async function () { - this.timeout(120000); - - before(async function () { - await lending.init('JsonRpc', {},{ gasPrice: 0 }, API_KEY_1INCH); - await lending.oneWayfactory.fetchMarkets(); - }); - - for (const oneWayMarketId of ONE_WAY_MARKETS) { - generalTest(oneWayMarketId); - } -}) diff --git a/test/leverageBorrowMore.test.ts b/test/leverageBorrowMore.test.ts new file mode 100644 index 0000000..88e2531 --- /dev/null +++ b/test/leverageBorrowMore.test.ts @@ -0,0 +1,242 @@ +import { assert } from "chai"; +import lending from "../src/index.js"; +import { API_KEY_1INCH } from "./rpcUrls.test.js"; +import { getOneWayMarket, OneWayMarketTemplate } from "../src/markets/index.js"; + + +const ONE_WAY_MARKETS = ['one-way-market-0']; + +const generalTest = (id: string) => { + describe(`${id} leverage borrowMore test`, function () { + let oneWayMarket: OneWayMarketTemplate; + + before(async function () { + oneWayMarket = getOneWayMarket(id); + if (Number(await oneWayMarket.vault.totalLiquidity()) === 0) { + const maxDeposit = Number(await oneWayMarket.vault.maxDeposit()) * 0.7; + await oneWayMarket.vault.deposit(maxDeposit); + } + if (!(await oneWayMarket.userLoanExists())) { + const collateralAmount = 0.5; + const borrowedAmount = 1000; + const N = 10; + const { maxDebt } = await oneWayMarket.leverage.createLoanMaxRecv(collateralAmount, borrowedAmount, N); + const debtAmount = (Number(maxDebt) / 2).toFixed(oneWayMarket.borrowed_token.decimals); + + await oneWayMarket.leverage.createLoan(collateralAmount, borrowedAmount, debtAmount, N, 1); + } + }); + + it('Leverage borrowMore collateral only, debt too high', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isTrue(loanExists, "loanExists"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const collateralAmount = 0.5; + const borrowedAmount = 0; + const { maxDebt } = await oneWayMarket.leverage.borrowMoreMaxRecv(collateralAmount, borrowedAmount); + const debtAmount = (Number(maxDebt) * 1.004).toFixed(oneWayMarket.borrowed_token.decimals); + + try { + await oneWayMarket.leverage.borrowMore(collateralAmount, borrowedAmount, debtAmount,1); + throw Error("Did not revert"); + } catch (e) { + // @ts-ignore + assert.notEqual(e.message, "Did not revert"); + // @ts-ignore + assert.isTrue(e.message.startsWith('execution reverted: "Debt too high"')); + } + }); + + it('Leverage borrowMore borrowed only, debt too high', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isTrue(loanExists, "loanExists"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const collateralAmount = 0; + const borrowedAmount = 1000; + const { maxDebt } = await oneWayMarket.leverage.borrowMoreMaxRecv(collateralAmount, borrowedAmount); + const debtAmount = (Number(maxDebt) * 1.004).toFixed(oneWayMarket.borrowed_token.decimals); + + try { + await oneWayMarket.leverage.borrowMore(collateralAmount, borrowedAmount, debtAmount,1); + throw Error("Did not revert"); + } catch (e) { + // @ts-ignore + assert.notEqual(e.message, "Did not revert"); + // @ts-ignore + assert.isTrue(e.message.startsWith('execution reverted: "Debt too high"')); + } + }); + + it('Leverage borrowMore, debt too high', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isTrue(loanExists, "loanExists"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const collateralAmount = 0.5; + const borrowedAmount = 1000; + const { maxDebt } = await oneWayMarket.leverage.borrowMoreMaxRecv(collateralAmount, borrowedAmount); + const debtAmount = (Number(maxDebt) * 1.004).toFixed(oneWayMarket.borrowed_token.decimals); + + try { + await oneWayMarket.leverage.borrowMore(collateralAmount, borrowedAmount, debtAmount,1); + throw Error("Did not revert"); + } catch (e) { + // @ts-ignore + assert.notEqual(e.message, "Did not revert"); + // @ts-ignore + assert.isTrue(e.message.startsWith('execution reverted: "Debt too high"')); + } + }); + + it('Leverage borrowMore collateral only', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const initialState = await oneWayMarket.userState(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isTrue(loanExists, "loanExists"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const collateralAmount = 0.5; + const borrowedAmount = 0; + const { maxDebt } = await oneWayMarket.leverage.borrowMoreMaxRecv(collateralAmount, borrowedAmount); + const debtAmount = (Number(maxDebt) / 2).toFixed(oneWayMarket.borrowed_token.decimals); + const borrowMoreBands = await oneWayMarket.leverage.borrowMoreBands(collateralAmount, borrowedAmount, debtAmount); + const borrowMorePrices = await oneWayMarket.leverage.borrowMorePrices(collateralAmount, borrowedAmount, debtAmount); + const borrowMoreFullHealth = await oneWayMarket.leverage.borrowMoreHealth(collateralAmount, borrowedAmount, debtAmount); + const borrowMoreHealth = await oneWayMarket.leverage.borrowMoreHealth(collateralAmount, borrowedAmount, debtAmount, false); + const { totalCollateral } = await oneWayMarket.leverage.borrowMoreExpectedCollateral(collateralAmount, borrowedAmount, debtAmount); + + await oneWayMarket.leverage.borrowMore(collateralAmount, borrowedAmount, debtAmount,1); + + const balances = await oneWayMarket.wallet.balances(); + const state = await oneWayMarket.userState(); + const userBands = await oneWayMarket.userBands(); + const userPrices = await oneWayMarket.userPrices(); + const fullHealth = await oneWayMarket.userHealth(); + const health = await oneWayMarket.userHealth(false); + + assert.equal(Number(borrowMoreBands[0]), Number(userBands[0]), 'band 0'); + assert.equal(Number(borrowMoreBands[1]), Number(userBands[1]), 'band 1'); + assert.approximately(Number(borrowMorePrices[0]), Number(userPrices[0]), 0.01, 'price 0'); + assert.approximately(Number(borrowMorePrices[1]), Number(userPrices[1]), 0.01, 'price 1'); + assert.approximately(Number(borrowMoreFullHealth), Number(fullHealth), 0.3, 'full health'); + assert.approximately(Number(borrowMoreHealth), Number(health), 0.3, 'health'); + assert.equal(Number(balances.collateral), Number(initialBalances.collateral) - Number(collateralAmount), 'wallet collateral'); + assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet borrowed'); + const collateralDiff = Number(state.collateral) - Number(initialState.collateral); + assert.isAtMost(Math.abs(collateralDiff - Number(totalCollateral)) / Number(totalCollateral), 0.01, 'state collateral'); + const debtDiff = Number(state.debt) - Number(initialState.debt); + assert.isAtMost(Math.abs(debtDiff - Number(debtAmount)) / Number(debtAmount), 1e-7, 'state debt'); + }); + + it('Leverage borrowMore borrowed only', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const initialState = await oneWayMarket.userState(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isTrue(loanExists, "loanExists"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const collateralAmount = 0; + const borrowedAmount = 1000; + const { maxDebt } = await oneWayMarket.leverage.borrowMoreMaxRecv(collateralAmount, borrowedAmount); + const debtAmount = (Number(maxDebt) / 2).toFixed(oneWayMarket.borrowed_token.decimals); + const borrowMoreBands = await oneWayMarket.leverage.borrowMoreBands(collateralAmount, borrowedAmount, debtAmount); + const borrowMorePrices = await oneWayMarket.leverage.borrowMorePrices(collateralAmount, borrowedAmount, debtAmount); + const borrowMoreFullHealth = await oneWayMarket.leverage.borrowMoreHealth(collateralAmount, borrowedAmount, debtAmount); + const borrowMoreHealth = await oneWayMarket.leverage.borrowMoreHealth(collateralAmount, borrowedAmount, debtAmount, false); + const { totalCollateral } = await oneWayMarket.leverage.borrowMoreExpectedCollateral(collateralAmount, borrowedAmount, debtAmount); + + await oneWayMarket.leverage.borrowMore(collateralAmount, borrowedAmount, debtAmount,1); + + const balances = await oneWayMarket.wallet.balances(); + const state = await oneWayMarket.userState(); + const userBands = await oneWayMarket.userBands(); + const userPrices = await oneWayMarket.userPrices(); + const fullHealth = await oneWayMarket.userHealth(); + const health = await oneWayMarket.userHealth(false); + + assert.equal(Number(borrowMoreBands[0]), Number(userBands[0]), 'band 0'); + assert.equal(Number(borrowMoreBands[1]), Number(userBands[1]), 'band 1'); + assert.approximately(Number(borrowMorePrices[0]), Number(userPrices[0]), 0.01, 'price 0'); + assert.approximately(Number(borrowMorePrices[1]), Number(userPrices[1]), 0.01, 'price 1'); + assert.approximately(Number(borrowMoreFullHealth), Number(fullHealth), 0.3, 'full health'); + assert.approximately(Number(borrowMoreHealth), Number(health), 0.3, 'health'); + assert.equal(Number(balances.collateral), Number(initialBalances.collateral) - Number(collateralAmount), 'wallet collateral'); + assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet borrowed'); + const collateralDiff = Number(state.collateral) - Number(initialState.collateral); + assert.isAtMost(Math.abs(collateralDiff - Number(totalCollateral)) / Number(totalCollateral), 3e-3, 'state collateral'); + const debtDiff = Number(state.debt) - Number(initialState.debt); + assert.isAtMost(Math.abs(debtDiff - Number(debtAmount)) / Number(debtAmount), 1e-7, 'state debt'); + }); + + it('Leverage borrowMore', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const initialState = await oneWayMarket.userState(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isTrue(loanExists, "loanExists"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const collateralAmount = 0.5; + const borrowedAmount = 1000; + const { maxDebt } = await oneWayMarket.leverage.borrowMoreMaxRecv(collateralAmount, borrowedAmount); + // const debtAmount = (Number(maxDebt) * 0.999).toFixed(oneWayMarket.borrowed_token.decimals); + const debtAmount = maxDebt; + const borrowMoreBands = await oneWayMarket.leverage.borrowMoreBands(collateralAmount, borrowedAmount, debtAmount); + const borrowMorePrices = await oneWayMarket.leverage.borrowMorePrices(collateralAmount, borrowedAmount, debtAmount); + const borrowMoreFullHealth = await oneWayMarket.leverage.borrowMoreHealth(collateralAmount, borrowedAmount, debtAmount); + const borrowMoreHealth = await oneWayMarket.leverage.borrowMoreHealth(collateralAmount, borrowedAmount, debtAmount, false); + const { totalCollateral } = await oneWayMarket.leverage.borrowMoreExpectedCollateral(collateralAmount, borrowedAmount, debtAmount); + + await oneWayMarket.leverage.borrowMore(collateralAmount, borrowedAmount, debtAmount,1); + + const balances = await oneWayMarket.wallet.balances(); + const state = await oneWayMarket.userState(); + const userBands = await oneWayMarket.userBands(); + const userPrices = await oneWayMarket.userPrices(); + const fullHealth = await oneWayMarket.userHealth(); + const health = await oneWayMarket.userHealth(false); + + assert.equal(Number(borrowMoreBands[0]), Number(userBands[0]), 'band 0'); + assert.equal(Number(borrowMoreBands[1]), Number(userBands[1]), 'band 1'); + assert.approximately(Number(borrowMorePrices[0]), Number(userPrices[0]), 0.01, 'price 0'); + assert.approximately(Number(borrowMorePrices[1]), Number(userPrices[1]), 0.01, 'price 1'); + assert.approximately(Number(borrowMoreFullHealth), Number(fullHealth), 0.3, 'full health'); + assert.approximately(Number(borrowMoreHealth), Number(health), 0.3, 'health'); + assert.equal(Number(balances.collateral), Number(initialBalances.collateral) - Number(collateralAmount), 'wallet collateral'); + assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet borrowed'); + const collateralDiff = Number(state.collateral) - Number(initialState.collateral); + assert.isAtMost(Math.abs(collateralDiff - Number(totalCollateral)) / Number(totalCollateral), 0.01, 'state collateral'); + const debtDiff = Number(state.debt) - Number(initialState.debt); + assert.isAtMost(Math.abs(debtDiff - Number(debtAmount)) / Number(debtAmount), 1e-7, 'state debt'); + }); + }) +} + +describe('Leverage borrowMore test', async function () { + this.timeout(180000); + + before(async function () { + await lending.init('JsonRpc', {},{ gasPrice: 0 }, API_KEY_1INCH); + await lending.oneWayfactory.fetchMarkets(); + }); + + for (const oneWayMarketId of ONE_WAY_MARKETS) { + generalTest(oneWayMarketId); + } +}) diff --git a/test/leverageCreateLoan.test.ts b/test/leverageCreateLoan.test.ts new file mode 100644 index 0000000..a0c127e --- /dev/null +++ b/test/leverageCreateLoan.test.ts @@ -0,0 +1,232 @@ +import { assert } from "chai"; +import lending from "../src/index.js"; +import { API_KEY_1INCH } from "./rpcUrls.test.js"; +import { getOneWayMarket, OneWayMarketTemplate } from "../src/markets/index.js"; + + +const ONE_WAY_MARKETS = ['one-way-market-0']; + +const generalTest = (id: string) => { + describe(`${id} leverage createLoan test`, function () { + let oneWayMarket: OneWayMarketTemplate; + + before(async function () { + oneWayMarket = getOneWayMarket(id); + if (Number(await oneWayMarket.vault.totalLiquidity()) === 0) { + const maxDeposit = Number(await oneWayMarket.vault.maxDeposit()) * 0.7; + await oneWayMarket.vault.deposit(maxDeposit); + } + }); + + it('Leverage createLoan collateral only, debt too high', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isFalse(loanExists, "loanExists"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const collateralAmount = 0.5; + const borrowedAmount = 0; + const N = 10; + const { maxDebt } = await oneWayMarket.leverage.createLoanMaxRecv(collateralAmount, borrowedAmount, N); + const debtAmount = (Number(maxDebt) * 1.003).toFixed(oneWayMarket.borrowed_token.decimals); + + try { + await oneWayMarket.leverage.createLoan(collateralAmount, borrowedAmount, debtAmount, N, 1); + throw Error("Did not revert"); + } catch (e) { + // @ts-ignore + assert.notEqual(e.message, "Did not revert"); + // @ts-ignore + assert.isTrue(e.message.startsWith('execution reverted: "Debt too high"')); + } + }); + + it('Leverage createLoan borrowed only, debt too high', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isFalse(loanExists, "loanExists"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const collateralAmount = 0; + const borrowedAmount = 1000; + const N = 10; + const { maxDebt } = await oneWayMarket.leverage.createLoanMaxRecv(collateralAmount, borrowedAmount, N); + const debtAmount = (Number(maxDebt) * 1.003).toFixed(oneWayMarket.borrowed_token.decimals); + + try { + await oneWayMarket.leverage.createLoan(collateralAmount, borrowedAmount, debtAmount, N, 1); + throw Error("Did not revert"); + } catch (e) { + // @ts-ignore + assert.notEqual(e.message, "Did not revert"); + // @ts-ignore + assert.isTrue(e.message.startsWith('execution reverted: "Debt too high"')); + } + }); + + it('Leverage createLoan, debt too high', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isFalse(loanExists, "loanExists"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const collateralAmount = 0.5; + const borrowedAmount = 1000; + const N = 10; + const { maxDebt } = await oneWayMarket.leverage.createLoanMaxRecv(collateralAmount, borrowedAmount, N); + const debtAmount = (Number(maxDebt) * 1.003).toFixed(oneWayMarket.borrowed_token.decimals); + + try { + await oneWayMarket.leverage.createLoan(collateralAmount, borrowedAmount, debtAmount, N, 1); + throw Error("Did not revert"); + } catch (e) { + // @ts-ignore + assert.notEqual(e.message, "Did not revert"); + // @ts-ignore + assert.isTrue(e.message.startsWith('execution reverted: "Debt too high"')); + } + }); + + it('Leverage createLoan collateral only', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isFalse(loanExists, "loanExists"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const collateralAmount = 0.5; + const borrowedAmount = 0; + const N = 10; + const { maxDebt } = await oneWayMarket.leverage.createLoanMaxRecv(collateralAmount, borrowedAmount, N); + const debtAmount = (Number(maxDebt) / 2).toFixed(oneWayMarket.borrowed_token.decimals); + const createLoanBands = await oneWayMarket.leverage.createLoanBands(collateralAmount, borrowedAmount, debtAmount, N); + const createLoanPrices = await oneWayMarket.leverage.createLoanPrices(collateralAmount, borrowedAmount, debtAmount, N); + const createLoanFullHealth = await oneWayMarket.leverage.createLoanHealth(collateralAmount, borrowedAmount, debtAmount, N); + const createLoanHealth = await oneWayMarket.leverage.createLoanHealth(collateralAmount, borrowedAmount, debtAmount, N, false); + const { totalCollateral } = await oneWayMarket.leverage.createLoanExpectedCollateral(collateralAmount, borrowedAmount, debtAmount); + + await oneWayMarket.leverage.createLoan(collateralAmount, borrowedAmount, debtAmount, N, 1); + + const balances = await oneWayMarket.wallet.balances(); + const state = await oneWayMarket.userState(); + const userBands = await oneWayMarket.userBands(); + const userPrices = await oneWayMarket.userPrices(); + const fullHealth = await oneWayMarket.userHealth(); + const health = await oneWayMarket.userHealth(false); + + assert.equal(Number(createLoanBands[0]), Number(userBands[0]), 'band 0'); + assert.equal(Number(createLoanBands[1]), Number(userBands[1]), 'band 1'); + assert.approximately(Number(createLoanPrices[0]), Number(userPrices[0]), 0.01, 'price 0'); + assert.approximately(Number(createLoanPrices[1]), Number(userPrices[1]), 0.01, 'price 1'); + assert.approximately(Number(createLoanFullHealth), Number(fullHealth), 0.3, 'full health'); + assert.approximately(Number(createLoanHealth), Number(health), 0.3, 'health'); + assert.equal(Number(balances.collateral), Number(initialBalances.collateral) - Number(collateralAmount), 'wallet collateral'); + assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet borrowed'); + assert.isAtMost(Math.abs(Number(state.collateral) - Number(totalCollateral)) / Number(totalCollateral), 0.01, 'state collateral'); + assert.equal(Number(state.debt), Number(debtAmount), 'state debt'); + + await oneWayMarket.fullRepay(); + }); + + it('Leverage createLoan borrowed only', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isFalse(loanExists, "loanExists"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const collateralAmount = 0; + const borrowedAmount = 1000; + const N = 10; + const { maxDebt } = await oneWayMarket.leverage.createLoanMaxRecv(collateralAmount, borrowedAmount, N); + const debtAmount = (Number(maxDebt) / 2).toFixed(oneWayMarket.borrowed_token.decimals); + const createLoanBands = await oneWayMarket.leverage.createLoanBands(collateralAmount, borrowedAmount, debtAmount, N); + const createLoanPrices = await oneWayMarket.leverage.createLoanPrices(collateralAmount, borrowedAmount, debtAmount, N); + const createLoanFullHealth = await oneWayMarket.leverage.createLoanHealth(collateralAmount, borrowedAmount, debtAmount, N); + const createLoanHealth = await oneWayMarket.leverage.createLoanHealth(collateralAmount, borrowedAmount, debtAmount, N, false); + const { totalCollateral } = await oneWayMarket.leverage.createLoanExpectedCollateral(collateralAmount, borrowedAmount, debtAmount); + + await oneWayMarket.leverage.createLoan(collateralAmount, borrowedAmount, debtAmount, N, 1); + + const balances = await oneWayMarket.wallet.balances(); + const state = await oneWayMarket.userState(); + const userBands = await oneWayMarket.userBands(); + const userPrices = await oneWayMarket.userPrices(); + const fullHealth = await oneWayMarket.userHealth(); + const health = await oneWayMarket.userHealth(false); + + assert.equal(Number(createLoanBands[0]), Number(userBands[0]), 'band 0'); + assert.equal(Number(createLoanBands[1]), Number(userBands[1]), 'band 1'); + assert.approximately(Number(createLoanPrices[0]), Number(userPrices[0]), 0.01, 'price 0'); + assert.approximately(Number(createLoanPrices[1]), Number(userPrices[1]), 0.01, 'price 1'); + assert.approximately(Number(createLoanFullHealth), Number(fullHealth), 0.3, 'full health'); + assert.approximately(Number(createLoanHealth), Number(health), 0.3, 'health'); + assert.equal(Number(balances.collateral), Number(initialBalances.collateral) - Number(collateralAmount), 'wallet collateral'); + assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet borrowed'); + assert.isAtMost(Math.abs(Number(state.collateral) - Number(totalCollateral)) / Number(totalCollateral), 0.01, 'state collateral'); + assert.equal(Number(state.debt), Number(debtAmount), 'state debt'); + + await oneWayMarket.fullRepay(); + }); + + it('Leverage createLoan', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isFalse(loanExists, "loanExists"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const collateralAmount = 0.5; + const borrowedAmount = 1000; + const N = 10; + const { maxDebt: debtAmount } = await oneWayMarket.leverage.createLoanMaxRecv(collateralAmount, borrowedAmount, N); + const createLoanBands = await oneWayMarket.leverage.createLoanBands(collateralAmount, borrowedAmount, debtAmount, N); + const createLoanPrices = await oneWayMarket.leverage.createLoanPrices(collateralAmount, borrowedAmount, debtAmount, N); + const createLoanFullHealth = await oneWayMarket.leverage.createLoanHealth(collateralAmount, borrowedAmount, debtAmount, N); + const createLoanHealth = await oneWayMarket.leverage.createLoanHealth(collateralAmount, borrowedAmount, debtAmount, N, false); + const { totalCollateral } = await oneWayMarket.leverage.createLoanExpectedCollateral(collateralAmount, borrowedAmount, debtAmount); + + await oneWayMarket.leverage.createLoan(collateralAmount, borrowedAmount, debtAmount, N, 1); + + const balances = await oneWayMarket.wallet.balances(); + const state = await oneWayMarket.userState(); + const userBands = await oneWayMarket.userBands(); + const userPrices = await oneWayMarket.userPrices(); + const fullHealth = await oneWayMarket.userHealth(); + const health = await oneWayMarket.userHealth(false); + + assert.equal(Number(createLoanBands[0]), Number(userBands[0]), 'band 0'); + assert.equal(Number(createLoanBands[1]), Number(userBands[1]), 'band 1'); + assert.approximately(Number(createLoanPrices[0]), Number(userPrices[0]), 0.01, 'price 0'); + assert.approximately(Number(createLoanPrices[1]), Number(userPrices[1]), 0.01, 'price 1'); + assert.approximately(Number(createLoanFullHealth), Number(fullHealth), 0.3, 'full health'); + assert.approximately(Number(createLoanHealth), Number(health), 0.3, 'health'); + assert.equal(Number(balances.collateral), Number(initialBalances.collateral) - Number(collateralAmount), 'wallet collateral'); + assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet borrowed'); + assert.isAtMost(Math.abs(Number(state.collateral) - Number(totalCollateral)) / Number(totalCollateral), 0.01, 'state collateral'); + assert.equal(Number(state.debt), Number(debtAmount), 'state debt'); + }); + }) +} + +describe('Leverage createLoan test', async function () { + this.timeout(180000); + + before(async function () { + await lending.init('JsonRpc', {},{ gasPrice: 0 }, API_KEY_1INCH); + await lending.oneWayfactory.fetchMarkets(); + }); + + for (const oneWayMarketId of ONE_WAY_MARKETS) { + generalTest(oneWayMarketId); + } +}) diff --git a/test/leverageRepay.test.ts b/test/leverageRepay.test.ts new file mode 100644 index 0000000..3c672e3 --- /dev/null +++ b/test/leverageRepay.test.ts @@ -0,0 +1,235 @@ +import { assert } from "chai"; +import lending from "../src/index.js"; +import { API_KEY_1INCH } from "./rpcUrls.test.js"; +import { getOneWayMarket, OneWayMarketTemplate } from "../src/markets/index.js"; +import { BN } from "../src/utils.js"; + + +const ONE_WAY_MARKETS = ['one-way-market-0']; + +const generalTest = (id: string) => { + describe(`${id} leverage test`, function () { + let oneWayMarket: OneWayMarketTemplate; + + before(async function () { + oneWayMarket = getOneWayMarket(id); + if (Number(await oneWayMarket.vault.totalLiquidity()) === 0) { + const maxDeposit = Number(await oneWayMarket.vault.maxDeposit()) * 0.7; + await oneWayMarket.vault.deposit(maxDeposit); + } + if (!(await oneWayMarket.userLoanExists())) { + const collateralAmount = 0.5; + const borrowedAmount = 1000; + const N = 10; + const { maxDebt } = await oneWayMarket.leverage.createLoanMaxRecv(collateralAmount, borrowedAmount, N); + const debtAmount = (Number(maxDebt) / 2).toFixed(oneWayMarket.borrowed_token.decimals); + + await oneWayMarket.leverage.createLoan(collateralAmount, borrowedAmount, debtAmount, N, 1); + } + }); + + it('Leverage repay state collateral only', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const initialState = await oneWayMarket.userState(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isTrue(loanExists, "loanExists"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const stateCollateralAmount = 0.2; + const collateralAmount = 0; + const borrowedAmount = 0; + const repayBands = await oneWayMarket.leverage.repayBands(stateCollateralAmount, collateralAmount, borrowedAmount); + const repayPrices = await oneWayMarket.leverage.repayPrices(stateCollateralAmount, collateralAmount, borrowedAmount); + const repayFullHealth = await oneWayMarket.leverage.repayHealth(stateCollateralAmount, collateralAmount, borrowedAmount); + const repayHealth = await oneWayMarket.leverage.repayHealth(stateCollateralAmount, collateralAmount, borrowedAmount, false); + const { totalBorrowed } = await oneWayMarket.leverage.repayExpectedBorrowed(stateCollateralAmount, collateralAmount, borrowedAmount); + + await oneWayMarket.leverage.repay(stateCollateralAmount, collateralAmount, borrowedAmount, 1); + + const balances = await oneWayMarket.wallet.balances(); + const state = await oneWayMarket.userState(); + const userBands = await oneWayMarket.userBands(); + const userPrices = await oneWayMarket.userPrices(); + const fullHealth = await oneWayMarket.userHealth(); + const health = await oneWayMarket.userHealth(false); + + assert.equal(Number(repayBands[0]), Number(userBands[0]), 'band 0'); + assert.equal(Number(repayBands[1]), Number(userBands[1]), 'band 1'); + assert.approximately(Number(repayPrices[0]), Number(userPrices[0]), 1e-2, 'price 0'); + assert.approximately(Number(repayPrices[1]), Number(userPrices[1]), 1e-2, 'price 1'); + assert.approximately(Number(repayFullHealth), Number(fullHealth), 0.1, 'full health'); + assert.approximately(Number(repayHealth), Number(health), 0.1, 'health'); + assert.equal(Number(balances.collateral), Number(initialBalances.collateral) - Number(collateralAmount), 'wallet collateral'); + assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet borrowed'); + assert.deepStrictEqual(BN(state.collateral), BN(initialState.collateral).minus(stateCollateralAmount),'state collateral'); + const debtDiff = Number(initialState.debt) - Number(state.debt); + assert.isAtMost(Math.abs(debtDiff - Number(totalBorrowed)) / Number(totalBorrowed), 0.01, 'state debt'); + }); + + it('Leverage repay user collateral only', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const initialState = await oneWayMarket.userState(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isTrue(loanExists, "loanExists"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const stateCollateralAmount = 0; + const collateralAmount = 0.2; + const borrowedAmount = 0; + const repayBands = await oneWayMarket.leverage.repayBands(stateCollateralAmount, collateralAmount, borrowedAmount); + const repayPrices = await oneWayMarket.leverage.repayPrices(stateCollateralAmount, collateralAmount, borrowedAmount); + const repayFullHealth = await oneWayMarket.leverage.repayHealth(stateCollateralAmount, collateralAmount, borrowedAmount); + const repayHealth = await oneWayMarket.leverage.repayHealth(stateCollateralAmount, collateralAmount, borrowedAmount, false); + const { totalBorrowed } = await oneWayMarket.leverage.repayExpectedBorrowed(stateCollateralAmount, collateralAmount, borrowedAmount); + + await oneWayMarket.leverage.repay(stateCollateralAmount, collateralAmount, borrowedAmount, 1); + + const balances = await oneWayMarket.wallet.balances(); + const state = await oneWayMarket.userState(); + const userBands = await oneWayMarket.userBands(); + const userPrices = await oneWayMarket.userPrices(); + const fullHealth = await oneWayMarket.userHealth(); + const health = await oneWayMarket.userHealth(false); + + assert.equal(Number(repayBands[0]), Number(userBands[0]), 'band 0'); + assert.equal(Number(repayBands[1]), Number(userBands[1]), 'band 1'); + assert.approximately(Number(repayPrices[0]), Number(userPrices[0]), 1e-2, 'price 0'); + assert.approximately(Number(repayPrices[1]), Number(userPrices[1]), 1e-2, 'price 1'); + assert.approximately(Number(repayFullHealth), Number(fullHealth), 0.1, 'full health'); + assert.approximately(Number(repayHealth), Number(health), 0.1, 'health'); + assert.deepStrictEqual(BN(balances.collateral), BN(initialBalances.collateral).minus(collateralAmount), 'wallet collateral'); + assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet borrowed'); + assert.equal(Number(state.collateral), Number(initialState.collateral) - Number(stateCollateralAmount), 'state collateral'); + const debtDiff = Number(initialState.debt) - Number(state.debt); + assert.isAtMost(Math.abs(debtDiff - Number(totalBorrowed)) / Number(totalBorrowed), 0.01, 'state debt'); + }); + + it('Leverage repay user borrowed only', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const initialState = await oneWayMarket.userState(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isTrue(loanExists, "loanExists"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const stateCollateralAmount = 0; + const collateralAmount = 0; + const borrowedAmount = 500; + const repayBands = await oneWayMarket.leverage.repayBands(stateCollateralAmount, collateralAmount, borrowedAmount); + const repayPrices = await oneWayMarket.leverage.repayPrices(stateCollateralAmount, collateralAmount, borrowedAmount); + const repayFullHealth = await oneWayMarket.leverage.repayHealth(stateCollateralAmount, collateralAmount, borrowedAmount); + const repayHealth = await oneWayMarket.leverage.repayHealth(stateCollateralAmount, collateralAmount, borrowedAmount, false); + const { totalBorrowed } = await oneWayMarket.leverage.repayExpectedBorrowed(stateCollateralAmount, collateralAmount, borrowedAmount); + + await oneWayMarket.leverage.repay(stateCollateralAmount, collateralAmount, borrowedAmount, 1); + + const balances = await oneWayMarket.wallet.balances(); + const state = await oneWayMarket.userState(); + const userBands = await oneWayMarket.userBands(); + const userPrices = await oneWayMarket.userPrices(); + const fullHealth = await oneWayMarket.userHealth(); + const health = await oneWayMarket.userHealth(false); + + assert.equal(Number(repayBands[0]), Number(userBands[0]), 'band 0'); + assert.equal(Number(repayBands[1]), Number(userBands[1]), 'band 1'); + assert.approximately(Number(repayPrices[0]), Number(userPrices[0]), 1e-2, 'price 0'); + assert.approximately(Number(repayPrices[1]), Number(userPrices[1]), 1e-2, 'price 1'); + assert.approximately(Number(repayFullHealth), Number(fullHealth), 0.1, 'full health'); + assert.approximately(Number(repayHealth), Number(health), 0.1, 'health'); + assert.deepStrictEqual(BN(balances.collateral), BN(initialBalances.collateral).minus(collateralAmount), 'wallet collateral'); + assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet borrowed'); + assert.equal(Number(state.collateral), Number(initialState.collateral) - Number(stateCollateralAmount), 'state collateral'); + assert.equal(borrowedAmount, Number(totalBorrowed), 'borrowed amount'); + const debtDiff = Number(initialState.debt) - Number(state.debt); + assert.isAtMost(Math.abs(debtDiff - Number(totalBorrowed)) / Number(totalBorrowed), 1e-7, 'state debt'); + }); + + it('Leverage repay', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const initialState = await oneWayMarket.userState(); + const loanExists = await oneWayMarket.userLoanExists(); + + assert.isTrue(loanExists, "loanExists"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const stateCollateralAmount = 0.3; + const collateralAmount = 0.2; + const borrowedAmount = 500; + const repayBands = await oneWayMarket.leverage.repayBands(stateCollateralAmount, collateralAmount, borrowedAmount); + const repayPrices = await oneWayMarket.leverage.repayPrices(stateCollateralAmount, collateralAmount, borrowedAmount); + const repayFullHealth = await oneWayMarket.leverage.repayHealth(stateCollateralAmount, collateralAmount, borrowedAmount); + const repayHealth = await oneWayMarket.leverage.repayHealth(stateCollateralAmount, collateralAmount, borrowedAmount, false); + const { totalBorrowed } = await oneWayMarket.leverage.repayExpectedBorrowed(stateCollateralAmount, collateralAmount, borrowedAmount); + + await oneWayMarket.leverage.repay(stateCollateralAmount, collateralAmount, borrowedAmount, 1); + + const balances = await oneWayMarket.wallet.balances(); + const state = await oneWayMarket.userState(); + const userBands = await oneWayMarket.userBands(); + const userPrices = await oneWayMarket.userPrices(); + const fullHealth = await oneWayMarket.userHealth(); + const health = await oneWayMarket.userHealth(false); + + assert.equal(Number(repayBands[0]), Number(userBands[0]), 'band 0'); + assert.equal(Number(repayBands[1]), Number(userBands[1]), 'band 1'); + assert.approximately(Number(repayPrices[0]), Number(userPrices[0]), 1e-2, 'price 0'); + assert.approximately(Number(repayPrices[1]), Number(userPrices[1]), 1e-2, 'price 1'); + assert.approximately(Number(repayFullHealth), Number(fullHealth), 0.1, 'full health'); + assert.approximately(Number(repayHealth), Number(health), 0.1, 'health'); + assert.equal(Number(balances.collateral), Number(initialBalances.collateral) - Number(collateralAmount), 'wallet collateral'); + assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet borrowed'); + assert.equal(Number(state.collateral), Number(initialState.collateral) - Number(stateCollateralAmount), 'state collateral'); + const debtDiff = Number(initialState.debt) - Number(state.debt); + assert.isAtMost(Math.abs(debtDiff - Number(totalBorrowed)) / Number(totalBorrowed), 0.01, 'state debt'); + }); + + it('Leverage full repay', async function () { + const initialBalances = await oneWayMarket.wallet.balances(); + const initialState = await oneWayMarket.userState(); + let loanExists = await oneWayMarket.userLoanExists(); + + assert.isTrue(loanExists, "loan does not exist"); + assert.isAbove(Number(initialBalances.collateral), 0, "collateral > 0"); + assert.isAbove(Number(initialBalances.borrowed), 0, "borrowed > 0"); + + const stateCollateralAmount = (Number(initialState.collateral) / 3).toFixed(oneWayMarket.collateral_token.decimals); + const collateralAmount = (Number(initialState.collateral) / 3).toFixed(oneWayMarket.collateral_token.decimals); + const borrowedAmount = (Number(initialState.debt) / 3).toFixed(oneWayMarket.borrowed_token.decimals); + const { totalBorrowed } = await oneWayMarket.leverage.repayExpectedBorrowed(stateCollateralAmount, collateralAmount, borrowedAmount); + + await oneWayMarket.leverage.repay(stateCollateralAmount, collateralAmount, borrowedAmount, 1); + + const balances = await oneWayMarket.wallet.balances(); + const state = await oneWayMarket.userState(); + loanExists = await oneWayMarket.userLoanExists(); + + assert.isFalse(loanExists, "loan still exists"); + assert.approximately(Number(balances.collateral), Number(initialBalances.collateral) + (Number(initialState.collateral) / 3), 1e-6, 'wallet collateral'); + const borrowedDiff = (Number(balances.borrowed) + Number(initialState.debt)) - (Number(initialBalances.borrowed) - Number(borrowedAmount)); + assert.isTrue(borrowedDiff > 0); // Same sign + assert.isAtMost(Math.abs(borrowedDiff - Number(totalBorrowed)) / Math.abs(borrowedDiff), 0.01, 'wallet borrowed'); + assert.equal(Number(state.collateral), 0, 'state collateral'); + assert.equal(Number(state.borrowed), 0, 'state borrowed'); + assert.equal(Number(state.debt), 0, 'state debt'); + }); + }) +} + +describe('Leverage test', async function () { + this.timeout(180000); + + before(async function () { + await lending.init('JsonRpc', {},{ gasPrice: 0 }, API_KEY_1INCH); + await lending.oneWayfactory.fetchMarkets(); + }); + + for (const oneWayMarketId of ONE_WAY_MARKETS) { + generalTest(oneWayMarketId); + } +}) From 22d7e1fc2169a69e4464d2786d67e036f2c63163 Mon Sep 17 00:00:00 2001 From: macket Date: Tue, 23 Apr 2024 13:15:04 +0400 Subject: [PATCH 27/36] fix: borrowed only repay --- src/markets/OneWayMarketTemplate.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index c57e83a..f58ac85 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -2399,7 +2399,8 @@ export class OneWayMarketTemplate { for (let i = 0; i < 5; i++) { maxBorrowablePrevBN = maxBorrowableBN; _userEffectiveCollateral = _userCollateral + fromBN(BN(userBorrowed).div(pAvgBN), this.collateral_token.decimals); - const _maxBorrowable = await contract.max_borrowable(this.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral, _N, fromBN(pAvgBN)); + let _maxBorrowable = await contract.max_borrowable(this.addresses.controller, _userEffectiveCollateral, _maxLeverageCollateral, _N, fromBN(pAvgBN)); + _maxBorrowable = _maxBorrowable * BigInt(999) / BigInt(1000); // Revert happens if I don't do this and try to borrow max if (_maxBorrowable === BigInt(0)) break; maxBorrowableBN = toBN(_maxBorrowable, this.borrowed_token.decimals); @@ -2517,9 +2518,14 @@ export class OneWayMarketTemplate { this._checkLeverageZap(); const _stateCollateral = parseUnits(stateCollateral, this.collateral_token.decimals); const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); - const _borrowedExpected = BigInt(await _getQuote1inch(this.addresses.collateral_token, this.addresses.borrowed_token, _stateCollateral + _userCollateral)); - const _borrowedFromStateCollateral = _stateCollateral * BigInt(10**18) / (_stateCollateral + _userCollateral) * _borrowedExpected / BigInt(10**18); - const _borrowedFromUserCollateral = _borrowedExpected - _borrowedFromStateCollateral; + let _borrowedExpected = BigInt(0); + let _borrowedFromStateCollateral = BigInt(0); + let _borrowedFromUserCollateral = BigInt(0); + if (_stateCollateral + _userCollateral > BigInt(0)) { + _borrowedExpected = BigInt(await _getQuote1inch(this.addresses.collateral_token, this.addresses.borrowed_token, _stateCollateral + _userCollateral)); + _borrowedFromStateCollateral = _stateCollateral * BigInt(10 ** 18) / (_stateCollateral + _userCollateral) * _borrowedExpected / BigInt(10 ** 18); + _borrowedFromUserCollateral = _borrowedExpected - _borrowedFromStateCollateral; + } const _totalBorrowed = _borrowedExpected + parseUnits(userBorrowed, this.borrowed_token.decimals); return { @@ -2655,9 +2661,12 @@ export class OneWayMarketTemplate { ): Promise { if (!(await this.userLoanExists())) throw Error("Loan does not exist"); const _stateCollateral = parseUnits(stateCollateral, this.collateral_token.decimals); - const _userCollateral = parseUnits(userCollateral, this.borrowed_token.decimals); + const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); - const calldata = await _getCalldata1inch(this.addresses.collateral_token, this.addresses.borrowed_token, _stateCollateral + _userCollateral, slippage); + let calldata = "0x"; + if (_stateCollateral + _userCollateral > BigInt(0)) { + calldata = await _getCalldata1inch(this.addresses.collateral_token, this.addresses.borrowed_token, _stateCollateral + _userCollateral, slippage); + } const contract = lending.contracts[this.addresses.controller].contract; const gas = await contract.repay_extended.estimateGas( lending.constants.ALIASES.leverage_zap, From b88b2bccea368be16f72902d03c80209fd962793 Mon Sep 17 00:00:00 2001 From: macket Date: Thu, 25 Apr 2024 15:54:17 +0400 Subject: [PATCH 28/36] test: precision issues --- test/leverageCreateLoan.test.ts | 6 +++--- test/leverageRepay.test.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/leverageCreateLoan.test.ts b/test/leverageCreateLoan.test.ts index a0c127e..56928c6 100644 --- a/test/leverageCreateLoan.test.ts +++ b/test/leverageCreateLoan.test.ts @@ -30,7 +30,7 @@ const generalTest = (id: string) => { const borrowedAmount = 0; const N = 10; const { maxDebt } = await oneWayMarket.leverage.createLoanMaxRecv(collateralAmount, borrowedAmount, N); - const debtAmount = (Number(maxDebt) * 1.003).toFixed(oneWayMarket.borrowed_token.decimals); + const debtAmount = (Number(maxDebt) * 1.004).toFixed(oneWayMarket.borrowed_token.decimals); try { await oneWayMarket.leverage.createLoan(collateralAmount, borrowedAmount, debtAmount, N, 1); @@ -55,7 +55,7 @@ const generalTest = (id: string) => { const borrowedAmount = 1000; const N = 10; const { maxDebt } = await oneWayMarket.leverage.createLoanMaxRecv(collateralAmount, borrowedAmount, N); - const debtAmount = (Number(maxDebt) * 1.003).toFixed(oneWayMarket.borrowed_token.decimals); + const debtAmount = (Number(maxDebt) * 1.004).toFixed(oneWayMarket.borrowed_token.decimals); try { await oneWayMarket.leverage.createLoan(collateralAmount, borrowedAmount, debtAmount, N, 1); @@ -80,7 +80,7 @@ const generalTest = (id: string) => { const borrowedAmount = 1000; const N = 10; const { maxDebt } = await oneWayMarket.leverage.createLoanMaxRecv(collateralAmount, borrowedAmount, N); - const debtAmount = (Number(maxDebt) * 1.003).toFixed(oneWayMarket.borrowed_token.decimals); + const debtAmount = (Number(maxDebt) * 1.004).toFixed(oneWayMarket.borrowed_token.decimals); try { await oneWayMarket.leverage.createLoan(collateralAmount, borrowedAmount, debtAmount, N, 1); diff --git a/test/leverageRepay.test.ts b/test/leverageRepay.test.ts index 3c672e3..67616c3 100644 --- a/test/leverageRepay.test.ts +++ b/test/leverageRepay.test.ts @@ -8,7 +8,7 @@ import { BN } from "../src/utils.js"; const ONE_WAY_MARKETS = ['one-way-market-0']; const generalTest = (id: string) => { - describe(`${id} leverage test`, function () { + describe(`${id} leverage repay test`, function () { let oneWayMarket: OneWayMarketTemplate; before(async function () { @@ -184,7 +184,7 @@ const generalTest = (id: string) => { assert.approximately(Number(repayHealth), Number(health), 0.1, 'health'); assert.equal(Number(balances.collateral), Number(initialBalances.collateral) - Number(collateralAmount), 'wallet collateral'); assert.equal(Number(balances.borrowed), Number(initialBalances.borrowed) - borrowedAmount, 'wallet borrowed'); - assert.equal(Number(state.collateral), Number(initialState.collateral) - Number(stateCollateralAmount), 'state collateral'); + assert.deepStrictEqual(BN(state.collateral), BN(initialState.collateral).minus(stateCollateralAmount), 'state collateral'); const debtDiff = Number(initialState.debt) - Number(state.debt); assert.isAtMost(Math.abs(debtDiff - Number(totalBorrowed)) / Number(totalBorrowed), 0.01, 'state debt'); }); @@ -221,7 +221,7 @@ const generalTest = (id: string) => { }) } -describe('Leverage test', async function () { +describe('Leverage repay test', async function () { this.timeout(180000); before(async function () { From e37b1e4411d3eaa9b57aa134a451680885e17314 Mon Sep 17 00:00:00 2001 From: macket Date: Thu, 25 Apr 2024 15:55:10 +0400 Subject: [PATCH 29/36] chore: one_way_factory and leverage_zap test addresses --- src/constants/aliases.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/constants/aliases.ts b/src/constants/aliases.ts index eb0f373..f4d873b 100644 --- a/src/constants/aliases.ts +++ b/src/constants/aliases.ts @@ -37,10 +37,10 @@ export const ALIASES_AVALANCHE = lowerCaseValues({ export const ALIASES_ARBITRUM = lowerCaseValues({ "crv": "0x11cDb42B0EB46D95f990BeDD4695A6e3fA034978", // "one_way_factory": "0xcaEC110C784c9DF37240a8Ce096D352A75922DeA", // REAL - "one_way_factory": "0x19010d0f5D5a88aC609B568c91057679eed643d3", // TEST + "one_way_factory": "0xb3adda9b28f0f117FD5b6eFE7f0a0cd662Dba5D6", // TEST "gauge_controller": "0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB", "gauge_factory": "0xabC000d88f23Bb45525E447528DBF656A9D55bf5", - "leverage_zap": "0xE1834AF57923059B4306B468013262D73F344D4E", // TEST + "leverage_zap": "0xA233e77AE47bf839Fd1c1d30f9EAa0584737376a", // TEST }); export const ALIASES_OPTIMISM = lowerCaseValues({ From d8563bedcd49621fd7978720b6a61ca8733d6ad6 Mon Sep 17 00:00:00 2001 From: macket Date: Thu, 25 Apr 2024 18:48:41 +0400 Subject: [PATCH 30/36] feat: leverageCreateLoanRoute --- src/external-api.ts | 8 ++++---- src/interfaces.ts | 5 +++++ src/markets/OneWayMarketTemplate.ts | 13 +++++++++++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/external-api.ts b/src/external-api.ts index c1e03c3..7e910d0 100644 --- a/src/external-api.ts +++ b/src/external-api.ts @@ -1,7 +1,7 @@ import axios from "axios"; import memoize from "memoizee"; import { lending } from "./lending.js"; -import { IExtendedPoolDataFromApi, INetworkName, IPoolFactory, T1inchRouteStep } from "./interfaces"; +import { IExtendedPoolDataFromApi, INetworkName, IPoolFactory, I1inchRoute } from "./interfaces"; export const _getPoolsFromApi = memoize( @@ -63,7 +63,7 @@ export const _getQuote1inch = memoize( ) const _getSwapData1inch = memoize( - async (fromToken: string, toToken: string, _amount: bigint, slippage: number): Promise<{ tx: { data: string }, protocols: T1inchRouteStep[][] }> => { + async (fromToken: string, toToken: string, _amount: bigint, slippage: number): Promise<{ tx: { data: string }, protocols: I1inchRoute[] }> => { if (_amount === BigInt(0)) throw Error("Amount must be > 0"); const url = `https://api.1inch.dev/swap/v6.0/${lending.chainId}/swap?src=${fromToken}&dst=${toToken}&amount=${_amount}&from=${lending.constants.ALIASES.leverage_zap}&slippage=${slippage}&protocols=${lending.constants.PROTOCOLS_1INCH}&includeTokensInfo=true&includeProtocols=true&disableEstimate=true`; const response = await axios.get( @@ -84,12 +84,12 @@ const _getSwapData1inch = memoize( } ) -export const _getCalldata1inch = async (fromToken: string, toToken: string, _amount: bigint, slippage: number) => { +export const _getCalldata1inch = async (fromToken: string, toToken: string, _amount: bigint, slippage: number): Promise => { const data = await _getSwapData1inch(fromToken, toToken, _amount, slippage); return data.tx.data; } -export const _getRoute1inch = async (fromToken: string, toToken: string, _amount: bigint, slippage: number) => { +export const _getRoute1inch = async (fromToken: string, toToken: string, _amount: bigint, slippage: number): Promise => { const data = await _getSwapData1inch(fromToken, toToken, _amount, slippage); return data.protocols; } diff --git a/src/interfaces.ts b/src/interfaces.ts index ef20633..196b3f0 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -129,3 +129,8 @@ export type T1inchRouteStep = { fromTokenAddress: string, toTokenAddress: string, }[] + +export interface I1inchRoute { + part: number, + hops: T1inchRouteStep[], +} diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index f58ac85..9335d5c 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -22,8 +22,8 @@ import { DIGas, smartNumber, } from "../utils.js"; -import { IDict, TGas, TAmount, IReward } from "../interfaces.js"; -import { _getQuote1inch, _getCalldata1inch } from "../external-api.js"; +import { IDict, TGas, TAmount, IReward, I1inchRoute } from "../interfaces.js"; +import { _getQuote1inch, _getCalldata1inch, _getRoute1inch } from "../external-api.js"; import ERC20Abi from '../constants/abis/ERC20.json' assert { type: 'json' }; @@ -181,6 +181,7 @@ export class OneWayMarketTemplate { createLoanHealth: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, full?: boolean) => Promise, createLoanIsApproved: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, createLoanApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + createLoanRoute: (userBorrowed: TAmount, debt: TAmount, slippage?: number) => Promise, createLoan: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number, slippage?: number) => Promise, borrowMoreMaxRecv: (userCollateral: TAmount, userBorrowed: TAmount, address?: string) => @@ -330,6 +331,7 @@ export class OneWayMarketTemplate { createLoanHealth: this.leverageCreateLoanHealth.bind(this), createLoanIsApproved: this.leverageCreateLoanIsApproved.bind(this), createLoanApprove: this.leverageCreateLoanApprove.bind(this), + createLoanRoute: this.leverageCreateLoanRoute.bind(this), createLoan: this.leverageCreateLoan.bind(this), borrowMoreMaxRecv: this.leverageBorrowMoreMaxRecv.bind(this), @@ -2314,6 +2316,13 @@ export class OneWayMarketTemplate { return [...collateralApproveTx, ...borrowedApproveTx] } + private async leverageCreateLoanRoute(userBorrowed: TAmount, debt: TAmount, slippage = 0.1): Promise { + const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); + const _debt = parseUnits(debt, this.borrowed_token.decimals); + + return await _getRoute1inch(this.addresses.borrowed_token, this.addresses.collateral_token, _debt + _userBorrowed, slippage); + } + private async _leverageCreateLoan( userCollateral: TAmount, userBorrowed: TAmount, From 8133b6dbeffa0522db990cb5ff3d7743a5011275 Mon Sep 17 00:00:00 2001 From: macket Date: Thu, 25 Apr 2024 19:29:18 +0400 Subject: [PATCH 31/36] fix: leverage calculation --- src/markets/OneWayMarketTemplate.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 9335d5c..c9f66dd 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -2140,7 +2140,8 @@ export class OneWayMarketTemplate { userCollateral: formatUnits(_userCollateral, this.collateral_token.decimals), collateralFromUserBorrowed: formatUnits(_collateralFromUserBorrowed, this.collateral_token.decimals), collateralFromDebt: formatUnits(_collateralFromDebt, this.collateral_token.decimals), - leverage: toBN(_collateralFromDebt, this.collateral_token.decimals).div(toBN(_userCollateral + _collateralFromUserBorrowed, this.collateral_token.decimals)).toString(), + leverage: toBN(_collateralFromDebt + _userCollateral + _collateralFromUserBorrowed, this.collateral_token.decimals) + .div(toBN(_userCollateral + _collateralFromUserBorrowed, this.collateral_token.decimals)).toString(), } } From eb2c28a406c0f79cec24f776e596371efdda2199 Mon Sep 17 00:00:00 2001 From: macket Date: Thu, 25 Apr 2024 19:37:23 +0400 Subject: [PATCH 32/36] feat: leverageBorrowMoreRoute --- src/markets/OneWayMarketTemplate.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index c9f66dd..9f3205d 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -200,6 +200,7 @@ export class OneWayMarketTemplate { borrowMoreHealth: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, full?: boolean, address?: string) => Promise, borrowMoreIsApproved: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, borrowMoreApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + borrowMoreRoute: (userBorrowed: TAmount, debt: TAmount, slippage?: number) => Promise, borrowMore: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, slippage?: number) => Promise, repayExpectedBorrowed: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount) => @@ -341,6 +342,7 @@ export class OneWayMarketTemplate { borrowMoreHealth: this.leverageBorrowMoreHealth.bind(this), borrowMoreIsApproved: this.leverageCreateLoanIsApproved.bind(this), borrowMoreApprove: this.leverageCreateLoanApprove.bind(this), + borrowMoreRoute: this.leverageBorrowMoreRoute.bind(this), borrowMore: this.leverageBorrowMore.bind(this), repayExpectedBorrowed: this.leverageRepayExpectedBorrowed.bind(this), @@ -2473,6 +2475,13 @@ export class OneWayMarketTemplate { return await this._leverageHealth(userCollateral, userBorrowed, dDebt, -1, full, address); } + private async leverageBorrowMoreRoute(userBorrowed: TAmount, debt: TAmount, slippage = 0.1): Promise { + const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); + const _debt = parseUnits(debt, this.borrowed_token.decimals); + + return await _getRoute1inch(this.addresses.borrowed_token, this.addresses.collateral_token, _debt + _userBorrowed, slippage); + } + private async _leverageBorrowMore( userCollateral: TAmount, userBorrowed: TAmount, From bea9921a009e746bcd092c03d546ed49a5302985 Mon Sep 17 00:00:00 2001 From: macket Date: Thu, 25 Apr 2024 20:10:05 +0400 Subject: [PATCH 33/36] feat: leverageRepayRoute --- src/markets/OneWayMarketTemplate.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 9f3205d..5a0ed2e 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -212,6 +212,7 @@ export class OneWayMarketTemplate { repayHealth: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, full?: boolean, address?: string) => Promise, repayIsApproved: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, repayApprove: (userCollateral: TAmount, userBorrowed: TAmount) => Promise, + repayRoute: (stateCollateral: TAmount, userCollateral: TAmount, slippage?: number) => Promise, repay: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, slippage?: number) => Promise, estimateGas: { @@ -353,6 +354,7 @@ export class OneWayMarketTemplate { repayHealth: this.leverageRepayHealth.bind(this), repayIsApproved: this.leverageRepayIsApproved.bind(this), repayApprove: this.leverageRepayApprove.bind(this), + repayRoute: this.leverageRepayRoute.bind(this), repay: this.leverageRepay.bind(this), estimateGas: { @@ -2671,6 +2673,13 @@ export class OneWayMarketTemplate { ); } + private async leverageRepayRoute(stateCollateral: TAmount, userCollateral: TAmount, slippage = 0.1): Promise { + const _stateCollateral = parseUnits(stateCollateral, this.collateral_token.decimals); + const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); + + return await _getRoute1inch(this.addresses.collateral_token, this.addresses.borrowed_token, _stateCollateral + _userCollateral, slippage); + } + private async _leverageRepay( stateCollateral: TAmount, userCollateral: TAmount, From 4afbdb3c8b02de06c4b6cc60ad6e8b54a70592e1 Mon Sep 17 00:00:00 2001 From: macket Date: Fri, 26 Apr 2024 12:53:00 +0400 Subject: [PATCH 34/36] feat: return avgPrice from leverage expected methods --- src/markets/OneWayMarketTemplate.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/markets/OneWayMarketTemplate.ts b/src/markets/OneWayMarketTemplate.ts index 5a0ed2e..18f5386 100644 --- a/src/markets/OneWayMarketTemplate.ts +++ b/src/markets/OneWayMarketTemplate.ts @@ -172,7 +172,7 @@ export class OneWayMarketTemplate { avgPrice: string, }>>, createLoanExpectedCollateral: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => - Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, leverage: string }>, + Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, leverage: string, avgPrice: string }>, createLoanMaxRange: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise, createLoanBands: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, range: number) => Promise<[number, number]>, createLoanBandsAllRanges: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount) => Promise>, @@ -194,7 +194,7 @@ export class OneWayMarketTemplate { avgPrice: string, }>, borrowMoreExpectedCollateral: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => - Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string }>, + Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, avgPrice: string }>, borrowMoreBands: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise<[number, number]>, borrowMorePrices: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address?: string) => Promise, borrowMoreHealth: (userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, full?: boolean, address?: string) => Promise, @@ -204,7 +204,7 @@ export class OneWayMarketTemplate { borrowMore: (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, slippage?: number) => Promise, repayExpectedBorrowed: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount) => - Promise<{ totalBorrowed: string, borrowedFromStateCollateral: string, borrowedFromUserCollateral: string, userBorrowed: string }>, + Promise<{ totalBorrowed: string, borrowedFromStateCollateral: string, borrowedFromUserCollateral: string, userBorrowed: string, avgPrice: string }>, repayIsFull: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, repayIsAvailable: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise, repayBands: (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount, address?: string) => Promise<[number, number]>, @@ -2111,7 +2111,8 @@ export class OneWayMarketTemplate { }); private _leverageExpectedCollateral = memoize(async (userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount, user?: string): - Promise<{ _futureStateCollateral: bigint, _totalCollateral: bigint, _userCollateral: bigint, _collateralFromUserBorrowed: bigint, _collateralFromDebt: bigint }> => { + Promise<{ _futureStateCollateral: bigint, _totalCollateral: bigint, _userCollateral: bigint, + _collateralFromUserBorrowed: bigint, _collateralFromDebt: bigint, avgPrice: string }> => { const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); const _debt = parseUnits(debt, this.borrowed_token.decimals); const _userBorrowed = parseUnits(userBorrowed, this.borrowed_token.decimals); @@ -2127,8 +2128,9 @@ export class OneWayMarketTemplate { } const _totalCollateral = _userCollateral + _additionalCollateral; const _futureStateCollateral = _stateCollateral + _totalCollateral; + const avgPrice = toBN(_debt + _userBorrowed, this.borrowed_token.decimals).div(toBN(_additionalCollateral, this.collateral_token.decimals)).toString(); - return { _futureStateCollateral, _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt }; + return { _futureStateCollateral, _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, avgPrice }; }, { promise: true, @@ -2136,8 +2138,8 @@ export class OneWayMarketTemplate { }); private async leverageCreateLoanExpectedCollateral(userCollateral: TAmount, userBorrowed: TAmount, debt: TAmount): - Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, leverage: string }> { - const { _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt } = + Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, leverage: string, avgPrice: string }> { + const { _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, avgPrice } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, debt); return { totalCollateral: formatUnits(_totalCollateral, this.collateral_token.decimals), @@ -2146,6 +2148,7 @@ export class OneWayMarketTemplate { collateralFromDebt: formatUnits(_collateralFromDebt, this.collateral_token.decimals), leverage: toBN(_collateralFromDebt + _userCollateral + _collateralFromUserBorrowed, this.collateral_token.decimals) .div(toBN(_userCollateral + _collateralFromUserBorrowed, this.collateral_token.decimals)).toString(), + avgPrice, } } @@ -2444,15 +2447,16 @@ export class OneWayMarketTemplate { } private async leverageBorrowMoreExpectedCollateral(userCollateral: TAmount, userBorrowed: TAmount, dDebt: TAmount, address = ""): - Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string }> { + Promise<{ totalCollateral: string, userCollateral: string, collateralFromUserBorrowed: string, collateralFromDebt: string, avgPrice: string }> { address = _getAddress(address); - const { _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt } = + const { _totalCollateral, _userCollateral, _collateralFromUserBorrowed, _collateralFromDebt, avgPrice } = await this._leverageExpectedCollateral(userCollateral, userBorrowed, dDebt, address); return { totalCollateral: formatUnits(_totalCollateral, this.collateral_token.decimals), userCollateral: formatUnits(_userCollateral, this.collateral_token.decimals), collateralFromUserBorrowed: formatUnits(_collateralFromUserBorrowed, this.collateral_token.decimals), collateralFromDebt: formatUnits(_collateralFromDebt, this.collateral_token.decimals), + avgPrice, } } @@ -2535,7 +2539,7 @@ export class OneWayMarketTemplate { // ---------------- LEVERAGE REPAY ---------------- private leverageRepayExpectedBorrowed = memoize( async (stateCollateral: TAmount, userCollateral: TAmount, userBorrowed: TAmount): - Promise<{ totalBorrowed: string, borrowedFromStateCollateral: string, borrowedFromUserCollateral: string, userBorrowed: string }> => { + Promise<{ totalBorrowed: string, borrowedFromStateCollateral: string, borrowedFromUserCollateral: string, userBorrowed: string, avgPrice: string }> => { this._checkLeverageZap(); const _stateCollateral = parseUnits(stateCollateral, this.collateral_token.decimals); const _userCollateral = parseUnits(userCollateral, this.collateral_token.decimals); @@ -2548,12 +2552,14 @@ export class OneWayMarketTemplate { _borrowedFromUserCollateral = _borrowedExpected - _borrowedFromStateCollateral; } const _totalBorrowed = _borrowedExpected + parseUnits(userBorrowed, this.borrowed_token.decimals); + const avgPrice = toBN(_borrowedExpected, this.borrowed_token.decimals).div(toBN(_stateCollateral + _userCollateral, this.collateral_token.decimals)).toString(); return { totalBorrowed: formatUnits(_totalBorrowed, this.borrowed_token.decimals), borrowedFromStateCollateral: formatUnits(_borrowedFromStateCollateral, this.borrowed_token.decimals), borrowedFromUserCollateral: formatUnits(_borrowedFromUserCollateral, this.borrowed_token.decimals), userBorrowed: formatNumber(userBorrowed, this.borrowed_token.decimals), + avgPrice, } }, { From 116cfcd736976a25732eada2573db35078f4ddaa Mon Sep 17 00:00:00 2001 From: macket Date: Fri, 26 Apr 2024 13:02:24 +0400 Subject: [PATCH 35/36] docs: leverage --- README.md | 502 +++++++++++++++++++++++++++++++++++++++++++- test/readme.test.ts | 132 ++++++++++++ 2 files changed, 633 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 23153d6..29e6c2e 100644 --- a/README.md +++ b/README.md @@ -856,4 +856,504 @@ import lending from "@curvefi/lending-api"; // loss_pct: '5.043158793049750311' // } })() -``` \ No newline at end of file +``` + +### Leverage (createLoan, borrowMore, repay) +```ts +(async () => { + await lending.init('JsonRpc', {}, {}, API_KEY_1INCH); + await lending.oneWayfactory.fetchMarkets(); + + const oneWayMarket = lending.getOneWayMarket('one-way-market-0'); + console.log(oneWayMarket.collateral_token, oneWayMarket.borrowed_token); + // { + // address: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1', + // decimals: 18, + // name: 'Wrapped Ether', + // symbol: 'WETH' + // } + // + // { + // address: '0x498bf2b1e120fed3ad3d42ea2165e9b73f99c1e5', + // decimals: 18, + // name: 'Curve.Fi USD Stablecoin', + // symbol: 'crvUSD' + // } + console.log(await oneWayMarket.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 oneWayMarket.leverage.maxLeverage(range); + // 7.4728229145282742179 + await oneWayMarket.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 oneWayMarket.leverage.createLoanExpectedCollateral(userCollateral, userBorrowed, debt); + // { + // totalCollateral: '1.946422996710829', + // userCollateral: '1.0', + // collateralFromUserBorrowed: '0.315474332236942984', + // collateralFromDebt: '0.630948664473886', + // leverage: '1.4796358613861877' + // avgPrice: '3169.8299919022623523421' + // } + await oneWayMarket.leverage.createLoanMaxRange(userCollateral, userBorrowed, debt); + // 50 + await oneWayMarket.leverage.createLoanBands(userCollateral, userBorrowed, debt, range); + // [ 76, 67 ] + await oneWayMarket.leverage.createLoanPrices(userCollateral, userBorrowed, debt, range); + // [ '1027.977701011670136614', '1187.061409925215211173' ] + await oneWayMarket.leverage.createLoanHealth(userCollateral, userBorrowed, debt, range); + // 195.8994783042570637 + await oneWayMarket.leverage.createLoanHealth(userCollateral, userBorrowed, debt, range, false); + // 3.2780908310686365 + await oneWayMarket.leverage.createLoanIsApproved(userCollateral, userBorrowed); + // false + await oneWayMarket.leverage.createLoanApprove(userCollateral, userBorrowed); + // [ + // '0xd5491d9f1e9d8ac84b03867494e35b25efad151c597d2fa4211d7bf5d540c98e', + // '0x93565f37ec5be902a824714a30bddc25cf9cd9ed39b4c0e8de61fab44af5bc8c' + // ] + await oneWayMarket.leverage.createLoanRoute(userBorrowed, debt, slippage); + // [ + // { + // part: 100, + // hops: [ + // [ + // { + // name: 'ARBITRUM_CURVE_STABLE_NG', + // part: 100, + // fromTokenAddress: '0x498bf2b1e120fed3ad3d42ea2165e9b73f99c1e5', + // toTokenAddress: '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9' + // } + // ], + // [ + // { + // name: 'ARBITRUM_UNISWAP_V3', + // part: 100, + // fromTokenAddress: '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9', + // toTokenAddress: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1' + // } + // ] + // ] + // } + // ] + + await oneWayMarket.leverage.createLoan(userCollateral, userBorrowed, debt, range); + // 0xeb1b7a92bcb02598f00dc8bbfe8fa3a554e7a2b1ca764e0ee45e2bf583edf731 + + await oneWayMarket.wallet.balances(); + // { + // collateral: '99.0', + // borrowed: '599000.0', + // vaultShares: '1400000000.0', + // gauge: '0' + // } + await oneWayMarket.userState(); + // { + // collateral: '1.945616160868693648', + // borrowed: '0.0', + // debt: '2000.0', + // N: '10' + // } + await oneWayMarket.userBands(); + // [ 76, 67 ] + await oneWayMarket.userPrices(); + // [ '1027.977718614028011906', '1187.061430251609195098' ] + await oneWayMarket.userHealth(); + // 195.8372633833293605 + await oneWayMarket.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 oneWayMarket.leverage.borrowMoreMaxRecv(userCollateral, userBorrowed); + // { + // maxDebt: '76182.8497941193262889', + // maxTotalCollateral: '26.639775583730298462', + // userCollateral: '2', + // collateralFromUserBorrowed: '1.677318306610359627', + // collateralFromMaxDebt: '22.962457277119938834', + // avgPrice: '3172.55402418338331369083' + // } + await oneWayMarket.leverage.borrowMoreExpectedCollateral(userCollateral, userBorrowed, debt); + // { + // totalCollateral: '5.783452104143246413', + // userCollateral: '2.0', + // collateralFromUserBorrowed: '0.630575350690541071', + // collateralFromDebt: '3.152876753452705342' + // avgPrice: '3171.70659749038129067231' + // } + await oneWayMarket.leverage.borrowMoreBands(userCollateral, userBorrowed, debt); + // [ 47, 38 ] + await oneWayMarket.leverage.borrowMorePrices(userCollateral, userBorrowed, debt); + // [ '1560.282474721398939216', '1801.742501325928269008' ] + await oneWayMarket.leverage.borrowMoreHealth(userCollateral, userBorrowed, debt, true); + // 91.6798951784708552 + await oneWayMarket.leverage.borrowMoreHealth(userCollateral, userBorrowed, debt, false); + // 3.7614279042995641 + await oneWayMarket.leverage.borrowMoreIsApproved(userCollateral, userBorrowed); + // true + await oneWayMarket.leverage.borrowMoreApprove(userCollateral, userBorrowed); + // [] + await oneWayMarket.leverage.borrowMoreRoute(userBorrowed, debt, slippage); + // [ + // { + // part: 50, + // hops: [ + // [ + // { + // name: 'ARBITRUM_CURVE_STABLE_NG', + // part: 100, + // fromTokenAddress: '0x498bf2b1e120fed3ad3d42ea2165e9b73f99c1e5', + // toTokenAddress: '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9' + // } + // ], + // [ + // { + // name: 'ARBITRUM_PANCAKESWAP_V3', + // part: 12, + // fromTokenAddress: '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9', + // toTokenAddress: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1' + // }, + // { + // name: 'ARBITRUM_PANCAKESWAP_V3', + // part: 42, + // fromTokenAddress: '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9', + // toTokenAddress: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1' + // }, + // { + // name: 'ARBITRUM_UNISWAP_V3', + // part: 46, + // fromTokenAddress: '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9', + // toTokenAddress: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1' + // } + // ] + // ] + // }, + // { + // part: 50, + // hops: [ + // [ + // { + // name: 'ARBITRUM_CURVE_STABLE_NG', + // part: 100, + // fromTokenAddress: '0x498bf2b1e120fed3ad3d42ea2165e9b73f99c1e5', + // toTokenAddress: '0xaf88d065e77c8cc2239327c5edb3a432268e5831' + // } + // ], + // [ + // { + // name: 'ARBITRUM_PANCAKESWAP_V3', + // part: 10, + // fromTokenAddress: '0xaf88d065e77c8cc2239327c5edb3a432268e5831', + // toTokenAddress: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1' + // }, + // { + // name: 'ARBITRUM_UNISWAP_V3', + // part: 90, + // fromTokenAddress: '0xaf88d065e77c8cc2239327c5edb3a432268e5831', + // toTokenAddress: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1' + // } + // ] + // ] + // } + // ] + + await oneWayMarket.leverage.borrowMore(userCollateral, userBorrowed, debt, slippage); + // 0x6357dd6ea7250d7adb2344cd9295f8255fd8fbbe85f00120fbcd1ebf139e057c + + await oneWayMarket.wallet.balances(); + // { + // collateral: '97.0', + // borrowed: '597000.0', + // vaultShares: '1400000000.0', + // gauge: '0' + // } + await oneWayMarket.userState(); + // { + // collateral: '7.727839965845165558', + // borrowed: '0.0', + // debt: '12000.000010193901375446', + // N: '10' + // } + await oneWayMarket.userBands(); + // [ 47, 38 ] + await oneWayMarket.userPrices(); + // [ '1560.28248267408177179', '1801.742510509320950242' ] + await oneWayMarket.userHealth(); + // 91.6519475547753288 + await oneWayMarket.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 oneWayMarket.leverage.repayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed); + // { + // totalBorrowed: '10998.882838599741571472', + // borrowedFromStateCollateral: '6332.588559066494374648', + // borrowedFromUserCollateral: '3166.294279533247196824', + // userBorrowed: '1500' + // avgPrice: '3166.29427953324743125312' + // } + + + await oneWayMarket.leverage.repayIsFull(stateCollateral, userCollateral, userBorrowed); + // false + await oneWayMarket.leverage.repayIsAvailable(stateCollateral, userCollateral, userBorrowed); + // true + await oneWayMarket.leverage.repayBands(stateCollateral, userCollateral, userBorrowed); + // [ 199, 190 ] + await oneWayMarket.leverage.repayPrices(stateCollateral, userCollateral, userBorrowed); + // [ '175.130965754280721633', '202.233191367561902757' ] + await oneWayMarket.leverage.repayHealth(stateCollateral, userCollateral, userBorrowed, true); + // 1699.6097751079226865 + await oneWayMarket.leverage.repayHealth(stateCollateral, userCollateral, userBorrowed, false); + // 3.4560086962806991 + await oneWayMarket.leverage.repayIsApproved(userCollateral, userBorrowed); + // false + await oneWayMarket.leverage.repayApprove(userCollateral, userBorrowed); + // ['0xd8a8d3b3f67395e1a4f4d4f95b041edcaf1c9f7bab5eb8a8a767467678295498'] + await oneWayMarket.leverage.repayRoute(stateCollateral, userCollateral, slippage); + // [ + // { + // part: 10, + // hops: [ + // [ + // { + // name: 'ARBITRUM_CURVE_V2', + // part: 32, + // fromTokenAddress: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1', + // toTokenAddress: '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9' + // }, + // { + // name: 'ARBITRUM_PANCAKESWAP_V3', + // part: 68, + // fromTokenAddress: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1', + // toTokenAddress: '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9' + // } + // ], + // [ + // { + // name: 'ARBITRUM_CURVE_STABLE_NG', + // part: 100, + // fromTokenAddress: '0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9', + // toTokenAddress: '0x498bf2b1e120fed3ad3d42ea2165e9b73f99c1e5' + // } + // ] + // ] + // }, + // { + // part: 90, + // hops: [ + // [ + // { + // name: 'ARBITRUM_UNISWAP_V3', + // part: 8, + // fromTokenAddress: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1', + // toTokenAddress: '0xaf88d065e77c8cc2239327c5edb3a432268e5831' + // }, + // { + // name: 'ARBITRUM_INTEGRAL', + // part: 92, + // fromTokenAddress: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1', + // toTokenAddress: '0xaf88d065e77c8cc2239327c5edb3a432268e5831' + // } + // ], + // [ + // { + // name: 'ARBITRUM_CAMELOT_V3', + // part: 100, + // fromTokenAddress: '0xaf88d065e77c8cc2239327c5edb3a432268e5831', + // toTokenAddress: '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8' + // } + // ], + // [ + // { + // name: 'ARBITRUM_CURVE_STABLE_NG', + // part: 100, + // fromTokenAddress: '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', + // toTokenAddress: '0x498bf2b1e120fed3ad3d42ea2165e9b73f99c1e5' + // } + // ] + // ] + // } + // ] + + + await oneWayMarket.leverage.repay(stateCollateral, userCollateral, userBorrowed, slippage); + // 0xe48a97fef1c54180a2c7d104d210a95ac1a516fdd22109682179f1582da23a82 + + await oneWayMarket.wallet.balances(); + // { + // collateral: '96.0', + // borrowed: '595500.0', + // vaultShares: '1400000000.0', + // gauge: '0' + // } + await oneWayMarket.userState(); + // { + // collateral: '5.727839965845165558', + // borrowed: '0.0', + // debt: '992.083214663467727334', + // N: '10' + // } + await oneWayMarket.userBands(); + // [ 199, 190 ] + await oneWayMarket.userPrices(); + // [ '175.13096689602455189', '202.233192685995210783' ] + await oneWayMarket.userHealth(); + // 1716.0249924305707883 + await oneWayMarket.userHealth(false); + // 3.6389352509210336 +})() +``` + +### Leverage createLoan all ranges methods +```ts + await lending.init('JsonRpc', {}, {}, API_KEY_1INCH); + await lending.oneWayfactory.fetchMarkets(); + + const oneWayMarket = lending.getOneWayMarket('one-way-market-0'); + + const userCollateral = 1; + const userBorrowed = 1000; + const debt = 2000; + await oneWayMarket.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 oneWayMarket.leverage.createLoanBandsAllRanges(userCollateral, userBorrowed, debt); + // { + // '4': [ 73, 70 ], + // '5': [ 73, 69 ], + // '6': [ 74, 69 ], + // '7': [ 74, 68 ], + // + // ... + // + // '50': [ 97, 48 ] + // } + + await oneWayMarket.leverage.createLoanPricesAllRanges(userCollateral, userBorrowed, debt); + // { + // '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' ] + // } +``` diff --git a/test/readme.test.ts b/test/readme.test.ts index 2fae21a..66925e6 100644 --- a/test/readme.test.ts +++ b/test/readme.test.ts @@ -1,3 +1,4 @@ +import { API_KEY_1INCH } from "./rpcUrls.test.js"; import lending from "../src/index.js"; const generalMethodsTest = async () => { @@ -332,6 +333,133 @@ const selfLiquidationTest = async () => { console.log(await oneWayMarket.userState()); } +const leverageTest = async () => { + await lending.init('JsonRpc', {}, {}, API_KEY_1INCH); + await lending.oneWayfactory.fetchMarkets(); + + const oneWayMarket = lending.getOneWayMarket('one-way-market-0'); + console.log(oneWayMarket.collateral_token, oneWayMarket.borrowed_token); + console.log(await oneWayMarket.wallet.balances()); + + if (Number(await oneWayMarket.vault.totalLiquidity()) === 0) { + const maxDeposit = Number(await oneWayMarket.vault.maxDeposit()) * 0.7; + await oneWayMarket.vault.deposit(maxDeposit); + } + + console.log("\n- Create Loan -\n") + + let userCollateral = 1; + let userBorrowed = 1000; + let debt = 2000; + const range = 10; + const slippage = 0.5; // % + console.log(await oneWayMarket.leverage.maxLeverage(range)); + console.log(await oneWayMarket.leverage.createLoanMaxRecv(userCollateral, userBorrowed, range)); + console.log(await oneWayMarket.leverage.createLoanExpectedCollateral(userCollateral, userBorrowed, debt)); + console.log(await oneWayMarket.leverage.createLoanMaxRange(userCollateral, userBorrowed, debt)); + console.log(await oneWayMarket.leverage.createLoanBands(userCollateral, userBorrowed, debt, range)); + console.log(await oneWayMarket.leverage.createLoanPrices(userCollateral, userBorrowed, debt, range)); + console.log(await oneWayMarket.leverage.createLoanHealth(userCollateral, userBorrowed, debt, range)); + console.log(await oneWayMarket.leverage.createLoanHealth(userCollateral, userBorrowed, debt, range, false)); + console.log(await oneWayMarket.leverage.createLoanIsApproved(userCollateral, userBorrowed)); + console.log(await oneWayMarket.leverage.createLoanApprove(userCollateral, userBorrowed)); + let route = await oneWayMarket.leverage.createLoanRoute(userBorrowed, debt, slippage); + console.log(route); + for (const r of route) { + console.log(r); + console.log(r.part); + console.log(r.hops); + } + + console.log(await oneWayMarket.leverage.createLoan(userCollateral, userBorrowed, debt, range)); + + console.log(await oneWayMarket.wallet.balances()); + console.log(await oneWayMarket.userState()); + console.log(await oneWayMarket.userBands()); + console.log(await oneWayMarket.userPrices()); + console.log(await oneWayMarket.userHealth()); + console.log(await oneWayMarket.userHealth(false)); + + console.log("\n- Borrow More -\n") + + userCollateral = 2; + userBorrowed = 2000; + debt = 10000; + console.log(await oneWayMarket.leverage.borrowMoreMaxRecv(userCollateral, userBorrowed)); + console.log(await oneWayMarket.leverage.borrowMoreExpectedCollateral(userCollateral, userBorrowed, debt)); + console.log(await oneWayMarket.leverage.borrowMoreBands(userCollateral, userBorrowed, debt)); + console.log(await oneWayMarket.leverage.borrowMorePrices(userCollateral, userBorrowed, debt)); + console.log(await oneWayMarket.leverage.borrowMoreHealth(userCollateral, userBorrowed, debt, true)); + console.log(await oneWayMarket.leverage.borrowMoreHealth(userCollateral, userBorrowed, debt, false)); + console.log(await oneWayMarket.leverage.borrowMoreIsApproved(userCollateral, userBorrowed)); + console.log(await oneWayMarket.leverage.borrowMoreApprove(userCollateral, userBorrowed)); + route = await oneWayMarket.leverage.borrowMoreRoute(userBorrowed, debt, slippage); + console.log(route); + for (const r of route) { + console.log(r); + console.log(r.part); + console.log(r.hops); + } + console.log(await oneWayMarket.leverage.borrowMore(userCollateral, userBorrowed, debt, slippage)); + + console.log(await oneWayMarket.wallet.balances()); + console.log(await oneWayMarket.userState()); + console.log(await oneWayMarket.userBands()); + console.log(await oneWayMarket.userPrices()); + console.log(await oneWayMarket.userHealth()); + console.log(await oneWayMarket.userHealth(false)); + + console.log("\n- Repay -\n") + + const stateCollateral = 2; + userCollateral = 1; + userBorrowed = 1500; + console.log(await oneWayMarket.leverage.repayExpectedBorrowed(stateCollateral, userCollateral, userBorrowed)); + console.log(await oneWayMarket.leverage.repayIsFull(stateCollateral, userCollateral, userBorrowed)); + console.log(await oneWayMarket.leverage.repayIsAvailable(stateCollateral, userCollateral, userBorrowed)); + console.log(await oneWayMarket.leverage.repayBands(stateCollateral, userCollateral, userBorrowed)); + console.log(await oneWayMarket.leverage.repayPrices(stateCollateral, userCollateral, userBorrowed)); + console.log(await oneWayMarket.leverage.repayHealth(stateCollateral, userCollateral, userBorrowed, true)); + console.log(await oneWayMarket.leverage.repayHealth(stateCollateral, userCollateral, userBorrowed, false)); + console.log(await oneWayMarket.leverage.repayIsApproved(userCollateral, userBorrowed)); + console.log(await oneWayMarket.leverage.repayApprove(userCollateral, userBorrowed)); + route = await oneWayMarket.leverage.repayRoute(stateCollateral, userCollateral, slippage); + console.log(route); + for (const r of route) { + console.log(r); + console.log(r.part); + console.log(r.hops); + } + + console.log(await oneWayMarket.leverage.repay(stateCollateral, userCollateral, userBorrowed, slippage)); + + console.log(await oneWayMarket.wallet.balances()); + console.log(await oneWayMarket.userState()); + console.log(await oneWayMarket.userBands()); + console.log(await oneWayMarket.userPrices()); + console.log(await oneWayMarket.userHealth()); + console.log(await oneWayMarket.userHealth(false)); +} + +const leverageAllRangesTest = async () => { + await lending.init('JsonRpc', {}, {}, API_KEY_1INCH); + await lending.oneWayfactory.fetchMarkets(); + + const oneWayMarket = lending.getOneWayMarket('one-way-market-0'); + + if (Number(await oneWayMarket.vault.totalLiquidity()) === 0) { + const maxDeposit = Number(await oneWayMarket.vault.maxDeposit()) * 0.7; + await oneWayMarket.vault.deposit(maxDeposit); + } + + const userCollateral = 1; + const userBorrowed = 1000; + const debt = 2000; + console.log(await oneWayMarket.leverage.createLoanMaxRecvAllRanges(userCollateral, userBorrowed)); + console.log(await oneWayMarket.leverage.createLoanBandsAllRanges(userCollateral, userBorrowed, debt)); + console.log(await oneWayMarket.leverage.createLoanPricesAllRanges(userCollateral, userBorrowed, debt)); +} + (async () => { console.log("\n--- generalMethodsTest ---\n") await generalMethodsTest(); @@ -351,4 +479,8 @@ const selfLiquidationTest = async () => { await swapTest(); console.log("\n--- selfLiquidationTest ---\n") await selfLiquidationTest(); + console.log("\n--- leverageTest ---\n") + await leverageTest(); + console.log("\n--- leverageAllRangesTest ---\n") + await leverageAllRangesTest(); })() From 8c3f7a69d5da0bcd4bd6858f001bc3605fa4ff8a Mon Sep 17 00:00:00 2001 From: macket Date: Fri, 26 Apr 2024 13:03:20 +0400 Subject: [PATCH 36/36] build: v2.0.0-alpha --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f125f58..e28dd0a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@curvefi/lending-api", - "version": "1.6.4", + "version": "2.0.0-alpha", "description": "JavaScript library for Curve Lending", "main": "lib/index.js", "author": "Macket",