diff --git a/package.json b/package.json index f26586dae..0b28b9818 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "bs58": "^5.0.0", "chart.js": "^4.4.0", "classnames": "^2.3.2", - "fbonds-core": "0.6.69", + "fbonds-core": "0.6.70", "firebase": "^10.1.0", "idb-keyval": "^6.2.1", "immer": "9.0.19", diff --git a/src/components/PlaceOfferSection/PlaceOfferContent/PlaceOfferContent.module.less b/src/components/PlaceOfferSection/PlaceOfferContent/PlaceOfferContent.module.less index e22187eeb..8a0c207e7 100644 --- a/src/components/PlaceOfferSection/PlaceOfferContent/PlaceOfferContent.module.less +++ b/src/components/PlaceOfferSection/PlaceOfferContent/PlaceOfferContent.module.less @@ -38,9 +38,6 @@ .offersAmountInput { max-width: 146px; } -.deltaInput { - max-width: 116px; -} .messageContainer { height: 14px; diff --git a/src/components/PlaceOfferSection/PlaceOfferContent/PlaceOfferContent.tsx b/src/components/PlaceOfferSection/PlaceOfferContent/PlaceOfferContent.tsx index 4576e6c0d..33f5d1d6d 100644 --- a/src/components/PlaceOfferSection/PlaceOfferContent/PlaceOfferContent.tsx +++ b/src/components/PlaceOfferSection/PlaceOfferContent/PlaceOfferContent.tsx @@ -16,10 +16,8 @@ import styles from './PlaceOfferContent.module.less' const PlaceOfferContent: FC = ({ loanValue, loansAmount, - deltaValue, onLoanValueChange, onLoanAmountChange, - onDeltaValueChange, onCreateOffer, onRemoveOffer, onUpdateOffer, @@ -65,16 +63,6 @@ const PlaceOfferContent: FC = ({ className={styles.offersAmountInput} step={1} /> -
{offerErrorMessage && } diff --git a/src/components/PlaceOfferSection/PlaceOfferContent/components/Diagram/helpers.ts b/src/components/PlaceOfferSection/PlaceOfferContent/components/Diagram/helpers.ts index 559cad3e0..09bebd04d 100644 --- a/src/components/PlaceOfferSection/PlaceOfferContent/components/Diagram/helpers.ts +++ b/src/components/PlaceOfferSection/PlaceOfferContent/components/Diagram/helpers.ts @@ -14,10 +14,6 @@ export const convertSimpleOfferToMark = (offer: SimpleOffer) => { return { value: offer.loanValue } } -export const convertOfferToMark = (offerValue: number, index: number, delta: number) => { - return { value: index === 0 ? offerValue : offerValue - delta * index } -} - export const calcLeftPercentage = (markers: Mark[] | Mark[][], currentIndex: number) => { const percentage = (currentIndex / (markers.length - 1)) * 100 || 0 return clamp(percentage, MIN_BOUND_PERCENTAGE, MAX_BOUND_PERCENTAGE) @@ -26,7 +22,6 @@ export const calcLeftPercentage = (markers: Mark[] | Mark[][], currentIndex: num export const groupMarks = (markers: Mark[]): Mark[] | Mark[][] => { const uniqueMarks = uniqBy(markers, (mark) => mark.value) - // //? If all marks have the same value, group them by value. Used for offers without delta if (uniqueMarks.length === 1) { return chain(markers) .groupBy((mark) => mark.value) diff --git a/src/components/PlaceOfferSection/PlaceOfferContent/components/Summary/helpers.ts b/src/components/PlaceOfferSection/PlaceOfferContent/components/Summary/helpers.ts index 3257120f9..6619e0cfb 100644 --- a/src/components/PlaceOfferSection/PlaceOfferContent/components/Summary/helpers.ts +++ b/src/components/PlaceOfferSection/PlaceOfferContent/components/Summary/helpers.ts @@ -1,6 +1,6 @@ import { core } from '@banx/api/nft' import { WEEKS_IN_YEAR } from '@banx/constants' -import { calcSyntheticLoanValue } from '@banx/store/nft' +import { calculateMaxLoanValueFromOffer } from '@banx/utils' interface CalcOfferSizeProps { initialOffer: core.Offer | undefined @@ -46,7 +46,7 @@ export const calcMaxLtv = ({ hasFormChanges: boolean }) => { //? Calculate initial LTV based on the best offer in the pool - const bestLoanValue = initialOffer ? calcSyntheticLoanValue(initialOffer) : 0 + const bestLoanValue = initialOffer ? calculateMaxLoanValueFromOffer(initialOffer) : 0 const initialCurrentLtv = calcLtv(bestLoanValue, collectionFloor) //? Calculate initial maximum LTV when the best offer in the pool was created @@ -54,7 +54,7 @@ export const calcMaxLtv = ({ const initialMaxLtv = calcLtv(initialMaxLoanValue, collectionFloor) //? Calculate updated LTV based on the best offer in the pool when form has changes - const updatedBestLoanValue = updatedOffer ? calcSyntheticLoanValue(updatedOffer) : 0 + const updatedBestLoanValue = updatedOffer ? calculateMaxLoanValueFromOffer(updatedOffer) : 0 const updatedCurrentLtv = calcLtv(updatedBestLoanValue, collectionFloor) || 0 const currentLtv = initialOffer && !hasFormChanges ? initialCurrentLtv : updatedCurrentLtv diff --git a/src/components/PlaceOfferSection/helpers.ts b/src/components/PlaceOfferSection/helpers.ts index 46a1de4c2..885755f7c 100644 --- a/src/components/PlaceOfferSection/helpers.ts +++ b/src/components/PlaceOfferSection/helpers.ts @@ -15,7 +15,6 @@ import { ZERO_BN } from '@banx/utils' type GetUpdatedBondOffer = (props: { loanValue: number //? lamports - deltaValue: number //? lamports loansAmount: number //? integer number syntheticOffer: SyntheticOffer tokenType: LendingTokenType @@ -23,7 +22,6 @@ type GetUpdatedBondOffer = (props: { export const getUpdatedBondOffer: GetUpdatedBondOffer = ({ loanValue, - deltaValue, loansAmount, syntheticOffer, tokenType, @@ -39,7 +37,6 @@ export const getUpdatedBondOffer: GetUpdatedBondOffer = ({ const updatedBondOffer = optimisticUpdateBondOfferBonding({ bondOffer: initializedOffer, newLoanValue: new BN(loanValue), - newDelta: new BN(deltaValue), newQuantityOfLoans: new BN(loansAmount), collateralsPerToken: ZERO_BN, tokenLendingApr: ZERO_BN, @@ -53,9 +50,7 @@ type GetErrorMessage = (props: { walletBalance: number escrowBalance: number offerSize: number - loanValue: number loansAmount: number - deltaValue: number hasFormChanges: boolean tokenType: LendingTokenType }) => string @@ -74,16 +69,13 @@ export const getErrorMessage: GetErrorMessage = ({ walletBalance, escrowBalance, offerSize, - loanValue, loansAmount, - deltaValue, syntheticOffer, hasFormChanges, tokenType, }) => { const initialOfferSize = calcOfferSize({ syntheticOffer, - deltaValue: syntheticOffer.deltaValue, loanValue: syntheticOffer.loanValue, loansAmount: syntheticOffer.loansAmount, tokenType, @@ -91,12 +83,10 @@ export const getErrorMessage: GetErrorMessage = ({ const totalFundsAvailable = initialOfferSize + walletBalance + escrowBalance - const isOfferInvalid = deltaValue && hasFormChanges ? deltaValue * loansAmount > loanValue : false const isBalanceInsufficient = !!walletBalance && offerSize > totalFundsAvailable const isEmptyLoansAmount = hasFormChanges && !loansAmount const errorConditions: Array<[boolean, string]> = [ - [isOfferInvalid, ERROR_MESSAGES.INVALID_OFFER], [isBalanceInsufficient, ERROR_MESSAGES.INSUFFICIENT_BALANCE[tokenType]], [isEmptyLoansAmount, ERROR_MESSAGES.EMPTY_LOANS_AMOUNT], ] @@ -115,7 +105,6 @@ export const checkIsEditMode = (offerPubkey: string) => type CalcOfferSize = (props: { syntheticOffer: SyntheticOffer loanValue: number //? lamports - deltaValue: number //? lamports loansAmount: number tokenType: LendingTokenType }) => number @@ -124,10 +113,9 @@ export const calcOfferSize: CalcOfferSize = ({ syntheticOffer, loanValue, loansAmount, - deltaValue, tokenType, }) => { - const offerToUpdate = { loanValue, deltaValue, loansAmount, syntheticOffer, tokenType } + const offerToUpdate = { loanValue, loansAmount, syntheticOffer, tokenType } const updatedBondOffer = getUpdatedBondOffer(offerToUpdate) const offerSize = updatedBondOffer.fundsSolOrTokenBalance + updatedBondOffer.bidSettlement diff --git a/src/components/PlaceOfferSection/hooks/useOfferFormController.ts b/src/components/PlaceOfferSection/hooks/useOfferFormController.ts index 43b4984a7..a49c68faf 100644 --- a/src/components/PlaceOfferSection/hooks/useOfferFormController.ts +++ b/src/components/PlaceOfferSection/hooks/useOfferFormController.ts @@ -9,48 +9,31 @@ import { getTokenDecimals } from '@banx/utils' export const useOfferFormController = (syntheticOffer: SyntheticOffer) => { const { tokenType } = useTokenType() - const { - deltaValue: syntheticDeltaValue, - loanValue: syntheticLoanValue, - loansAmount: syntheticLoansAmount, - } = syntheticOffer + const { loanValue: syntheticLoanValue, loansAmount: syntheticLoansAmount } = syntheticOffer const decimals = getTokenDecimals(tokenType) const initialValues = useMemo(() => { return { - deltaValue: formatNumber(syntheticDeltaValue / decimals), loanValue: formatNumber(syntheticLoanValue / decimals), loansAmount: syntheticOffer.isEdit ? String(syntheticLoansAmount) : '1', } - }, [ - decimals, - syntheticDeltaValue, - syntheticLoanValue, - syntheticLoansAmount, - syntheticOffer.isEdit, - ]) + }, [decimals, syntheticLoanValue, syntheticLoansAmount, syntheticOffer.isEdit]) const [loanValue, setLoanValue] = useState(initialValues.loanValue) const [loansAmount, setLoansAmount] = useState(initialValues.loansAmount) - const [deltaValue, setDeltaValue] = useState(initialValues.deltaValue) useEffect(() => { - const { loanValue, loansAmount, deltaValue } = initialValues + const { loanValue, loansAmount } = initialValues setLoanValue(loanValue) setLoansAmount(loansAmount) - setDeltaValue(deltaValue) }, [initialValues]) const onLoanValueChange = useCallback((nextValue: string) => { setLoanValue(nextValue) }, []) - const onDeltaValueChange = useCallback((nextValue: string) => { - setDeltaValue(nextValue) - }, []) - const onLoanAmountChange = useCallback((nextValue: string) => { const sanitizedValue = trimStart(nextValue, '0') const numericValue = parseFloat(sanitizedValue) || 0 @@ -62,25 +45,18 @@ export const useOfferFormController = (syntheticOffer: SyntheticOffer) => { const resetFormValues = () => { setLoanValue(initialValues.loanValue) setLoansAmount(initialValues.loansAmount) - setDeltaValue(initialValues.deltaValue) } const hasFormChanges = useMemo(() => { - return ( - loansAmount !== initialValues.loansAmount || - loanValue !== initialValues.loanValue || - deltaValue !== initialValues.deltaValue - ) - }, [initialValues, loansAmount, loanValue, deltaValue]) + return loansAmount !== initialValues.loansAmount || loanValue !== initialValues.loanValue + }, [initialValues, loansAmount, loanValue]) return { loanValue, loansAmount, - deltaValue, onLoanValueChange, onLoanAmountChange, - onDeltaValueChange, hasFormChanges: Boolean(hasFormChanges), resetFormValues, diff --git a/src/components/PlaceOfferSection/hooks/useOfferTransactions.ts b/src/components/PlaceOfferSection/hooks/useOfferTransactions.ts index 39e646fcd..0f7ed096b 100644 --- a/src/components/PlaceOfferSection/hooks/useOfferTransactions.ts +++ b/src/components/PlaceOfferSection/hooks/useOfferTransactions.ts @@ -37,7 +37,6 @@ export const useOfferTransactions = ({ marketPubkey, loansAmount, loanValue, - deltaValue, optimisticOffer, updateOrAddOffer, resetFormValues, @@ -46,7 +45,6 @@ export const useOfferTransactions = ({ marketPubkey: string loansAmount: number loanValue: number - deltaValue: number optimisticOffer?: core.Offer updateOrAddOffer: (offer: core.Offer) => void resetFormValues: () => void @@ -69,7 +67,6 @@ export const useOfferTransactions = ({ marketPubkey, loansAmount, loanValue, - deltaValue, tokenType, bondFeature: BondFeatures.AutoReceiveAndReceiveNft, escrowBalance: userVault?.offerLiquidityAmount, @@ -126,7 +123,6 @@ export const useOfferTransactions = ({ marketPubkey, loansAmount, loanValue, - deltaValue, tokenType, }, walletPubkey: wallet?.publicKey?.toBase58(), @@ -148,7 +144,6 @@ export const useOfferTransactions = ({ loanValue, offer: optimisticOffer, loansAmount, - deltaValue, tokenType, escrowBalance: userVault?.offerLiquidityAmount, depositAmountToVault, @@ -201,7 +196,6 @@ export const useOfferTransactions = ({ loanValue, offer: optimisticOffer, loansAmount, - deltaValue, tokenType, }, walletPubkey: wallet?.publicKey?.toBase58(), diff --git a/src/components/PlaceOfferSection/hooks/usePlaceOffer.ts b/src/components/PlaceOfferSection/hooks/usePlaceOffer.ts index 1af537295..f79d3edbc 100644 --- a/src/components/PlaceOfferSection/hooks/usePlaceOffer.ts +++ b/src/components/PlaceOfferSection/hooks/usePlaceOffer.ts @@ -13,7 +13,6 @@ import { convertOffersToSimple, getTokenDecimals } from '@banx/utils' import { Mark } from '../PlaceOfferContent/components' import { convertLoanToMark, - convertOfferToMark, convertSimpleOfferToMark, } from '../PlaceOfferContent/components/Diagram' import { calcOfferSize, getErrorMessage, getUpdatedBondOffer } from '../helpers' @@ -40,11 +39,9 @@ export interface PlaceOfferParams { onUpdateOffer: () => void loansAmount: string - deltaValue: string loanValue: string offerSize: number - onDeltaValueChange: (value: string) => void onLoanValueChange: (value: string) => void onLoanAmountChange: (value: string) => void @@ -79,15 +76,12 @@ export const usePlaceOffer: UsePlaceOffer = ({ marketPubkey, offerPubkey, setOff const { loanValue: loanValueString, loansAmount: loansAmountString, - deltaValue: deltaValueString, - onDeltaValueChange, onLoanValueChange, onLoanAmountChange, hasFormChanges, resetFormValues, } = useOfferFormController(syntheticOffer) - const deltaValue = parseFloat(deltaValueString) * decimals const loanValue = parseFloat(loanValueString) * decimals const loansAmount = parseFloat(loansAmountString) @@ -100,60 +94,51 @@ export const usePlaceOffer: UsePlaceOffer = ({ marketPubkey, offerPubkey, setOff useEffect(() => { if (!syntheticOffer) return - const newSyntheticOffer = { ...syntheticOffer, deltaValue, loanValue, loansAmount } + const newSyntheticOffer = { ...syntheticOffer, loanValue, loansAmount } setSyntheticOffer(newSyntheticOffer) - }, [syntheticOffer, setSyntheticOffer, deltaValue, loanValue, loansAmount]) + }, [syntheticOffer, setSyntheticOffer, loanValue, loansAmount]) const { onCreateOffer, onRemoveOffer, onUpdateOffer } = useOfferTransactions({ marketPubkey, loanValue, loansAmount, optimisticOffer: offer, - deltaValue, updateOrAddOffer, resetFormValues, exitEditMode, }) const offerSize = useMemo(() => { - return calcOfferSize({ syntheticOffer, loanValue, loansAmount, deltaValue, tokenType }) - }, [syntheticOffer, loanValue, loansAmount, deltaValue, tokenType]) + return calcOfferSize({ syntheticOffer, loanValue, loansAmount, tokenType }) + }, [syntheticOffer, loanValue, loansAmount, tokenType]) const updatedOffer = useMemo(() => { - return getUpdatedBondOffer({ syntheticOffer, loanValue, loansAmount, deltaValue, tokenType }) - }, [syntheticOffer, loanValue, loansAmount, deltaValue, tokenType]) + return getUpdatedBondOffer({ syntheticOffer, loanValue, loansAmount, tokenType }) + }, [syntheticOffer, loanValue, loansAmount, tokenType]) const offerErrorMessage = getErrorMessage({ syntheticOffer, walletBalance, escrowBalance: userVault?.offerLiquidityAmount.toNumber() || 0, offerSize, - loanValue, loansAmount, - deltaValue, hasFormChanges, tokenType, }) const diagramData = useMemo(() => { - const isOfferInvalid = - deltaValue && hasFormChanges ? deltaValue * loansAmount > loanValue : false - - if (isOfferInvalid) return [] - if (!isEditMode) { return chain(new Array(loansAmount)) .fill(loanValue) - .map((offerValue, index) => convertOfferToMark(offerValue, index, deltaValue)) - .sortBy(({ value }) => value) + .sortBy((value) => value) .reverse() .value() } if (!offer) return [] - const offerToUpdate = { syntheticOffer, loanValue, deltaValue, loansAmount, tokenType } + const offerToUpdate = { syntheticOffer, loanValue, loansAmount, tokenType } const offerToUse = hasFormChanges ? getUpdatedBondOffer(offerToUpdate) : offer const loansToMarks = lenderLoans.map(convertLoanToMark) @@ -171,7 +156,6 @@ export const usePlaceOffer: UsePlaceOffer = ({ marketPubkey, offerPubkey, setOff isEditMode, loansAmount, loanValue, - deltaValue, offer, hasFormChanges, syntheticOffer, @@ -187,10 +171,8 @@ export const usePlaceOffer: UsePlaceOffer = ({ marketPubkey, offerPubkey, setOff loanValue: loanValueString, loansAmount: loansAmountString, - deltaValue: deltaValueString, offerSize, - onDeltaValueChange, onLoanValueChange, onLoanAmountChange, diff --git a/src/components/PlaceTokenOfferSection/hooks/useTokenOfferTransaction.ts b/src/components/PlaceTokenOfferSection/hooks/useTokenOfferTransaction.ts index 23e57b0eb..1fda72c5c 100644 --- a/src/components/PlaceTokenOfferSection/hooks/useTokenOfferTransaction.ts +++ b/src/components/PlaceTokenOfferSection/hooks/useTokenOfferTransaction.ts @@ -67,7 +67,6 @@ export const useTokenOfferTransactions = ({ marketPubkey, loansAmount: 1, loanValue, - deltaValue: 0, collateralsPerToken, tokenLendingApr: apr * 100, bondFeature: BondFeatures.AutoReceiveAndReceiveSpl, @@ -145,7 +144,6 @@ export const useTokenOfferTransactions = ({ loanValue, offer: convertBondOfferV3ToCore(optimisticOffer), loansAmount: 1, - deltaValue: 0, tokenType, collateralsPerToken, tokenLendingApr: apr * 100, diff --git a/src/constants/config.ts b/src/constants/config.ts index 94752ec30..847c5f507 100644 --- a/src/constants/config.ts +++ b/src/constants/config.ts @@ -1,8 +1,13 @@ import { BONDS_PROGRAM_PUBKEY } from 'fbonds-core/lib/fbond-protocol/constants' +import sdkPackage from 'fbonds-core/package.json' + +export const IS_SDK_ON_DEV_CONTRACT = sdkPackage.version.includes('dev') export const IS_DEVELOPMENT = process.env.NODE_ENV !== 'production' -export const BACKEND_BASE_URL = 'https://api.banx.gg' +export const BACKEND_BASE_URL = IS_SDK_ON_DEV_CONTRACT + ? 'http://ec2-52-87-120-232.compute-1.amazonaws.com:3000' + : 'https://api.banx.gg' export const FCM = { AUTH_DOMAIN: 'frakt-ee9cc.firebaseapp.com', @@ -40,6 +45,8 @@ export const BANX_STAKING = { FRAKT_MARKET: 'HrsMreAqj4ss19WDemwFCVnxnhgJ5tTNjt4k8cKzTmko', } -export const IS_PRIVATE_MARKETS = process.env.IS_PRIVATE_MARKETS === 'true' +export const IS_PRIVATE_MARKETS = IS_SDK_ON_DEV_CONTRACT + ? false + : process.env.IS_PRIVATE_MARKETS === 'true' export const DYNAMIC_APR = true diff --git a/src/pages/nftLending/BorrowPage/InstantLoansContent/components/MarketBorrowCardExpandedContent/Cells.tsx b/src/pages/nftLending/BorrowPage/InstantLoansContent/components/MarketBorrowCardExpandedContent/Cells.tsx index 4827fa329..ce5c612e8 100644 --- a/src/pages/nftLending/BorrowPage/InstantLoansContent/components/MarketBorrowCardExpandedContent/Cells.tsx +++ b/src/pages/nftLending/BorrowPage/InstantLoansContent/components/MarketBorrowCardExpandedContent/Cells.tsx @@ -1,10 +1,8 @@ import { FC } from 'react' import classNames from 'classnames' -import { BN } from 'fbonds-core' import { calculateCurrentInterestSolPure } from 'fbonds-core/lib/fbond-protocol/functions/perpetual' import { calcBorrowerTokenAPR } from 'fbonds-core/lib/fbond-protocol/helpers' -import { LendingTokenType } from 'fbonds-core/lib/fbond-protocol/types' import { DisplayValue, @@ -13,11 +11,7 @@ import { } from '@banx/components/TableComponents' import { SECONDS_IN_DAY } from '@banx/constants' -import { - adjustBorrowValueWithSolanaRentFee, - calculateApr, - calculateBorrowValueWithProtocolFee, -} from '@banx/utils' +import { calculateApr, calculateBorrowValueWithProtocolFee } from '@banx/utils' import { TableNftData } from './hooks' @@ -38,10 +32,9 @@ const TooltipRow: FC = ({ label, value }) => ( interface BorrowCellProps { nft: TableNftData - tokenType: LendingTokenType } -export const BorrowCell: FC = ({ nft, tokenType }) => { +export const BorrowCell: FC = ({ nft }) => { const marketUpfrontFee = nft.nft.nft.upfrontFee const loanValueWithProtocolFee = calculateBorrowValueWithProtocolFee( @@ -64,15 +57,9 @@ export const BorrowCell: FC = ({ nft, tokenType }) => {
) - const borrowValueRentFeeAdjusted = adjustBorrowValueWithSolanaRentFee({ - value: new BN(loanValueWithProtocolFee), - marketPubkey: nft.nft.loan.marketPubkey, - tokenType, - }).toNumber() - return ( } + value={} tooltipContent={tooltipContent} /> ) diff --git a/src/pages/nftLending/BorrowPage/InstantLoansContent/components/MarketBorrowCardExpandedContent/components/Summary/Summary.tsx b/src/pages/nftLending/BorrowPage/InstantLoansContent/components/MarketBorrowCardExpandedContent/components/Summary/Summary.tsx index 98dd20981..46c47e61a 100644 --- a/src/pages/nftLending/BorrowPage/InstantLoansContent/components/MarketBorrowCardExpandedContent/components/Summary/Summary.tsx +++ b/src/pages/nftLending/BorrowPage/InstantLoansContent/components/MarketBorrowCardExpandedContent/components/Summary/Summary.tsx @@ -1,9 +1,7 @@ import { FC, useMemo, useState } from 'react' import classNames from 'classnames' -import { BN } from 'fbonds-core' import { calcBorrowerTokenAPR } from 'fbonds-core/lib/fbond-protocol/helpers' -import { LendingTokenType } from 'fbonds-core/lib/fbond-protocol/types' import { map, sumBy } from 'lodash' import { Button } from '@banx/components/Buttons' @@ -12,10 +10,8 @@ import { DisplayValue, createPercentValueJSX } from '@banx/components/TableCompo import { core } from '@banx/api/nft' import { ONE_WEEK_IN_SECONDS } from '@banx/constants' -import { useTokenType } from '@banx/store/common' import { NftWithLoanValue, - adjustBorrowValueWithSolanaRentFee, calcWeightedAverage, calculateApr, calculateBorrowValueWithProtocolFee, @@ -47,9 +43,7 @@ export const Summary: FC = ({ }) => { const { borrowBulk } = useBorrowNftTransactions(marketPubkey) - const { tokenType } = useTokenType() - - const totalBorrow = sumBy(nftsInCart, (nft) => calcLoanValueWithFees(nft, tokenType)) + const totalBorrow = sumBy(nftsInCart, (nft) => calcLoanValueWithFees(nft)) const totalUpfrontFee = sumBy(nftsInCart, ({ loanValue, nft }) => { const marketUpfrontFee = nft.nft.upfrontFee @@ -73,10 +67,10 @@ export const Summary: FC = ({ calcBorrowerTokenAPR(caclAprValue(nft, loanValue), nft.nft.interestFee, false) / 100, ) - const totalLoanValue = map(nftsInCart, (nft) => calcLoanValueWithFees(nft, tokenType)) + const totalLoanValue = map(nftsInCart, (nft) => calcLoanValueWithFees(nft)) return calcWeightedAverage(totalApr, totalLoanValue) - }, [nftsInCart, tokenType]) + }, [nftsInCart]) const [isBorrowing, setIsBorrowing] = useState(false) const onBorrow = async () => { @@ -169,19 +163,10 @@ const MaxLtvSlider: FC = ({ value, onChange, ...props }) => { ) } -const calcLoanValueWithFees = (nft: NftWithLoanValue, tokenType: LendingTokenType) => { +const calcLoanValueWithFees = (nft: NftWithLoanValue) => { const marketUpfrontFee = nft.nft.nft.upfrontFee - const loanValueWithProtocolFee = calculateBorrowValueWithProtocolFee( - nft.loanValue, - marketUpfrontFee, - ) - - return adjustBorrowValueWithSolanaRentFee({ - value: new BN(loanValueWithProtocolFee), - marketPubkey: nft.nft.loan.marketPubkey, - tokenType, - }).toNumber() + return calculateBorrowValueWithProtocolFee(nft.loanValue, marketUpfrontFee) } const caclAprValue = (nft: core.BorrowNft, loanValue: number) => { diff --git a/src/pages/nftLending/BorrowPage/InstantLoansContent/components/MarketBorrowCardExpandedContent/getTableColumns.tsx b/src/pages/nftLending/BorrowPage/InstantLoansContent/components/MarketBorrowCardExpandedContent/getTableColumns.tsx index 115295b06..559f1ad2c 100644 --- a/src/pages/nftLending/BorrowPage/InstantLoansContent/components/MarketBorrowCardExpandedContent/getTableColumns.tsx +++ b/src/pages/nftLending/BorrowPage/InstantLoansContent/components/MarketBorrowCardExpandedContent/getTableColumns.tsx @@ -1,5 +1,3 @@ -import { LendingTokenType } from 'fbonds-core/lib/fbond-protocol/types' - import Checkbox from '@banx/components/Checkbox' import { ColumnType } from '@banx/components/Table' import { @@ -24,7 +22,6 @@ interface GetTableColumnsProps { isCardView: boolean cartNotEmpty: boolean onSelectAll: () => void - tokenType: LendingTokenType goToRequestLoanTab: () => void } @@ -35,7 +32,6 @@ export const getTableColumns = ({ isCardView, cartNotEmpty, onSelectAll, - tokenType, goToRequestLoanTab, }: GetTableColumnsProps) => { const columns: ColumnType[] = [ @@ -64,7 +60,7 @@ export const getTableColumns = ({ { key: 'loanValue', title: , - render: (nft) => , + render: (nft) => , }, { key: 'fee', diff --git a/src/pages/nftLending/BorrowPage/InstantLoansContent/components/MarketBorrowCardExpandedContent/hooks/useBorrowTable.ts b/src/pages/nftLending/BorrowPage/InstantLoansContent/components/MarketBorrowCardExpandedContent/hooks/useBorrowTable.ts index 945c3570b..58e19879b 100644 --- a/src/pages/nftLending/BorrowPage/InstantLoansContent/components/MarketBorrowCardExpandedContent/hooks/useBorrowTable.ts +++ b/src/pages/nftLending/BorrowPage/InstantLoansContent/components/MarketBorrowCardExpandedContent/hooks/useBorrowTable.ts @@ -117,7 +117,6 @@ export const useBorrowTable = ({ marketPubkey, goToRequestLoanTab }: UseBorrowTa isCardView: viewState === ViewState.CARD, findOfferInCart, goToRequestLoanTab, - tokenType, cartNotEmpty: !isEmpty(offerByMintInCart), onSelectAll, }) diff --git a/src/pages/nftLending/LendPage/PlaceOffersContent/components/Offer/Offer.tsx b/src/pages/nftLending/LendPage/PlaceOffersContent/components/Offer/Offer.tsx index af69cadf9..4b1feff72 100644 --- a/src/pages/nftLending/LendPage/PlaceOffersContent/components/Offer/Offer.tsx +++ b/src/pages/nftLending/LendPage/PlaceOffersContent/components/Offer/Offer.tsx @@ -3,7 +3,6 @@ import { FC } from 'react' import { useWallet } from '@solana/wallet-adapter-react' import classNames from 'classnames' import { PUBKEY_PLACEHOLDER } from 'fbonds-core/lib/fbond-protocol/constants' -import { LendingTokenType } from 'fbonds-core/lib/fbond-protocol/types' import { Button } from '@banx/components/Buttons' import { createDisplayValueJSX, createPercentValueJSX } from '@banx/components/TableComponents' @@ -45,7 +44,7 @@ const Offer: FC = ({ editOffer, offer, collectionFloor }) => { [styles.hidden]: !isEdit && !isNewOffer, } - const displayOfferValue = getDisplayOfferRange(offer, tokenType) + const displayOfferValue = formatValueByTokenType(loanValue, tokenType) const maxDynamicApr = calculateApr({ loanValue, collectionFloor, marketPubkey }) / 100 const tokenUnit = getTokenUnit(tokenType) @@ -86,18 +85,3 @@ const EditOfferButton: FC<{ onClick: () => void }> = ({ onClick }) => ( ) - -const getDisplayOfferRange = (offer: SyntheticOffer, tokenType: LendingTokenType) => { - const { loanValue, loansAmount, deltaValue } = offer - - const minDeltaValue = loanValue - (loansAmount - 1) * deltaValue - - const formattedLoanValue = formatValueByTokenType(loanValue, tokenType) - const formattedMinLoanValue = formatValueByTokenType(minDeltaValue, tokenType) - - const displayOfferRange = deltaValue - ? `${formattedLoanValue} - ${formattedMinLoanValue}` - : formattedLoanValue - - return displayOfferRange || '0' -} diff --git a/src/pages/nftLending/LoansPage/components/LoansActiveTable/TableCells/ActionsCell/RefinanceModal.tsx b/src/pages/nftLending/LoansPage/components/LoansActiveTable/TableCells/ActionsCell/RefinanceModal.tsx index ff47543f1..9af201af5 100644 --- a/src/pages/nftLending/LoansPage/components/LoansActiveTable/TableCells/ActionsCell/RefinanceModal.tsx +++ b/src/pages/nftLending/LoansPage/components/LoansActiveTable/TableCells/ActionsCell/RefinanceModal.tsx @@ -35,6 +35,7 @@ import { calculateApr, calculateBorrowedAmount, calculateLoanRepayValue, + calculateMaxLoanValueFromOffer, convertToHumanNumber, destroySnackbar, enqueueConfirmationError, @@ -72,27 +73,28 @@ export const RefinanceModal: FC = ({ loan }) => { const bestOffer = useMemo(() => { return chain(offers) - .sortBy(({ currentSpotPrice }) => currentSpotPrice) + .filter(isOfferNotEmpty) + .sortBy((offer) => calculateMaxLoanValueFromOffer(offer)) .thru((offers) => filterOutWalletLoans({ offers, walletPubkey: wallet?.publicKey?.toBase58(), }), ) - .filter(isOfferNotEmpty) + .reverse() .value() .at(0) }, [offers, wallet]) - const initialCurrentSpotPrice = useMemo(() => { + const initialMaxLoanValue = useMemo(() => { if (!bestOffer) return 0 - return bestOffer.currentSpotPrice + return calculateMaxLoanValueFromOffer(bestOffer) }, [bestOffer]) useEffect(() => { - setCurrentSpotPrice(initialCurrentSpotPrice) - }, [initialCurrentSpotPrice]) + setMaxLoanValue(initialMaxLoanValue) + }, [initialMaxLoanValue]) const { update: updateLoansOptimistic } = useLoansOptimistic() const { clear: clearSelection } = useSelectedLoans() @@ -100,11 +102,11 @@ export const RefinanceModal: FC = ({ loan }) => { const isTerminatingStatus = isLoanTerminating(loan) const [partialPercent, setPartialPercent] = useState(100) - const [currentSpotPrice, setCurrentSpotPrice] = useState(initialCurrentSpotPrice) + const [maxLoanValue, setMaxLoanValue] = useState(initialMaxLoanValue) const onPartialPercentChange = (percentValue: number) => { setPartialPercent(percentValue) - setCurrentSpotPrice(Math.max(Math.floor((initialCurrentSpotPrice * percentValue) / 100), 1000)) + setMaxLoanValue(Math.max(Math.floor((initialMaxLoanValue * percentValue) / 100), 1000)) } const currentLoanDebt = calculateLoanRepayValue(loan) @@ -112,10 +114,10 @@ export const RefinanceModal: FC = ({ loan }) => { const currentApr = bondTradeTransaction.amountOfBonds //? Upfront fee on reborrow is calculated: (newDebt - prevDebt) / 100 - const upfrontFee = Math.max((currentSpotPrice - currentLoanDebt) / 100, 0) + const upfrontFee = Math.max((maxLoanValue - currentLoanDebt) / 100, 0) - const newLoanBorrowedAmount = currentSpotPrice - upfrontFee - const newLoanDebt = currentSpotPrice + const newLoanBorrowedAmount = maxLoanValue - upfrontFee + const newLoanDebt = maxLoanValue const newApr = calculateApr({ loanValue: newLoanBorrowedAmount, @@ -137,24 +139,24 @@ export const RefinanceModal: FC = ({ loan }) => { ) .thru((offers) => findSuitableOffer({ - loanValue: currentSpotPrice, + loanValue: maxLoanValue, offers, }), ) .value() - if (!suitableOffer) return - const loadingSnackbarId = uniqueId() try { + if (!suitableOffer) throw new Error('Unable to find the suitable offer') + const walletAndConnection = createExecutorWalletAndConnection({ wallet, connection }) const txnData = await createBorrowRefinanceTxnData( { loan, offer: suitableOffer, - solToRefinance: currentSpotPrice, + solToRefinance: maxLoanValue, aprRate: newApr, tokenType, }, diff --git a/src/pages/nftLending/OffersPage/components/OffersTabContent/components/OfferCard/OfferCard.tsx b/src/pages/nftLending/OffersPage/components/OffersTabContent/components/OfferCard/OfferCard.tsx index ae19898b3..06866ea1f 100644 --- a/src/pages/nftLending/OffersPage/components/OffersTabContent/components/OfferCard/OfferCard.tsx +++ b/src/pages/nftLending/OffersPage/components/OffersTabContent/components/OfferCard/OfferCard.tsx @@ -11,7 +11,12 @@ import { DisplayValue, createPercentValueJSX } from '@banx/components/TableCompo import { core } from '@banx/api/nft' import { ChevronDown } from '@banx/icons' import { convertToSynthetic, useSyntheticOffers } from '@banx/store/nft' -import { HealthColorIncreasing, calculateApr, getColorByPercent } from '@banx/utils' +import { + HealthColorIncreasing, + calculateApr, + calculateOfferSize, + getColorByPercent, +} from '@banx/utils' import { TOOLTIP_TEXTS } from '../../constants' @@ -81,11 +86,11 @@ interface AdditionalOfferOverviewProps { } const AdditionalOfferOverview: FC = ({ offer, market, isOpen }) => { - const { fundsSolOrTokenBalance, bidSettlement, validation, buyOrdersQuantity, hadoMarket } = offer + const { validation, buyOrdersQuantity, hadoMarket } = offer const { collectionFloor = 0, bestOffer = 0 } = market || {} const loanValue = validation.loanToValueFilter - const offerSize = fundsSolOrTokenBalance + bidSettlement + const offerSize = calculateOfferSize(offer).toNumber() const maxLtv = (loanValue / collectionFloor) * 100 const maxApr = calculateApr({ loanValue, collectionFloor, marketPubkey: hadoMarket }) / 100 diff --git a/src/pages/nftLending/OffersPage/helpers.ts b/src/pages/nftLending/OffersPage/helpers.ts index 3a4cfd092..d309dd553 100644 --- a/src/pages/nftLending/OffersPage/helpers.ts +++ b/src/pages/nftLending/OffersPage/helpers.ts @@ -4,7 +4,7 @@ import { core } from '@banx/api/nft' import { calculateBorrowedAmount, calculateLoanRepayValue, - calculateLoanValue, + calculateMaxLoanValueFromOffer, isLoanLiquidated, isLoanTerminating, } from '@banx/utils' @@ -36,7 +36,7 @@ export const findBestOffer: FindBestOffer = ({ loan, offers, walletPubkey }) => (offer) => offer.hadoMarket === loan.fraktBond.hadoMarket && offer.assetReceiver !== walletPubkey, ) - .filter((offer) => calculateLoanValue(offer) > calculateLoanRepayValue(loan)) + .filter((offer) => calculateMaxLoanValueFromOffer(offer) > calculateLoanRepayValue(loan)) .sortBy((offer) => offer.fundsSolOrTokenBalance) .value() diff --git a/src/pages/tokenLending/LeveragePage/constants.ts b/src/pages/tokenLending/LeveragePage/constants.ts index 026f238ad..a9e2ef904 100644 --- a/src/pages/tokenLending/LeveragePage/constants.ts +++ b/src/pages/tokenLending/LeveragePage/constants.ts @@ -3,6 +3,7 @@ import { LendingTokenType } from 'fbonds-core/lib/fbond-protocol/types' import { fetchSolayerApr } from '@banx/api/common' import { fetchJlpAprPercent } from '@banx/api/tokens' +import { IS_SDK_ON_DEV_CONTRACT } from '@banx/constants' import { CreateLeverageTxnData, CreateSellToRepayTokenLoanTxnData, @@ -32,7 +33,9 @@ export const JLP_PAIR: MultiplyPair = { collateralMint: new web3.PublicKey('27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4'), getCollateralYield: fetchJlpAprPercent, marketTokenType: LendingTokenType.Usdc, - marketPublicKey: new web3.PublicKey('ECxo4ZF9zyTGVXq42wwnKboX4hFNmAyhyqnyCgxVAm4S'), + marketPublicKey: !IS_SDK_ON_DEV_CONTRACT + ? new web3.PublicKey('ECxo4ZF9zyTGVXq42wwnKboX4hFNmAyhyqnyCgxVAm4S') + : new web3.PublicKey('GHMX8me6S4RGi5fs3VRpTS7VSrA5AdtwrqfM3uhKYnpr'), loanValueLimit: undefined, createLeverageTxnHandler: leverage.createLeverageTxnData, createSellToRepayTxnHandler: leverage.createSellToRepayTokenLoanTxnData, @@ -45,7 +48,9 @@ export const LRTS_PAIR: MultiplyPair = { collateralMint: lrtsSOL.LRTS_MINT, getCollateralYield: fetchSolayerApr, marketTokenType: LendingTokenType.BanxSol, - marketPublicKey: new web3.PublicKey('7EuPa26AjGdnQ7JcqM3kFhwFR4U2NQTU9guHcmaDF2G'), + marketPublicKey: !IS_SDK_ON_DEV_CONTRACT + ? new web3.PublicKey('7EuPa26AjGdnQ7JcqM3kFhwFR4U2NQTU9guHcmaDF2G') + : new web3.PublicKey('EJXya3FW1T8uZnxjtph7PxTeQJkb44eqYJwJHhTJhms3'), loanValueLimit: new BN(120 * getTokenDecimals(LendingTokenType.BanxSol)), createLeverageTxnHandler: lrtsSOL.createLrtsLeverageTxnData, createSellToRepayTxnHandler: lrtsSOL.createLrtsSellToRepayTokenLoanTxnData, diff --git a/src/pages/tokenLending/LoansTokenPage/TokenLoansContent/components/RefinanceTokenModal/RefinanceTokenModal.tsx b/src/pages/tokenLending/LoansTokenPage/TokenLoansContent/components/RefinanceTokenModal/RefinanceTokenModal.tsx index 8a364273e..293be1f4d 100644 --- a/src/pages/tokenLending/LoansTokenPage/TokenLoansContent/components/RefinanceTokenModal/RefinanceTokenModal.tsx +++ b/src/pages/tokenLending/LoansTokenPage/TokenLoansContent/components/RefinanceTokenModal/RefinanceTokenModal.tsx @@ -27,7 +27,7 @@ import { parseBorrowTokenRefinanceSimulatedAccounts, } from '@banx/transactions/tokenLending' import { - calculateIdleFundsInOffer, + calculateOfferSize, destroySnackbar, enqueueConfirmationError, enqueueSnackbar, @@ -156,9 +156,7 @@ const RefinanceTokenModal: FC = ({ loan }) => { //? Filter out user offers .filter((offer) => wallet?.publicKey?.toBase58() !== offer.assetReceiver.toBase58()) //? Filter out offers which can't cover upfront fee of prev loan - .filter((offer) => - upfrontFee.lte(calculateIdleFundsInOffer(convertBondOfferV3ToCore(offer))), - ) + .filter((offer) => upfrontFee.lte(calculateOfferSize(convertBondOfferV3ToCore(offer)))) .sortBy((offer) => offer.validation.collateralsPerToken.toNumber()) .value() ) diff --git a/src/pages/tokenLending/LoansTokenPage/TokenLoansContent/components/RefinanceTokenModal/helpers.ts b/src/pages/tokenLending/LoansTokenPage/TokenLoansContent/components/RefinanceTokenModal/helpers.ts index 68d235635..96baaa49b 100644 --- a/src/pages/tokenLending/LoansTokenPage/TokenLoansContent/components/RefinanceTokenModal/helpers.ts +++ b/src/pages/tokenLending/LoansTokenPage/TokenLoansContent/components/RefinanceTokenModal/helpers.ts @@ -7,7 +7,7 @@ import { ZERO_BN, caclulateBorrowTokenLoanValue, calcTokenLoanAprWithRepayFee, - calculateIdleFundsInOffer, + calculateOfferSize, } from '@banx/utils' export const getCurrentLoanInfo = (loan: TokenLoan) => { @@ -36,7 +36,7 @@ export const calculateTokensToGet: CalculateTokensToGet = ({ loan, marketTokenDecimals, }) => { - const maxTokenToGet = calculateIdleFundsInOffer(convertBondOfferV3ToCore(offer)) + const maxTokenToGet = calculateOfferSize(convertBondOfferV3ToCore(offer)) const tokenSupply = loan.fraktBond.fbondTokenSupply const collateralsPerToken = offer.validation.collateralsPerToken diff --git a/src/pages/tokenLending/OffersTokenPage/components/LenderTokenLoansContent/components/ExpandedCardContent/ManageModal/ClosureContent.tsx b/src/pages/tokenLending/OffersTokenPage/components/LenderTokenLoansContent/components/ExpandedCardContent/ManageModal/ClosureContent.tsx index b0bbb3915..7d704f6cd 100644 --- a/src/pages/tokenLending/OffersTokenPage/components/LenderTokenLoansContent/components/ExpandedCardContent/ManageModal/ClosureContent.tsx +++ b/src/pages/tokenLending/OffersTokenPage/components/LenderTokenLoansContent/components/ExpandedCardContent/ManageModal/ClosureContent.tsx @@ -17,8 +17,8 @@ import { useTokenBondOffers } from '@banx/hooks' import { useTokenType } from '@banx/store/common' import { caclulateBorrowTokenLoanValue, - calculateIdleFundsInOffer, calculateLentTokenValueWithInterest, + calculateOfferSize, formatValueByTokenType, getTokenDecimals, getTokenUnit, @@ -67,7 +67,7 @@ export const ClosureContent: FC<{ loan: core.TokenLoan }> = ({ loan }) => { //? Filter out user offers .filter((offer) => offer.assetReceiver.toBase58() !== publicKey?.toBase58()) //? Filter out offers that can't fully cover the loan debt - .filter((offer) => loanDebt.lt(calculateIdleFundsInOffer(convertBondOfferV3ToCore(offer)))) + .filter((offer) => loanDebt.lt(calculateOfferSize(convertBondOfferV3ToCore(offer)))) //? Filter out offers with an LTV lower than the loan LTV .filter((offer) => offer.validation.collateralsPerToken.lte(maxCollateralsPerToken)) //? Filter out offers with an APR greater than the loan APR diff --git a/src/store/nft/useSyntheticOffers.ts b/src/store/nft/useSyntheticOffers.ts index d1c2c7265..2d9608a44 100644 --- a/src/store/nft/useSyntheticOffers.ts +++ b/src/store/nft/useSyntheticOffers.ts @@ -1,16 +1,15 @@ import { PUBKEY_PLACEHOLDER } from 'fbonds-core/lib/fbond-protocol/constants' -import { calculateNextSpotPrice } from 'fbonds-core/lib/fbond-protocol/functions/perpetual' import produce from 'immer' import { create } from 'zustand' import { core } from '@banx/api/nft' +import { calculateMaxLoanValueFromOffer, сalculateFullLoansAmount } from '@banx/utils' export interface SyntheticOffer { isEdit: boolean //? if offer exits on blochain and in edit mode publicKey: string //? PUBKEY_PLACEHOLDER for offers to create loanValue: number loansAmount: number - deltaValue: number assetReceiver: string marketPubkey: string mathCounter: number @@ -65,47 +64,21 @@ export const createEmptySyntheticOffer: CreateEmptySyntheticOffer = ({ assetReceiver: walletPubkey, marketPubkey, mathCounter: 0, - deltaValue: 0, }) export const convertToSynthetic = (offer: core.Offer, isEdit = false): SyntheticOffer => { - const { publicKey, assetReceiver, hadoMarket, mathCounter, bondingCurve, buyOrdersQuantity } = - offer + const { publicKey, assetReceiver, hadoMarket, mathCounter } = offer + + const loanValue = calculateMaxLoanValueFromOffer(offer) + const loansAmount = сalculateFullLoansAmount(offer) - const loanValue = calcSyntheticLoanValue(offer) return { isEdit, publicKey, - loansAmount: loanValue > 0 ? Math.max(buyOrdersQuantity, 1) : 0, - loanValue: loanValue, + loansAmount, + loanValue, assetReceiver, marketPubkey: hadoMarket, mathCounter, - deltaValue: bondingCurve.delta, } } - -export const calcSyntheticLoanValue = (offer: core.Offer): number => { - const { - currentSpotPrice, - baseSpotPrice, - validation, - bidSettlement, - mathCounter, - bondingCurve, - buyOrdersQuantity, - } = offer - - const prevSpotPrice = calculateNextSpotPrice({ - bondingCurveType: bondingCurve.bondingType, - delta: bondingCurve.delta, - spotPrice: baseSpotPrice, - counter: mathCounter + 2, - }) - - return Math.min( - validation.loanToValueFilter, - (buyOrdersQuantity > 0 ? currentSpotPrice : 0) + bidSettlement, - prevSpotPrice, - ) -} diff --git a/src/store/token/useSyntheticTokenOffers.ts b/src/store/token/useSyntheticTokenOffers.ts index 2d95832e9..a36340019 100644 --- a/src/store/token/useSyntheticTokenOffers.ts +++ b/src/store/token/useSyntheticTokenOffers.ts @@ -5,7 +5,7 @@ import produce from 'immer' import { create } from 'zustand' import { convertBondOfferV3ToCore } from '@banx/api/nft' -import { ZERO_BN, calculateIdleFundsInOffer } from '@banx/utils' +import { ZERO_BN, calculateOfferSize } from '@banx/utils' export interface SyntheticTokenOffer { isEdit: boolean //? if offer exits on blochain and in edit mode @@ -74,7 +74,7 @@ export const createEmptySyntheticTokenOffer: CreateEmptySyntheticTokenOffer = ({ export const convertToSynthetic = (offer: BondOfferV3, isEdit = false): SyntheticTokenOffer => { const { publicKey, assetReceiver, hadoMarket } = offer - const offerSize = calculateIdleFundsInOffer(convertBondOfferV3ToCore(offer)) + const offerSize = calculateOfferSize(convertBondOfferV3ToCore(offer)) const apr = offer.loanApr.toNumber() / 100 return { diff --git a/src/transactions/nftLending/offers/createMakeBondingOfferTxnData.ts b/src/transactions/nftLending/offers/createMakeBondingOfferTxnData.ts index 28c25afee..45b74ce2b 100644 --- a/src/transactions/nftLending/offers/createMakeBondingOfferTxnData.ts +++ b/src/transactions/nftLending/offers/createMakeBondingOfferTxnData.ts @@ -26,7 +26,6 @@ export type CreateMakeBondingOfferTxnDataParams = { loanValue: number //? normal number loansAmount: number - deltaValue: number //? normal number collateralsPerToken?: BN tokenLendingApr?: number escrowBalance: BN | undefined @@ -55,7 +54,6 @@ export const createMakeBondingOfferTxnData: CreateMakeBondingOfferTxnData = asyn tokenLendingApr = 0, escrowBalance = ZERO_BN, bondFeature, - deltaValue, depositAmountToVault = ZERO_BN, } = params @@ -91,7 +89,6 @@ export const createMakeBondingOfferTxnData: CreateMakeBondingOfferTxnData = asyn }, args: { loanValue: new BN(loanValue), - delta: new BN(deltaValue), quantityOfLoans: loansAmount, bondingCurveType, bondFeature, @@ -117,7 +114,6 @@ export const createMakeBondingOfferTxnData: CreateMakeBondingOfferTxnData = asyn const offerSize = calculateNewOfferSize({ loanValue: loanValue, loansAmount, - deltaValue, }) const diff = offerSize.sub(banxSolBalance).sub(escrowBalance) diff --git a/src/transactions/nftLending/offers/createUpdateBondingOfferTxnData.ts b/src/transactions/nftLending/offers/createUpdateBondingOfferTxnData.ts index 28d6cfa09..07b93dd88 100644 --- a/src/transactions/nftLending/offers/createUpdateBondingOfferTxnData.ts +++ b/src/transactions/nftLending/offers/createUpdateBondingOfferTxnData.ts @@ -16,7 +16,7 @@ import { fetchTokenBalance } from '@banx/api/common' import { core } from '@banx/api/nft' import { BANX_SOL_ADDRESS, BONDS } from '@banx/constants' import { banxSol } from '@banx/transactions' -import { ZERO_BN, calculateIdleFundsInOffer, isBanxSolTokenType } from '@banx/utils' +import { ZERO_BN, calculateOfferSize, isBanxSolTokenType } from '@banx/utils' import { accountConverterBNAndPublicKey, parseAccountInfoByPubkey } from '../../functions' import { sendTxnPlaceHolder } from '../../helpers' @@ -24,7 +24,6 @@ import { sendTxnPlaceHolder } from '../../helpers' export type CreateUpdateBondingOfferTxnDataParams = { loanValue: number //? human number loansAmount: number - deltaValue: number //? human number offer: core.Offer tokenType: LendingTokenType collateralsPerToken?: BN @@ -46,7 +45,6 @@ export const createUpdateBondingOfferTxnData: CreateUpdateBondingOfferTxnData = const { loanValue, loansAmount, - deltaValue, offer, tokenType, tokenLendingApr = 0, @@ -86,7 +84,6 @@ export const createUpdateBondingOfferTxnData: CreateUpdateBondingOfferTxnData = args: { loanValue: new BN(loanValue), - delta: new BN(deltaValue), quantityOfLoans: loansAmount, lendingTokenType: tokenType, collateralsPerToken: new BN(collateralsPerToken), @@ -111,14 +108,13 @@ export const createUpdateBondingOfferTxnData: CreateUpdateBondingOfferTxnData = const updatedOffer = optimisticUpdateBondOfferBonding({ bondOffer: core.convertCoreOfferToBondOfferV3(offer), newLoanValue: new BN(loanValue), - newDelta: new BN(deltaValue), newQuantityOfLoans: new BN(loansAmount), collateralsPerToken, tokenLendingApr: new BN(tokenLendingApr), }) //? Optimistic offer is broken - const updatedOfferSize = calculateIdleFundsInOffer(core.convertBondOfferV3ToCore(updatedOffer)) + const updatedOfferSize = calculateOfferSize(core.convertBondOfferV3ToCore(updatedOffer)) const diff = updatedOfferSize.sub(banxSolBalance).sub(escrowBalance) diff --git a/src/utils/core/index.ts b/src/utils/core/index.ts index 388996627..891ce4126 100644 --- a/src/utils/core/index.ts +++ b/src/utils/core/index.ts @@ -1,8 +1,5 @@ -import { BN } from 'fbonds-core' import { reduce } from 'lodash' -import { core } from '@banx/api/nft' - export const calcWeightedAverage = (nums: number[], weights: number[]) => { const [sum, weightSum] = reduce( weights, @@ -18,28 +15,6 @@ export const calcWeightedAverage = (nums: number[], weights: number[]) => { return weightedAverage || 0 } -type CalculateNewOfferSizeParams = { - loanValue: number - loansAmount: number - deltaValue: number -} -export const calculateNewOfferSize = ({ - loanValue, - loansAmount, - deltaValue, -}: CalculateNewOfferSizeParams): BN => { - //? Sum of arithmetic progression - const a_n = loanValue - deltaValue * (loansAmount - 1) - const S = ((loanValue + a_n) * loansAmount) / 2 - - return new BN(S) -} - -export const calculateIdleFundsInOffer = (offer: core.Offer): BN => { - const { fundsSolOrTokenBalance, bidSettlement } = offer - return new BN(fundsSolOrTokenBalance).add(new BN(bidSettlement)) -} - export * from './loans' export * from './offers' export * from './tokenLoans' diff --git a/src/utils/core/offers/helpers.ts b/src/utils/core/offers/helpers.ts index f5b69969d..ae95d35a0 100644 --- a/src/utils/core/offers/helpers.ts +++ b/src/utils/core/offers/helpers.ts @@ -1,10 +1,10 @@ -import { calculateNextSpotPrice } from 'fbonds-core/lib/fbond-protocol/functions/perpetual' -import { getMaxLoanValueFromBondOfferBN } from 'fbonds-core/lib/fbond-protocol/helpers' +import { BN } from 'fbonds-core' import { PairState } from 'fbonds-core/lib/fbond-protocol/types' import { chain, reduce, uniqueId } from 'lodash' import { Offer, core } from '@banx/api/nft' import { UserVaultPrimitive } from '@banx/api/shared' +import { ZERO_BN } from '@banx/utils/bn' import { SimpleOffer } from './types' @@ -13,116 +13,41 @@ const spreadToSimpleOffers = ( //? use null to ignore userVaultBalance adjustments userVaultBalance: number | null, ): SimpleOffer[] => { - const { baseSpotPrice, mathCounter, buyOrdersQuantity, bondingCurve, bidSettlement, validation } = - offer - - const baseMathCounterInitial = mathCounter + 1 - - const prevSpotPriceInitial = calculateNextSpotPrice({ - bondingCurveType: bondingCurve.bondingType, - delta: bondingCurve.delta, - spotPrice: baseSpotPrice, - counter: baseMathCounterInitial + 1, - }) - // if (buyOrdersQuantity === 0) { - // const baseMathCounter = mathCounter + 1 - - // const loanValue = Math.min(validation.loanToValueFilter, bidSettlement, prevSpotPrice) - - // const simpleOffer = { - // id: uniqueId(), - // loanValue, - // hadoMarket: offer.hadoMarket, - // publicKey: offer.publicKey, - // } - // return [simpleOffer] - // } - const { - reserve, - simpleOffers: mainOffers, - prevSpotPrice, - } = Array(buyOrdersQuantity) - .fill(0) - .reduce( - (acc: { reserve: number; simpleOffers: SimpleOffer[] }, _, idx) => { - const baseMathCounter = mathCounter + 1 - idx - - const prevSpotPrice = calculateNextSpotPrice({ - bondingCurveType: bondingCurve.bondingType, - delta: bondingCurve.delta, - spotPrice: baseSpotPrice, - counter: baseMathCounter + 1, - }) - - const nextSpotPrice = calculateNextSpotPrice({ - bondingCurveType: bondingCurve.bondingType, - delta: bondingCurve.delta, - spotPrice: baseSpotPrice, - counter: baseMathCounter, - }) - - const loanValue = Math.min( - validation.loanToValueFilter, - nextSpotPrice + acc.reserve, - prevSpotPrice, - ) - - const nextReserve = acc.reserve - Math.max(loanValue - nextSpotPrice, 0) - - const simpleOffer: SimpleOffer = { - id: uniqueId(), - loanValue, - hadoMarket: offer.hadoMarket, - publicKey: offer.publicKey, - assetReceiver: offer.assetReceiver, - } - - return { - reserve: nextReserve, - simpleOffers: [...acc.simpleOffers, simpleOffer], - prevSpotPrice: nextSpotPrice, - } - }, - { reserve: bidSettlement, simpleOffers: [], prevSpotPrice: prevSpotPriceInitial }, - ) - - const reserveDenominator = Math.min(validation.loanToValueFilter, prevSpotPrice) - const reserveOrdersCount = reserveDenominator > 0 ? Math.floor(reserve / reserveDenominator) : 0 - //? Prevent almost infinite loop for offers with huge size and very small price - const MAX_OFFERS_AMOUNT_FROM_RESERVES = 100 - const reserveOffers = - reserveOrdersCount > 0 - ? Array(reserveOrdersCount) - .fill(0) - .slice(0, MAX_OFFERS_AMOUNT_FROM_RESERVES) - .map(() => ({ - id: uniqueId(), - loanValue: reserveDenominator, - hadoMarket: offer.hadoMarket, - publicKey: offer.publicKey, - assetReceiver: offer.assetReceiver, - })) - : [] - - const lastOfferValue = reserve % reserveDenominator - const lastOffer = { - id: uniqueId(), - loanValue: lastOfferValue, - hadoMarket: offer.hadoMarket, - publicKey: offer.publicKey, - assetReceiver: offer.assetReceiver, + const offerSize = calculateOfferSize(offer).toNumber() + const offerLoanValue = calculateMaxLoanValueFromOffer(offer) + const fullLoansAmount = сalculateFullLoansAmount(offer) + + const simpleOffers: SimpleOffer[] = [] + + if (fullLoansAmount >= 1) { + //? Need to use map to call uniqueId for each offer to prevent same id generation + const fullSimpleOffers: SimpleOffer[] = new Array(fullLoansAmount).fill(null).map(() => ({ + id: uniqueId(), + loanValue: offerLoanValue, + hadoMarket: offer.hadoMarket, + publicKey: offer.publicKey, + assetReceiver: offer.assetReceiver, + })) + + simpleOffers.push(...fullSimpleOffers) } - const simpleOffers = [ - ...mainOffers, - ...reserveOffers, - ...(lastOfferValue > 0 ? [lastOffer] : []), - ].sort((a, b) => b.loanValue - a.loanValue) + const additionalOfferLoanValue = offerSize - offerLoanValue * fullLoansAmount + if (additionalOfferLoanValue > 0) { + simpleOffers.push({ + id: uniqueId(), + loanValue: additionalOfferLoanValue, + hadoMarket: offer.hadoMarket, + publicKey: offer.publicKey, + assetReceiver: offer.assetReceiver, + }) + } if (userVaultBalance === null) { return simpleOffers } + //? Filter/Patch simpleOffers according to userVaultBalance const { offers } = reduce( simpleOffers, (acc: { vaultBalance: number; offers: SimpleOffer[] }, offer) => { @@ -161,7 +86,6 @@ type ConvertOffersToSimple = (params: { userVaults: UserVaultPrimitive[] | null sort?: 'desc' | 'asc' }) => SimpleOffer[] -//TODO Add param to ignore userVaults filtering for not borrow nft cases export const convertOffersToSimple: ConvertOffersToSimple = ({ offers, userVaults, @@ -190,22 +114,40 @@ export const convertOffersToSimple: ConvertOffersToSimple = ({ return convertedOffers } -export const сalculateLoansAmount = (offer: core.Offer) => { - const { fundsSolOrTokenBalance, currentSpotPrice } = offer +export const сalculateFullLoansAmount = (offer: core.Offer) => { + const { currentSpotPrice } = offer + + const offerSize = calculateOfferSize(offer).toNumber() + + const loansAmount = offerSize / currentSpotPrice + + return Math.floor(loansAmount) +} + +type CalculateNewOfferSizeParams = { + loanValue: number + loansAmount: number +} +export const calculateNewOfferSize = ({ + loanValue, + loansAmount, +}: CalculateNewOfferSizeParams): BN => { + return new BN(loanValue * loansAmount) +} - const loansAmount = fundsSolOrTokenBalance / currentSpotPrice +export const calculateOfferSize = (offer: core.Offer): BN => { + const { fundsSolOrTokenBalance, bidSettlement } = offer - return loansAmount + return new BN(fundsSolOrTokenBalance).add(new BN(bidSettlement)) } -export const calculateLoanValue = (offer: core.Offer): number => { - return getMaxLoanValueFromBondOfferBN(core.convertCoreOfferToBondOfferV3(offer)).toNumber() - // const { currentSpotPrice } = offer +export const calculateMaxLoanValueFromOffer = (offer: core.Offer): number => { + const { currentSpotPrice, validation } = offer + const offerSize = calculateOfferSize(offer).toNumber() - // const loansAmount = сalculateLoansAmount(offer) - // const loanValue = currentSpotPrice * Math.min(loansAmount, 1) + const potentialMaxLoanValue = Math.min(currentSpotPrice, validation.loanToValueFilter) - // return loanValue + return Math.min(potentialMaxLoanValue, offerSize) } export const isOfferStateClosed = (pairState: PairState) => { @@ -259,12 +201,7 @@ export const findSuitableOffer: FindSuitableOffer = ({ loanValue, offers }) => { } export const isOfferNotEmpty = (offer: core.Offer) => { - const { fundsSolOrTokenBalance, currentSpotPrice } = offer - const fullOffersAmount = Math.floor(fundsSolOrTokenBalance / currentSpotPrice) - if (fullOffersAmount >= 1) return true - const decimalLoanValue = fundsSolOrTokenBalance - currentSpotPrice * fullOffersAmount - if (decimalLoanValue > 0) return true - return false + return calculateOfferSize(offer).gt(ZERO_BN) } export type NftWithLoanValue = { diff --git a/yarn.lock b/yarn.lock index 02f910157..756d315fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7353,10 +7353,10 @@ faye-websocket@0.11.4, faye-websocket@^0.11.3: dependencies: websocket-driver ">=0.5.1" -fbonds-core@0.6.69: - version "0.6.69" - resolved "https://registry.yarnpkg.com/fbonds-core/-/fbonds-core-0.6.69.tgz#87ca0a0a78f02cf5e33174ecdc9db41298fd3ede" - integrity sha512-I+LkCu5clhHBbNIybrfqfZ3UJMvO8oE9T7FHlqKSueKMyLnutZLXrYS3vcH1peL/7PI5MjOW7PUfs1Wkzan3aw== +fbonds-core@0.6.70: + version "0.6.70" + resolved "https://registry.yarnpkg.com/fbonds-core/-/fbonds-core-0.6.70.tgz#1b0855d8bb2e1ab079a61ab5c99b59732262f40e" + integrity sha512-mZmAbozywP8UVQiSRaeMTdd7tJpNu8N3jDYNaNaJyYOXN4tsT2Mhz8bu4WQEtQSe2gRzbkLS6KsGtov5prPYvg== dependencies: "@coral-xyz/anchor" "^0.30.1" "@metaplex-foundation/mpl-token-metadata" "^2.8.1"