@frakt-protocol/frakt-sdk
Version:
Frakt SDK for interacting with frakt.xyz protocols
330 lines (329 loc) • 18.1 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findRuleSetPDA = exports.getMetaplexMetadata = exports.findTokenRecordPda = exports.createAndSendV0Tx = exports.objectBNsAndPubkeysToNums = exports.getMostOptimalLoansClosestToNeededSolInBulk = exports.anchorRawBNsAndPubkeysToNumsAndStrings = exports.getMetaplexEditionPda = exports.decodeLotTicket = exports.decodeLoan = exports.decodedLoan = exports.decodedDeposit = exports.decodedPriceBasedLiquidityPool = exports.decodedTimeBasedLiquidityPool = exports.decodedFarmer = exports.decodedLendingStake = exports.decodedCollectionInfo = exports.returnAnchorProgram = void 0;
const anchor_1 = require("@project-serum/anchor");
const nft_lending_v2_1 = require("./idl/nft_lending_v2");
const common_1 = require("../common");
const constants_1 = require("./constants");
const returnAnchorProgram = (programId, connection) => new anchor_1.Program(nft_lending_v2_1.IDL, programId, new anchor_1.AnchorProvider(connection, (0, common_1.createFakeWallet)(), anchor_1.AnchorProvider.defaultOptions()));
exports.returnAnchorProgram = returnAnchorProgram;
const decodedCollectionInfo = (decodedCollection, address) => ({
collectionInfoPubkey: address.toBase58(),
creator: decodedCollection.creator.toBase58(),
liquidityPool: decodedCollection.liquidityPool.toBase58(),
pricingLookupAddress: decodedCollection.pricingLookupAddress.toBase58(),
royaltyAddress: decodedCollection.royaltyAddress.toBase58(),
royaltyFeeTime: decodedCollection.royaltyFeeTime.toNumber(),
royaltyFeePrice: decodedCollection.royaltyFeePrice.toNumber(),
loanToValue: decodedCollection.loanToValue.toNumber(),
collaterizationRate: decodedCollection.collaterizationRate.toNumber(),
availableLoanTypes: Object.keys(decodedCollection.availableLoanTypes)[0],
expirationTime: decodedCollection.expirationTime.toNumber(),
});
exports.decodedCollectionInfo = decodedCollectionInfo;
const decodedLendingStake = (decodedStake, address) => {
var _a;
return ({
lendingStakePubkey: address.toBase58(),
stakeType: Object.keys(decodedStake.stakeType)[0],
loan: decodedStake.loan.toBase58(),
stakeContract: decodedStake.stakeContract.toBase58(),
stakeConstractOptional: (_a = decodedStake.stakeConstractOptional) === null || _a === void 0 ? void 0 : _a.toBase58(),
stakeState: Object.keys(decodedStake.stakeState)[0],
identity: decodedStake.identity.toBase58(),
dataA: decodedStake.dataA.toBase58(),
dataB: decodedStake.dataB.toBase58(),
dataC: decodedStake.dataC.toBase58(),
dataD: decodedStake.dataD.toBase58(),
totalHarvested: decodedStake.totalHarvested.toNumber(),
totalHarvestedOptional: decodedStake.totalHarvestedOptional.toNumber(),
lastTime: decodedStake.lastTime.toNumber(),
});
};
exports.decodedLendingStake = decodedLendingStake;
const decodedFarmer = (decodedFarmer, address) => ({
farmerPubkey: address.toBase58(),
farm: decodedFarmer.farm.toBase58(),
identity: decodedFarmer.identity.toBase58(),
vault: decodedFarmer.vault.toBase58(),
state: Object.keys(decodedFarmer.state)[0],
gemsStaked: decodedFarmer.gemsStaked.toNumber(),
minStakingEndsTs: decodedFarmer.minStakingEndsTs.toNumber(),
cooldownEndsTs: decodedFarmer.cooldownEndsTs.toNumber(),
rewardA: decodedReward(decodedFarmer.rewardA),
rewardB: decodedReward(decodedFarmer.rewardB),
});
exports.decodedFarmer = decodedFarmer;
const decodedReward = (decodedReward) => {
var _a, _b;
return ({
paidOutReward: decodedReward.paidOutReward.toNumber(),
accruedReward: decodedReward.accruedReward.toNumber(),
variableRate: (_b = (_a = decodedReward.lastRecordedAccruedRewardPerRarityPoint) === null || _a === void 0 ? void 0 : _a.n) === null || _b === void 0 ? void 0 : _b.toNumber(),
fixedRate: decodedFixedRate(decodedReward.fixedRate),
});
};
const decodedFixedRate = (decodedFixedRate) => ({
beginScheduleTs: decodedFixedRate.beginScheduleTs.toNumber(),
beginStakingTs: decodedFixedRate.beginStakingTs.toNumber(),
lastUpdatedTs: decodedFixedRate.lastUpdatedTs.toNumber(),
promisedDuration: decodedFixedRate.promisedDuration.toNumber(),
promisedSchedule: decodedPromisedSchedule(decodedFixedRate.promisedSchedule),
});
const decodedPromisedSchedule = (decodedSchedule) => {
var _a, _b, _c, _d, _e;
return ({
baseRate: (_a = decodedSchedule.baseRate) === null || _a === void 0 ? void 0 : _a.toNumber(),
tier1: (_b = decodedSchedule.tier1) === null || _b === void 0 ? void 0 : _b.toNumber(),
tier2: (_c = decodedSchedule.tier2) === null || _c === void 0 ? void 0 : _c.toNumber(),
tier3: (_d = decodedSchedule.tier3) === null || _d === void 0 ? void 0 : _d.toNumber(),
denominator: (_e = decodedSchedule.denominator) === null || _e === void 0 ? void 0 : _e.toNumber(),
});
};
const decodedTimeBasedLiquidityPool = (decodedLiquidityPool, address) => ({
liquidityPoolPubkey: address.toBase58(),
id: decodedLiquidityPool.id.toNumber(),
rewardInterestRateTime: decodedLiquidityPool.rewardInterestRateTime.toNumber(),
feeInterestRateTime: decodedLiquidityPool.feeInterestRateTime.toNumber(),
rewardInterestRatePrice: decodedLiquidityPool.rewardInterestRatePrice.toNumber(),
feeInterestRatePrice: decodedLiquidityPool.feeInterestRatePrice.toNumber(),
liquidityAmount: decodedLiquidityPool.liquidityAmount.toNumber(),
liqOwner: decodedLiquidityPool.liqOwner.toBase58(),
amountOfStaked: decodedLiquidityPool.amountOfStaked.toNumber(),
userRewardsAmount: decodedLiquidityPool.userRewardsAmount.toNumber(),
apr: decodedLiquidityPool.apr.toNumber(),
cumulative: decodedLiquidityPool.cumulative.toNumber(),
lastTime: decodedLiquidityPool.lastTime.toNumber(),
oldCumulative: decodedLiquidityPool.oldCumulative.toNumber(),
period: decodedLiquidityPool.period.toNumber(),
});
exports.decodedTimeBasedLiquidityPool = decodedTimeBasedLiquidityPool;
const decodedPriceBasedLiquidityPool = (decodedLiquidityPool, address) => ({
liquidityPoolPubkey: address.toBase58(),
id: decodedLiquidityPool.id.toNumber(),
baseBorrowRate: decodedLiquidityPool.baseBorrowRate,
variableSlope1: decodedLiquidityPool.variableSlope1,
variableSlope2: decodedLiquidityPool.variableSlope2,
utilizationRateOptimal: decodedLiquidityPool.utilizationRateOptimal,
reserveFactor: decodedLiquidityPool.reserveFactor,
reserveAmount: decodedLiquidityPool.reserveAmount.toString(),
liquidityAmount: decodedLiquidityPool.liquidityAmount.toNumber(),
liqOwner: decodedLiquidityPool.liqOwner.toBase58(),
amountOfStaked: decodedLiquidityPool.amountOfStaked.toNumber(),
depositApr: decodedLiquidityPool.depositApr.toNumber(),
depositCumulative: decodedLiquidityPool.depositCumulative.toNumber(),
borrowApr: decodedLiquidityPool.borrowApr.toNumber(),
borrowCumulative: decodedLiquidityPool.borrowCumulative.toNumber(),
lastTime: decodedLiquidityPool.lastTime.toNumber(),
depositCommission: decodedLiquidityPool.depositCommission,
borrowCommission: decodedLiquidityPool.borrowCommission,
});
exports.decodedPriceBasedLiquidityPool = decodedPriceBasedLiquidityPool;
const decodedDeposit = (decodedDeposit, address) => ({
depositPubkey: address.toBase58(),
liquidityPool: decodedDeposit.liquidityPool.toBase58(),
user: decodedDeposit.user.toBase58(),
amount: decodedDeposit.amount.toNumber(),
stakedAt: decodedDeposit.stakedAt.toNumber(),
stakedAtCumulative: decodedDeposit.stakedAtCumulative.toNumber(),
});
exports.decodedDeposit = decodedDeposit;
const decodedLoan = (decodedLoan, address) => ({
loanPubkey: address.toBase58(),
user: decodedLoan.user.toBase58(),
nftMint: decodedLoan.nftMint.toBase58(),
nftUserTokenAccount: decodedLoan.nftUserTokenAccount.toBase58(),
liquidityPool: decodedLoan.liquidityPool.toBase58(),
collectionInfo: decodedLoan.collectionInfo.toBase58(),
startedAt: decodedLoan.startedAt.toNumber(),
expiredAt: new anchor_1.BN(decodedLoan.expiredAt || 0).toNumber(),
finishedAt: decodedLoan.finishedAt.toNumber(),
originalPrice: decodedLoan.originalPrice.toNumber(),
amountToGet: decodedLoan.amountToGet.toNumber(),
rewardAmount: decodedLoan.rewardAmount.toNumber(),
feeAmount: decodedLoan.feeAmount.toNumber(),
royaltyAmount: decodedLoan.royaltyAmount.toNumber(),
borrowedAtCumulative: new anchor_1.BN(decodedLoan.rewardInterestRate || 0).toNumber(),
alreadyPaidBack: new anchor_1.BN(decodedLoan.feeInterestRate || 0).toNumber(),
loanStatus: Object.keys(decodedLoan.loanStatus)[0],
loanType: Object.keys(decodedLoan.loanType)[0],
});
exports.decodedLoan = decodedLoan;
const decodeLoan = (buffer, connection, programId) => {
const program = (0, exports.returnAnchorProgram)(programId, connection);
return program.coder.accounts.decode('Loan', buffer);
};
exports.decodeLoan = decodeLoan;
const decodeLotTicket = (buffer, lotTicketPubkey, connection, programId) => {
const program = (0, exports.returnAnchorProgram)(programId, connection);
const rawAccount = program.coder.accounts.decode('LotTicket', buffer);
return (0, exports.anchorRawBNsAndPubkeysToNumsAndStrings)({ account: rawAccount, publicKey: lotTicketPubkey });
};
exports.decodeLotTicket = decodeLotTicket;
const getMetaplexEditionPda = (mintPubkey) => {
const editionPda = anchor_1.utils.publicKey.findProgramAddressSync([
Buffer.from(constants_1.METADATA_PREFIX),
constants_1.METADATA_PROGRAM_PUBKEY.toBuffer(),
new anchor_1.web3.PublicKey(mintPubkey).toBuffer(),
Buffer.from(constants_1.EDITION_PREFIX),
], constants_1.METADATA_PROGRAM_PUBKEY);
return editionPda[0];
};
exports.getMetaplexEditionPda = getMetaplexEditionPda;
const anchorRawBNsAndPubkeysToNumsAndStrings = (rawAccount) => {
const copyRawAccount = Object.assign({}, rawAccount);
for (let key in copyRawAccount.account) {
if (copyRawAccount.account[key] === null)
continue;
if (copyRawAccount.account[key].toNumber) {
copyRawAccount.account[key] = copyRawAccount.account[key].toNumber();
}
if (copyRawAccount.account[key].toBase58) {
copyRawAccount.account[key] = copyRawAccount.account[key].toBase58();
}
if (typeof copyRawAccount.account[key] === 'object') {
copyRawAccount.account[key] = Object.keys(copyRawAccount.account[key])[0];
}
}
return Object.assign(Object.assign({}, copyRawAccount.account), { publicKey: copyRawAccount.publicKey.toBase58() });
};
exports.anchorRawBNsAndPubkeysToNumsAndStrings = anchorRawBNsAndPubkeysToNumsAndStrings;
const knapsackAlgorithm = (items, capacity) => {
const getLast = (memo) => {
let lastRow = memo[memo.length - 1];
return lastRow[lastRow.length - 1];
};
const getSolution = (row, cap, memo) => {
const NO_SOLUTION = { maxValue: 0, subset: [] };
// the column number starts from zero.
let col = cap - 1;
let lastItem = items[row];
// The remaining capacity for the sub-problem to solve.
let remaining = cap - lastItem.w;
// Refer to the last solution for this capacity,
// which is in the cell of the previous row with the same column
let lastSolution = row > 0 ? memo[row - 1][col] || NO_SOLUTION : NO_SOLUTION;
// Refer to the last solution for the remaining capacity,
// which is in the cell of the previous row with the corresponding column
let lastSubSolution = row > 0 ? memo[row - 1][remaining - 1] || NO_SOLUTION : NO_SOLUTION;
// If any one of the items weights greater than the 'cap', return the last solution
if (remaining < 0) {
return lastSolution;
}
// Compare the current best solution for the sub-problem with a specific capacity
// to a new solution trial with the lastItem(new item) added
let lastValue = lastSolution.maxValue;
let lastSubValue = lastSubSolution.maxValue;
let newValue = lastSubValue + lastItem.v;
if (newValue >= lastValue) {
// copy the subset of the last sub-problem solution
let _lastSubSet = lastSubSolution.subset.slice();
_lastSubSet.push(lastItem);
return { maxValue: newValue, subset: _lastSubSet };
}
else {
return lastSolution;
}
};
// This implementation uses dynamic programming.
// Variable 'memo' is a grid(2-dimentional array) to store optimal solution for sub-problems,
// which will be later used as the code execution goes on.
// This is called memoization in programming.
// The cell will store best solution objects for different capacities and selectable items.
let memo = [];
// Filling the sub-problem solutions grid.
for (let i = 0; i < items.length; i++) {
// Variable 'cap' is the capacity for sub-problems. In this example, 'cap' ranges from 1 to 6.
let row = [];
for (let cap = 1; cap <= capacity; cap++) {
row.push(getSolution(i, cap, memo));
}
memo.push(row);
}
// The right-bottom-corner cell of the grid contains the final solution for the whole problem.
return getLast(memo);
};
/*
Returns most optimal loans by lowest interest using Knapsack Algorithm.
*/
const getMostOptimalLoansClosestToNeededSolInBulk = ({ neededSol, possibleLoans, }) => {
const divider = 1e7;
const preparedItems = possibleLoans.map((loan) => (Object.assign(Object.assign({}, loan), { v: Math.ceil((loan.loanValue - loan.interest) / divider), w: Math.ceil(loan.loanValue / divider) })));
const preparedNeededSol = Math.ceil(neededSol / divider);
const { maxValue, subset } = knapsackAlgorithm(preparedItems, preparedNeededSol);
const result = subset.map((item) => ({ nftMint: item.nftMint, loanValue: item.loanValue, interest: item.interest }));
return result;
};
exports.getMostOptimalLoansClosestToNeededSolInBulk = getMostOptimalLoansClosestToNeededSolInBulk;
function objectBNsAndPubkeysToNums(obj) {
const copyobj = Object.assign({}, obj);
for (const key in copyobj.account) {
if (copyobj.account[key] === null)
continue;
if (copyobj.account[key].toNumber) {
copyobj.account[key] = copyobj.account[key].toNumber();
}
if (copyobj.account[key].toBase58) {
copyobj.account[key] = copyobj.account[key].toBase58();
}
if (typeof copyobj.account[key] === 'object') {
copyobj.account[key] = Object.keys(copyobj.account[key])[0];
}
}
return Object.assign(Object.assign({}, copyobj.account), { publicKey: copyobj.publicKey.toBase58() });
}
exports.objectBNsAndPubkeysToNums = objectBNsAndPubkeysToNums;
const createAndSendV0Tx = (connection, txInstructions, signer, additionalSigners, lookupTablePubkey, skipPreflight) => __awaiter(void 0, void 0, void 0, function* () {
let latestBlockhash = yield connection.getLatestBlockhash('finalized');
const lookupTable = (yield connection.getAddressLookupTable(lookupTablePubkey)).value;
if (!lookupTable)
return;
const messageV0 = new anchor_1.web3.TransactionMessage({
payerKey: signer.publicKey,
recentBlockhash: latestBlockhash.blockhash,
instructions: txInstructions,
}).compileToV0Message([lookupTable]);
const transaction = new anchor_1.web3.VersionedTransaction(messageV0);
transaction.sign([signer, ...additionalSigners]);
const txid = yield connection.sendTransaction(transaction, {
maxRetries: 5,
skipPreflight: skipPreflight ? true : false,
});
const confirmation = yield connection.confirmTransaction({
signature: txid,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
});
console.log(`txid ${txid}`);
if (confirmation.value.err) {
throw new Error(' ❌ - Transaction not confirmed.');
}
});
exports.createAndSendV0Tx = createAndSendV0Tx;
const findTokenRecordPda = (mintPubkey, token) => {
return anchor_1.web3.PublicKey.findProgramAddressSync([
Buffer.from(constants_1.METADATA_PREFIX),
constants_1.METADATA_PROGRAM_PUBKEY.toBuffer(),
mintPubkey.toBuffer(),
Buffer.from(constants_1.TOKEN_RECORD),
token.toBuffer(),
], constants_1.METADATA_PROGRAM_PUBKEY)[0];
};
exports.findTokenRecordPda = findTokenRecordPda;
const getMetaplexMetadata = (mintPubkey) => {
return anchor_1.web3.PublicKey.findProgramAddressSync([Buffer.from(constants_1.METADATA_PREFIX), constants_1.METADATA_PROGRAM_PUBKEY.toBuffer(), mintPubkey.toBuffer()], constants_1.METADATA_PROGRAM_PUBKEY)[0];
};
exports.getMetaplexMetadata = getMetaplexMetadata;
const findRuleSetPDA = (payer, name) => __awaiter(void 0, void 0, void 0, function* () {
return (yield anchor_1.web3.PublicKey.findProgramAddress([Buffer.from('rule_set'), payer.toBuffer(), Buffer.from(name)], constants_1.AUTHORIZATION_RULES_PROGRAM))[0];
});
exports.findRuleSetPDA = findRuleSetPDA;