@indigo-labs/indigo-sdk
Version:
Indigo SDK for interacting with Indigo endpoints via lucid-evolution
176 lines (162 loc) • 4.84 kB
text/typescript
import { Network, slotToUnixTime, UTxO } from '@lucid-evolution/lucid';
import { calculateAccruedInterest } from '../interest-oracle/helpers';
import { match, P } from 'ts-pattern';
import { calculateFeeFromRatio } from '../../utils/indigo-helpers';
import { assetClassValueOf } from '@3rd-eye-labs/cardano-offchain-common';
import { InterestOracleDatum } from '../interest-oracle/types-new';
import { CDPContent } from './types-new';
import {
Rational,
rationalAdd,
rationalCeil,
rationalDiv,
rationalFloor,
rationalFromInt,
rationalMul,
rationalNegate,
rationalSub,
} from '../../types/rational';
/**
* Amount of iasset equal in value to the given number of collateral amount.
*/
export function iassetValueOfCollateral(
collateralAmt: bigint,
oraclePrice: Rational,
): bigint {
return rationalFloor(
rationalDiv(rationalFromInt(collateralAmt), oraclePrice),
);
}
/**
* This is mostly for debugging purposes.
*/
export function cdpCollateralRatioPercentage(
currentSlot: number,
iassetPrice: Rational,
cdpUtxo: UTxO,
cdpContent: CDPContent,
interestOracleDatum: InterestOracleDatum,
network: Network,
): number {
const currentTime = BigInt(slotToUnixTime(network, currentSlot));
return match(cdpContent.cdpFees)
.with({ ActiveCDPInterestTracking: P.select() }, (interest) => {
const interestAmt = calculateAccruedInterest(
currentTime,
interest.unitaryInterestSnapshot,
cdpContent.mintedAmt,
interest.lastSettled,
interestOracleDatum,
);
const collateral = assetClassValueOf(
cdpUtxo.assets,
cdpContent.collateralAsset,
);
const ratio = rationalDiv(
rationalFromInt(collateral),
rationalMul(
rationalFromInt(cdpContent.mintedAmt + interestAmt),
iassetPrice,
),
);
return Number((ratio.numerator * 100n) / ratio.denominator);
})
.with({ FrozenCDPAccumulatedFees: P.any }, () => 0)
.exhaustive();
}
/**
* The amount of iassets to redeem to reach the RMR.
*/
export function calculateIAssetRedemptionAmt(
collateralAmt: bigint,
mintedAmt: bigint,
price: Rational,
rmr: Rational,
): bigint {
return rationalCeil(
rationalDiv(
rationalAdd(
rationalNegate(rationalDiv(rationalFromInt(collateralAmt), price)),
rationalMul(rmr, rationalFromInt(mintedAmt)),
),
rationalSub(rmr, rationalFromInt(1n)),
),
);
}
/**
* Calculates the allowable redemption amount so the min collateral constraint still holds.
* It caps the redemption amount to still satisfy the min collateral.
*
* Returns uncapped max iassets /\ capped max iassets
*
* The derived calculation comes from the following equation where:
* c - collateral
* m - min collateral
* r - reimburstment ratio
* x - redemption amount
*
* `c - x + r * x = m`
* `-x + r * x = m - c`
* `x * (r - 1) = m - c`
* `x = (m - c) / r - 1`
*/
export function calculateMinCollateralCappedIAssetRedemptionAmt(
collateralAmt: bigint,
mintedAmt: bigint,
price: Rational,
rmr: Rational,
reimbursementRatio: Rational,
minCollateral: bigint,
): {
uncappedIAssetRedemptionAmt: bigint;
cappedIAssetRedemptionAmt: bigint;
} {
const uncappedMaxIAssetRedemptionAmt = calculateIAssetRedemptionAmt(
collateralAmt,
mintedAmt,
price,
rmr,
);
const uncappedMaxRedemptionLovelacesAmt = rationalFloor(
rationalMul(price, rationalFromInt(uncappedMaxIAssetRedemptionAmt)),
);
const maxReimburstment = calculateFeeFromRatio(
reimbursementRatio,
uncappedMaxRedemptionLovelacesAmt,
);
const doesMaxBreakMinCollateral =
collateralAmt - uncappedMaxRedemptionLovelacesAmt + maxReimburstment <
minCollateral;
if (!doesMaxBreakMinCollateral) {
return {
uncappedIAssetRedemptionAmt: uncappedMaxIAssetRedemptionAmt,
cappedIAssetRedemptionAmt: uncappedMaxIAssetRedemptionAmt,
};
// already below min collateral
} else if (collateralAmt <= minCollateral) {
return {
uncappedIAssetRedemptionAmt: uncappedMaxIAssetRedemptionAmt,
cappedIAssetRedemptionAmt: 0n,
};
} else {
const resLovelaces = rationalDiv(
rationalFromInt(minCollateral - collateralAmt),
rationalSub(reimbursementRatio, rationalFromInt(1n)),
);
const resIAsset = rationalDiv(resLovelaces, price);
return {
uncappedIAssetRedemptionAmt: uncappedMaxIAssetRedemptionAmt,
cappedIAssetRedemptionAmt: rationalFloor(resIAsset),
};
}
}
export function adjustPriceToDecimals(
extraDecimals: bigint,
price: Rational,
): Rational {
return extraDecimals === 0n
? price
: extraDecimals > 0n
? rationalMul(price, rationalFromInt(10n ** extraDecimals))
: rationalDiv(price, rationalFromInt(10n ** -extraDecimals));
}