@hubbleprotocol/hubble-sdk
Version:
Hubble Protocol client SDK
515 lines • 20 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertTokenLamportsToDecimal = exports.addCollateralAmounts = exports.calculateStabilityProvided = exports.decimalToNumWithdrawalCap = exports.decimalToNumCollateralWithdrawalCap = exports.decimalToNumSupportedCollateral = exports.replaceBigNumberWithDecimal = exports.lamportsTokenToDecimal = exports.lamportsToDecimal = void 0;
exports.calculateTotalDebt = calculateTotalDebt;
exports.calculateTotalCollateral = calculateTotalCollateral;
exports.sub = sub;
exports.isZero = isZero;
exports.zeroCollateral = zeroCollateral;
exports.zeroExtraCollateral = zeroExtraCollateral;
exports.mulFrac = mulFrac;
exports.calculatePendingGains = calculatePendingGains;
const constants_1 = require("../constants");
const anchor_1 = require("@coral-xyz/anchor");
const decimal_js_1 = __importDefault(require("decimal.js"));
/**
* Divide all collateral amounts to convert from lamports to decimals
*/
const lamportsToDecimal = (collateral) => {
return {
sol: collateral.sol.div(constants_1.LAMPORTS_PER_SOL),
btc: collateral.btc.div(constants_1.DECIMALS_BTC),
msol: collateral.msol.div(constants_1.LAMPORTS_PER_MSOL),
ray: collateral.ray.div(constants_1.DECIMALS_RAY),
ftt: collateral.ftt.div(constants_1.DECIMALS_FTT),
eth: collateral.eth.div(constants_1.DECIMALS_ETH),
srm: collateral.srm.div(constants_1.DECIMALS_SRM),
extraCollaterals: collateral.extraCollaterals
.filter((x) => constants_1.ExtraCollateralMap.some((y) => x.tokenId.eq(y.id)))
.map((coll) => {
return {
amount: (0, exports.convertTokenLamportsToDecimal)(coll.amount, (0, constants_1.getExtraCollateralTokenById)(coll.tokenId).name),
tokenId: coll.tokenId,
};
}),
};
};
exports.lamportsToDecimal = lamportsToDecimal;
/**
* Divide all token map big amounts to convert from lamports to decimals
*/
const lamportsTokenToDecimal = (collateral) => {
return {
sol: collateral.sol.div(constants_1.LAMPORTS_PER_SOL),
btc: collateral.btc.div(constants_1.DECIMALS_BTC),
msol: collateral.msol.div(constants_1.LAMPORTS_PER_MSOL),
ray: collateral.ray.div(constants_1.DECIMALS_RAY),
ftt: collateral.ftt.div(constants_1.DECIMALS_FTT),
eth: collateral.eth.div(constants_1.DECIMALS_ETH),
srm: collateral.srm.div(constants_1.DECIMALS_SRM),
hbb: collateral.hbb.div(constants_1.HBB_DECIMALS),
};
};
exports.lamportsTokenToDecimal = lamportsTokenToDecimal;
/**
* Replace all big numbers ({@link BN} with {@link Decimal}) of an object
* We use this because Anchor deserializes everything to BN, but it doesn't support decimals.
* @param obj Object of type T
*/
const replaceBigNumberWithDecimal = (obj) => {
for (let [key, value] of Object.entries(obj)) {
if (value instanceof anchor_1.BN) {
// @ts-ignore
obj[key] = new decimal_js_1.default(value.toString());
}
}
return obj;
};
exports.replaceBigNumberWithDecimal = replaceBigNumberWithDecimal;
const decimalToNumSupportedCollateral = (supportedCollateral) => {
return {
token: supportedCollateral.token.toNumber(),
tokenCap: supportedCollateral.tokenCap,
};
};
exports.decimalToNumSupportedCollateral = decimalToNumSupportedCollateral;
const decimalToNumCollateralWithdrawalCap = (cap) => {
return {
token: cap.token.toNumber(),
tokenCap: cap.tokenCap,
};
};
exports.decimalToNumCollateralWithdrawalCap = decimalToNumCollateralWithdrawalCap;
const decimalToNumWithdrawalCap = (cap) => {
return {
configCapacity: cap.configCapacity,
currentTotal: cap.currentTotal,
lastIntervalStartTimestamp: cap.lastIntervalStartTimestamp.toNumber(),
configIntervalLengthSeconds: cap.configIntervalLengthSeconds.toNumber(),
};
};
exports.decimalToNumWithdrawalCap = decimalToNumWithdrawalCap;
/**
* Calculate stability provider's actual stability provided
* @param stabilityPoolState
* @param stabilityProviderState
*/
const calculateStabilityProvided = (stabilityPoolState, stabilityProviderState) => {
if (stabilityProviderState.depositedStablecoin.isZero() || !stabilityProviderState.userDepositSnapshot.enabled) {
return new decimal_js_1.default(0);
}
if (stabilityProviderState.userDepositSnapshot.epoch < stabilityPoolState.currentEpoch) {
return new decimal_js_1.default(0);
}
const scaleDiff = stabilityPoolState.currentScale.minus(stabilityProviderState.userDepositSnapshot.scale);
if (scaleDiff.isZero()) {
return stabilityProviderState.depositedStablecoin
.mul(stabilityPoolState.p)
.dividedBy(stabilityProviderState.userDepositSnapshot.product);
}
return stabilityProviderState.depositedStablecoin
.mul(stabilityPoolState.p)
.dividedBy(stabilityProviderState.userDepositSnapshot.product.dividedBy(constants_1.SCALE_FACTOR));
};
exports.calculateStabilityProvided = calculateStabilityProvided;
/**
* Add all collateral amounts to the second one
* @param first
* @param second
*/
const addCollateralAmounts = (first, second) => {
const leftExtra = first.extraCollaterals ? first.extraCollaterals : zeroExtraCollateral();
const rightExtra = second.extraCollaterals ? second.extraCollaterals : zeroExtraCollateral();
const extraCollaterals = [
...new Set([...leftExtra.map((x) => x.tokenId.toNumber()), ...rightExtra.map((x) => x.tokenId.toNumber())]),
];
return {
sol: first.sol.add(second.sol),
eth: first.eth.add(second.eth),
ftt: first.ftt.add(second.ftt),
ray: first.ray.add(second.ray),
btc: first.btc.add(second.btc),
srm: first.srm.add(second.srm),
msol: first.msol.add(second.msol),
extraCollaterals: extraCollaterals.map((id) => {
const left = leftExtra.find((x) => x.tokenId.eq(id))?.amount ?? new decimal_js_1.default(0);
const right = rightExtra.find((x) => x.tokenId.eq(id))?.amount ?? new decimal_js_1.default(0);
return {
amount: left.plus(right),
tokenId: new decimal_js_1.default(id),
};
}),
};
};
exports.addCollateralAmounts = addCollateralAmounts;
/**
* Calculate pending rewards debt
* @param market
* @param user
*/
const getPendingDebt = (market, user) => {
const diffStableRpt = market.stablecoinRewardPerToken.minus(user.userStablecoinRewardPerToken);
return user.status !== 1 || diffStableRpt.isZero()
? new decimal_js_1.default(0)
: diffStableRpt.mul(user.userStake).div(constants_1.DECIMAL_FACTOR);
};
/**
* Calculate pending rewards debt
* @param market
* @param user
*/
const getPendingCollateral = (market, user) => {
const diffCollRpt = sub(market.collateralRewardPerToken, user.userCollateralRewardPerToken);
return user.status !== 1 || isZero(diffCollRpt)
? zeroCollateral()
: mulFrac(diffCollRpt, user.userStake, constants_1.DECIMAL_FACTOR);
};
/**
* Calculate the user's total USDH debt (borrowed stablecoin + pending rewards)
* @param user
* @param market
*/
function calculateTotalDebt(user, market) {
const pendingDebt = getPendingDebt(market, user);
return user.borrowedStablecoin.add(pendingDebt).dividedBy(constants_1.STABLECOIN_DECIMALS);
}
/**
* Calculate the user's total collateral debt (collateral + pending rewards)
* @param user
* @param market
*/
function calculateTotalCollateral(user, market) {
const pendingCollateral = getPendingCollateral(market, user);
const collateral = (0, exports.addCollateralAmounts)((0, exports.lamportsToDecimal)(user.depositedCollateral), (0, exports.lamportsToDecimal)(user.inactiveCollateral));
return (0, exports.addCollateralAmounts)(pendingCollateral, collateral);
}
/**
* Subtract collateral amounts
* @param left
* @param right
*/
function sub(left, right) {
const leftExtra = left.extraCollaterals ? left.extraCollaterals : zeroExtraCollateral();
const rightExtra = right.extraCollaterals ? right.extraCollaterals : zeroExtraCollateral();
return {
sol: left.sol.minus(right.sol),
btc: left.btc.minus(right.btc),
eth: left.eth.minus(right.eth),
ftt: left.ftt.minus(right.ftt),
ray: left.ray.minus(right.ray),
srm: left.srm.minus(right.srm),
msol: left.msol.minus(right.msol),
extraCollaterals: leftExtra.map((coll) => {
return {
amount: coll.amount.minus(rightExtra.find((x) => x.tokenId.eq(coll.tokenId))?.amount ?? 0),
tokenId: coll.tokenId,
};
}),
};
}
/**
* Returns true if all collateral amounts equal zero
* @param coll
*/
function isZero(coll) {
return (coll.sol.isZero() &&
coll.btc.isZero() &&
coll.eth.isZero() &&
coll.ftt.isZero() &&
coll.ray.isZero() &&
coll.srm.isZero() &&
coll.msol.isZero() &&
coll.extraCollaterals.every((x) => x.amount.isZero()));
}
/**
* Create new collateral amounts with 0 collateral
*/
function zeroCollateral() {
return {
sol: new decimal_js_1.default(0),
btc: new decimal_js_1.default(0),
eth: new decimal_js_1.default(0),
ftt: new decimal_js_1.default(0),
ray: new decimal_js_1.default(0),
srm: new decimal_js_1.default(0),
msol: new decimal_js_1.default(0),
extraCollaterals: zeroExtraCollateral(),
};
}
function zeroExtraCollateral() {
const extra = [];
for (let i = 0; i < constants_1.ExtraCollateralMap.length; i++) {
extra.push({ tokenId: new decimal_js_1.default(constants_1.ExtraCollateralMap[i].id), amount: new decimal_js_1.default(0) });
}
return extra;
}
/**
Transforms collateral amounts multiplying by the fraction
* @param coll
* @param numerator
* @param denominator
*/
function mulFrac(coll, numerator, denominator) {
return {
sol: new decimal_js_1.default(coll.sol).div(denominator).mul(numerator),
btc: new decimal_js_1.default(coll.btc).div(denominator).mul(numerator),
eth: new decimal_js_1.default(coll.eth).div(denominator).mul(numerator),
ftt: new decimal_js_1.default(coll.ftt).div(denominator).mul(numerator),
ray: new decimal_js_1.default(coll.ray).div(denominator).mul(numerator),
srm: new decimal_js_1.default(coll.srm).div(denominator).mul(numerator),
msol: new decimal_js_1.default(coll.msol).div(denominator).mul(numerator),
extraCollaterals: coll.extraCollaterals.map((coll) => {
return {
amount: coll.amount.div(denominator).mul(numerator),
tokenId: coll.tokenId,
};
}),
};
}
function deserializeEpoch(data) {
const hmap = [];
const numEpochs = data[1].toNumber();
let currentCursor = 1;
for (let i = 0; i < numEpochs; i++) {
const scale = [];
currentCursor += 1;
const scaleLength = data[currentCursor].toNumber();
for (let j = 0; j < scaleLength; j++) {
const tokenMap = {
sol: new decimal_js_1.default(data[currentCursor + 1]),
eth: new decimal_js_1.default(data[currentCursor + 2]),
btc: new decimal_js_1.default(data[currentCursor + 3]),
srm: new decimal_js_1.default(data[currentCursor + 4]),
ray: new decimal_js_1.default(data[currentCursor + 5]),
ftt: new decimal_js_1.default(data[currentCursor + 6]),
hbb: new decimal_js_1.default(data[currentCursor + 7]),
msol: new decimal_js_1.default(data[currentCursor + 8]),
};
scale.push(tokenMap);
currentCursor += constants_1.EPOCH_TO_SCALE_TO_SUM_TOKENS;
}
hmap.push(scale);
}
return hmap;
}
function calculatePendingGains(stabilityPoolState, stabilityProviderState, epochToScaleToSum) {
const deserializedEpochToScaleToSum = deserializeEpoch(epochToScaleToSum);
const pendingGains = getPendingGains(stabilityProviderState, deserializedEpochToScaleToSum);
return (0, exports.lamportsTokenToDecimal)(pendingGains);
}
function getPendingGains(stabilityProviderState, epochToScaleToSum) {
const oldPendingGain = stabilityProviderState.pendingGainsPerUser;
const oldPendingGainBig = {
sol: new decimal_js_1.default(oldPendingGain.sol),
eth: new decimal_js_1.default(oldPendingGain.eth),
btc: new decimal_js_1.default(oldPendingGain.btc),
ftt: new decimal_js_1.default(oldPendingGain.ftt),
ray: new decimal_js_1.default(oldPendingGain.ray),
srm: new decimal_js_1.default(oldPendingGain.srm),
hbb: new decimal_js_1.default(oldPendingGain.hbb),
msol: new decimal_js_1.default(oldPendingGain.msol),
};
const newPendingGains = getDepositorPendingGain(stabilityProviderState, epochToScaleToSum);
return add(oldPendingGainBig, newPendingGains);
}
function add(left, right) {
return {
sol: left.sol.add(right.sol),
eth: left.eth.add(right.eth),
btc: left.btc.add(right.btc),
srm: left.srm.add(right.srm),
ray: left.ray.add(right.ray),
ftt: left.ftt.add(right.ftt),
hbb: left.hbb.add(right.hbb),
msol: left.msol.add(right.msol),
};
}
function subBig(left, right) {
return {
sol: left.sol.sub(right.sol),
eth: left.eth.sub(right.eth),
btc: left.btc.sub(right.btc),
srm: left.srm.sub(right.srm),
ray: left.ray.sub(right.ray),
ftt: left.ftt.sub(right.ftt),
hbb: left.hbb.sub(right.hbb),
msol: left.msol.sub(right.msol),
};
}
function getDepositorPendingGain(stabilityProviderState, epochToScaleToSum) {
const initialDeposit = stabilityProviderState.depositedStablecoin;
if (initialDeposit.isZero()) {
return {
sol: new decimal_js_1.default(0),
eth: new decimal_js_1.default(0),
btc: new decimal_js_1.default(0),
srm: new decimal_js_1.default(0),
ray: new decimal_js_1.default(0),
ftt: new decimal_js_1.default(0),
hbb: new decimal_js_1.default(0),
msol: new decimal_js_1.default(0),
};
}
const depositSnapshot = stabilityProviderState.userDepositSnapshot;
const epochSnapshot = depositSnapshot.epoch;
const scaleSnapshot = depositSnapshot.scale;
const sumSnapshot = depositSnapshot.sum;
const productSnapshot = depositSnapshot.product;
const sSnapshotBig = {
sol: new decimal_js_1.default(sumSnapshot.sol),
eth: new decimal_js_1.default(sumSnapshot.eth),
btc: new decimal_js_1.default(sumSnapshot.btc),
srm: new decimal_js_1.default(sumSnapshot.srm),
ray: new decimal_js_1.default(sumSnapshot.ray),
ftt: new decimal_js_1.default(sumSnapshot.ftt),
hbb: new decimal_js_1.default(sumSnapshot.hbb),
msol: new decimal_js_1.default(sumSnapshot.msol),
};
const firstPortion = subBig(getSum(epochToScaleToSum, epochSnapshot.toNumber(), scaleSnapshot.toNumber()), sSnapshotBig);
const secondPortion = div(getSum(epochToScaleToSum, epochSnapshot.toNumber(), scaleSnapshot.add(1).toNumber()), new decimal_js_1.default(constants_1.SCALE_FACTOR));
return divb(div(mul(add(firstPortion, secondPortion), initialDeposit), productSnapshot), constants_1.DECIMAL_FACTOR);
}
function getSum(epochToScaletoSum, epoch, scale) {
if (epoch < epochToScaletoSum.length) {
if (scale < epochToScaletoSum[epoch].length) {
return epochToScaletoSum[epoch][scale];
}
}
return {
sol: new decimal_js_1.default(0),
eth: new decimal_js_1.default(0),
btc: new decimal_js_1.default(0),
srm: new decimal_js_1.default(0),
ray: new decimal_js_1.default(0),
ftt: new decimal_js_1.default(0),
hbb: new decimal_js_1.default(0),
msol: new decimal_js_1.default(0),
};
}
function div(left, right) {
return {
sol: left.sol.div(right),
eth: left.eth.div(right),
btc: left.btc.div(right),
srm: left.srm.div(right),
ray: left.ray.div(right),
ftt: left.ftt.div(right),
hbb: left.hbb.div(right),
msol: left.msol.div(right),
};
}
function divb(left, right) {
return {
sol: left.sol.dividedBy(right),
eth: left.eth.dividedBy(right),
btc: left.btc.dividedBy(right),
srm: left.srm.dividedBy(right),
ray: left.ray.dividedBy(right),
ftt: left.ftt.dividedBy(right),
hbb: left.hbb.dividedBy(right),
msol: left.msol.dividedBy(right),
};
}
function mul(left, right) {
return {
sol: left.sol.mul(right),
eth: left.eth.mul(right),
btc: left.btc.mul(right),
srm: left.srm.mul(right),
ray: left.ray.mul(right),
ftt: left.ftt.mul(right),
hbb: left.hbb.mul(right),
msol: left.msol.mul(right),
};
}
const convertTokenLamportsToDecimal = (lamports, tokenName) => {
let factor = constants_1.LAMPORTS_PER_SOL;
switch (tokenName) {
case 'SOL':
case 'scnSOL':
case 'daoSOL':
case 'STSOL':
case 'MSOL':
factor = constants_1.LAMPORTS_PER_SOL;
break;
case 'ETH':
case 'wstETH':
case 'LDO':
factor = constants_1.DECIMALS_ETH;
break;
case 'BTC':
factor = constants_1.DECIMALS_BTC;
break;
case 'SRM':
factor = constants_1.DECIMALS_SRM;
break;
case 'RAY':
factor = constants_1.DECIMALS_RAY;
break;
case 'FTT':
factor = constants_1.DECIMALS_FTT;
break;
case 'JSOL':
factor = constants_1.DECIMALS_JSOL;
break;
case 'USDT':
factor = constants_1.DECIMALS_USDT;
break;
case 'CSOL':
factor = constants_1.DECIMALS_CSOL;
break;
case 'CETH':
factor = constants_1.DECIMALS_CETH;
break;
case 'CBTC':
factor = constants_1.DECIMALS_CBTC;
break;
case 'CMSOL':
factor = constants_1.DECIMALS_CMSOL;
break;
case 'CUSDC':
factor = constants_1.DECIMALS_CUSDC;
break;
case 'CSRM':
factor = constants_1.DECIMALS_CSRM;
break;
case 'CRAY':
factor = constants_1.DECIMALS_CRAY;
break;
case 'CFTT':
factor = constants_1.DECIMALS_CFTT;
break;
case 'CSTSOL':
factor = constants_1.DECIMALS_CSTSOL;
break;
case 'CSLND':
factor = constants_1.DECIMALS_CSLND;
break;
case 'CORCA':
factor = constants_1.DECIMALS_CORCA;
break;
case 'KUSDHUSDCORCA':
factor = constants_1.DECIMALS_KUSDHUSDCORCA;
break;
case 'KUSDCUSDTORCA':
factor = constants_1.DECIMALS_KUSDCUSDTORCA;
break;
case 'KSTSOLSOLORCA':
factor = constants_1.DECIMALS_KSTSOLSOLORCA;
break;
case 'KUSHUSDCORCA':
factor = constants_1.DECIMALS_KUSHUSDCORCA;
break;
default:
throw Error(`${tokenName} not supported yet`);
}
if (lamports.isZero()) {
return lamports;
}
return lamports.dividedBy(factor);
};
exports.convertTokenLamportsToDecimal = convertTokenLamportsToDecimal;
//# sourceMappingURL=mathUtils.js.map