@rainfi/sdk
Version:
This package is used to interact with Rain.fi protocol on Solana
985 lines (984 loc) • 51 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
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());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMortgagesFeesDetailed = exports.getFeesDetailed = exports.computeInterest = exports.computeAmount = exports.parseCollectionAccount = exports.parseCustomLoanAccount = exports.parseLoanAccount = exports.parsePoolAccount = exports.getFiltersPool = exports.getDebtsSold = exports.getDebtsForSale = exports.getMortgagesFromCollectionId = exports.getMortgageFromAddress = exports.getCollection = exports.getAvailableCollections = exports.getWhitelistedCollectionFromIds = exports.getWhitelistedCollectionFromPool = exports.getAllUsersStats = exports.getFullUserStats = exports.getUserStats = exports.fetchPoolFromOwner = exports.getPoolFromOwnerAddress = exports.getCustomLoanOffersFromCollectionId = exports.getCustomLoanOffersFromLender = exports.getCustomLoanOffersFromBorrower = exports.getLoansFromCollectionId = exports.getLoansFromBorrower = exports.getLoansFromPool = exports.getAllPoolAvailable = exports.getAllPools = exports.createInitializeAccountInstruction = void 0;
const Collection_1 = require("./../generated/accounts/Collection");
const Loan_1 = require("./../generated/accounts/Loan");
const LoanKind_1 = require("./../generated/types/LoanKind");
const layout_utils_1 = require("./layout.utils");
const bn_js_1 = __importDefault(require("bn.js"));
const web3_js_1 = require("@solana/web3.js");
const accounts = __importStar(require("../generated/accounts"));
const pda_utils_1 = require("./pda.utils");
const generated_1 = require("../generated");
const constant_1 = require("./constant");
const bytes_1 = require("@coral-xyz/anchor/dist/cjs/utils/bytes");
const tools_utils_1 = require("./tools.utils");
const bs58_1 = __importDefault(require("bs58"));
const LoanStatus_1 = require("../generated/types/LoanStatus");
const spl_token_1 = require("@solana/spl-token");
Object.defineProperty(exports, "createInitializeAccountInstruction", { enumerable: true, get: function () { return spl_token_1.createInitializeAccountInstruction; } });
const REPLACE = new RegExp('\u0000', 'g');
/**
* Fetch all Rain pools depending on status. Default to 'active' pools
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param status pool status: 'active' | 'inactive' | 'all' | 'custom'
* @param customFilter Optional custom filter to fetch depending on custom condition
*
*/
function getAllPools(connection, status, customFilter) {
return __awaiter(this, void 0, void 0, function* () {
let query;
switch (status) {
case 'active':
query = yield accounts.Pool.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", generated_1.poolDiscriminator)
.addFilter("status", generated_1.PoolStatus.Ready).run(connection);
break;
case 'inactive':
query = yield accounts.Pool.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", generated_1.poolDiscriminator)
.addFilter("status", generated_1.PoolStatus.Disabled).run(connection);
break;
case 'all':
query = yield accounts.Pool.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", generated_1.poolDiscriminator)
.run(connection);
break;
case 'custom':
let param = accounts.Pool.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", generated_1.poolDiscriminator);
customFilter && (customFilter === null || customFilter === void 0 ? void 0 : customFilter.map(x => {
param = param.addFilter(x.key, x.value);
}));
query = yield param.run(connection);
break;
default:
query = yield accounts.Pool.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", generated_1.poolDiscriminator)
.addFilter("status", generated_1.PoolStatus.Ready).run(connection);
break;
}
const pool = query.map((x) => {
const poolAccountParsed = accounts.Pool.fromAccountInfo(x.account)[0].pretty();
return Object.assign({ poolAddress: x.pubkey.toString() }, parsePoolAccount(poolAccountParsed));
});
return pool
.filter((x) => (x.availableAmount + x.borrowedAmount)) // Removing the empty pools
.sort((a, b) => a.availableAmount > b.availableAmount ? -1 : a.availableAmount < b.availableAmount ? 1 : 0);
});
}
exports.getAllPools = getAllPools;
/**
* DEPRECATED Fetch all pools from rain with an open status.
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param customFilter Optional custom filter to fetch depending on custom condition
*
*/
function getAllPoolAvailable(connection, customFilter) {
return __awaiter(this, void 0, void 0, function* () {
const statusOpen = bs58_1.default.encode(new bn_js_1.default(1).toArrayLike(Buffer, 'le', 1));
const exchange = yield connection.getProgramAccounts(constant_1.RAIN_PROGRAM, { filters: getFiltersPool(customFilter ? customFilter : [{ key: "status", value: statusOpen }]) });
const pool = yield Promise.all(exchange.map((x) => __awaiter(this, void 0, void 0, function* () {
const poolAccountParsed = accounts.Pool.fromAccountInfo(x.account)[0].pretty();
return Object.assign({ poolAddress: x.pubkey.toString() }, parsePoolAccount(poolAccountParsed));
})));
return pool
.filter((x) => (x.availableAmount + x.borrowedAmount)) // Removing the empty pools
.sort((a, b) => a.availableAmount > b.availableAmount ? -1 : a.availableAmount < b.availableAmount ? 1 : 0);
});
}
exports.getAllPoolAvailable = getAllPoolAvailable;
/**
* Fetch loan from a lender address.
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param address lender pubkey
* @param customFilter Optional custom filter to fetch depending on custom condition
*
*/
function getLoansFromPool(connection, address, customFilter) {
return __awaiter(this, void 0, void 0, function* () {
let query;
if (customFilter) {
let param = accounts.Loan.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator)
.addFilter('kind', LoanKind_1.LoanKind.Loan);
customFilter && (customFilter === null || customFilter === void 0 ? void 0 : customFilter.map(x => {
param = param.addFilter(x.key, x.value);
}));
query = yield param.run(connection);
}
else {
query = yield accounts.Loan.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator)
.addFilter("lender", address)
.addFilter('kind', LoanKind_1.LoanKind.Loan).run(connection);
}
// const exchange = await connection.getProgramAccounts(RAIN_PROGRAM, { filters: getFiltersLoan(customFilter ? [{ key: "lender", value: address.toBase58() }, ...customFilter] : [{ key: "lender", value: address.toBase58() }]) })
const loans = yield Promise.all(query.map((x) => __awaiter(this, void 0, void 0, function* () {
return Object.assign({ accountAddress: x.pubkey.toString() }, parseLoanAccount(accounts.Loan.fromAccountInfo(x.account)[0].pretty()));
})));
return loans;
});
}
exports.getLoansFromPool = getLoansFromPool;
/**
* Fetch all loans from a borrower address.
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param address borrower pubkey
* @param customFilter Optional custom filter to fetch depending on custom condition
*
*/
function getLoansFromBorrower(connection, address, customFilter) {
return __awaiter(this, void 0, void 0, function* () {
let query;
if (customFilter) {
let param = accounts.Loan.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator)
.addFilter("borrower", address)
.addFilter('kind', LoanKind_1.LoanKind.Loan);
customFilter && (customFilter === null || customFilter === void 0 ? void 0 : customFilter.map(x => {
param = param.addFilter(x.key, x.value)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator);
}));
query = yield param.run(connection);
}
else {
query = yield accounts.Loan.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator)
.addFilter("borrower", address)
.addFilter('kind', LoanKind_1.LoanKind.Loan).run(connection);
}
// const exchange = await connection.getProgramAccounts(RAIN_PROGRAM, { filters: getFiltersLoan(customFilter ? [{ key: "borrower", value: address.toBase58() }, ...customFilter] : [{ key: "borrower", value: address.toBase58() }]) })
const loans = yield Promise.all(query.map((x) => __awaiter(this, void 0, void 0, function* () {
return Object.assign({ accountAddress: x.pubkey.toString() }, parseLoanAccount(accounts.Loan.fromAccountInfo(x.account)[0].pretty()));
})));
return loans;
});
}
exports.getLoansFromBorrower = getLoansFromBorrower;
/**
* Fetch all loans from a collectionId.
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param collectionId collection number/id
* @param customFilter Optional custom filter to fetch depending on custom condition
*
*/
function getLoansFromCollectionId(connection, collectionId, customFilter) {
return __awaiter(this, void 0, void 0, function* () {
let query;
if (customFilter) {
let param = accounts.Loan.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator)
.addFilter("collection", collectionId)
.addFilter('kind', LoanKind_1.LoanKind.Loan);
customFilter && (customFilter === null || customFilter === void 0 ? void 0 : customFilter.map(x => {
param = param.addFilter(x.key, x.value)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator);
}));
query = yield param.run(connection);
}
else {
query = yield accounts.Loan.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator)
.addFilter("collection", collectionId)
.addFilter('kind', LoanKind_1.LoanKind.Loan)
.run(connection);
}
// const collectionIdBuffer = new BN(collectionId).toArrayLike(Buffer, 'le', 4);
// const exchange = await connection.getProgramAccounts(RAIN_PROGRAM, { filters: getFiltersLoan(customFilter ? [{ key: "collection", value: base58.encode(collectionIdBuffer) }, ...customFilter] : [{ key: "collection", value: base58.encode(collectionIdBuffer) }]) })
const loans = yield Promise.all(query.map((x) => __awaiter(this, void 0, void 0, function* () {
return Object.assign({ accountAddress: x.pubkey.toString() }, parseLoanAccount(accounts.Loan.fromAccountInfo(x.account)[0].pretty()));
})));
return loans;
});
}
exports.getLoansFromCollectionId = getLoansFromCollectionId;
/**
* Fetch all custom loans account from a borrower address.
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param address borrower pubkey
* @param customFilter Optional custom filter to fetch depending on custom condition
*
*/
function getCustomLoanOffersFromBorrower(connection, address, customFilter) {
return __awaiter(this, void 0, void 0, function* () {
let query;
if (customFilter) {
let param = accounts.Request.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", generated_1.requestDiscriminator)
.addFilter("borrower", address);
customFilter && (customFilter === null || customFilter === void 0 ? void 0 : customFilter.map(x => {
param = param.addFilter(x.key, x.value);
}));
query = yield param.run(connection);
}
else {
query = yield accounts.Request.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", generated_1.requestDiscriminator)
.addFilter("borrower", address)
.run(connection);
}
// const gpaAccounts = await connection.getProgramAccounts(RAIN_PROGRAM, { filters: getFiltersLoanRequest(customFilter ? [{ key: "borrower", value: address.toBase58() }, ...customFilter] : [{ key: "borrower", value: address.toBase58() }]) })
const loans = query.map((x) => {
return Object.assign({ accountAddress: x.pubkey.toString(), type: 'loan' }, parseCustomLoanAccount(accounts.Request.fromAccountInfo(x.account)[0].pretty()));
}).filter(x => x.kind == 'loan');
const mortgages = query.map((x) => {
return Object.assign({ accountAddress: x.pubkey.toString(), type: 'mortgage' }, parseCustomLoanAccount(accounts.Request.fromAccountInfo(x.account)[0].pretty()));
}).filter(x => x.kind == 'mortgage');
return { loans, mortgages };
});
}
exports.getCustomLoanOffersFromBorrower = getCustomLoanOffersFromBorrower;
/**
* Fetch all custom loans account from a lender address.
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param address lender pubkey
* @param customFilter Optional custom filter to fetch depending on custom condition
*
*/
function getCustomLoanOffersFromLender(connection, address, customFilter) {
return __awaiter(this, void 0, void 0, function* () {
const poolAddress = (0, pda_utils_1.findPoolPda)(address);
let query;
if (customFilter) {
let param = accounts.Request.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", generated_1.requestDiscriminator)
.addFilter("pool", poolAddress);
customFilter && (customFilter === null || customFilter === void 0 ? void 0 : customFilter.map(x => {
param = param.addFilter(x.key, x.value);
}));
query = yield param.run(connection);
}
else {
query = yield accounts.Request.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", generated_1.requestDiscriminator)
.addFilter("pool", poolAddress)
.run(connection);
}
// const gpaAccounts = await connection.getProgramAccounts(RAIN_PROGRAM, { filters: getFiltersLoanRequest(customFilter ? [{ key: "pool", value: poolAddress.toBase58() }, ...customFilter] : [{ key: "pool", value: poolAddress.toBase58() }]) })
const loans = query.map((x) => {
return Object.assign({ accountAddress: x.pubkey.toString(), type: 'loan' }, parseCustomLoanAccount(accounts.Request.fromAccountInfo(x.account)[0].pretty()));
}).filter(x => x.kind == 'loan');
const mortgages = query.map((x) => {
return Object.assign({ accountAddress: x.pubkey.toString(), type: 'mortgage' }, parseCustomLoanAccount(accounts.Request.fromAccountInfo(x.account)[0].pretty()));
}).filter(x => x.kind == 'mortgage');
return { loans, mortgages };
});
}
exports.getCustomLoanOffersFromLender = getCustomLoanOffersFromLender;
/**
* Fetch all custom loans account for a given collectionId.
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param collectionId lender pubkey
* @param customFilter Optional custom filter to fetch depending on custom condition
*
*/
function getCustomLoanOffersFromCollectionId(connection, collectionId, customFilter) {
return __awaiter(this, void 0, void 0, function* () {
let query;
if (customFilter) {
let param = accounts.Request.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", generated_1.requestDiscriminator)
.addFilter("collection", collectionId)
.addFilter('kind', LoanKind_1.LoanKind.Loan);
customFilter && (customFilter === null || customFilter === void 0 ? void 0 : customFilter.map(x => {
param = param.addFilter(x.key, x.value);
}));
query = yield param.run(connection);
}
else {
query = yield accounts.Request.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", generated_1.requestDiscriminator)
.addFilter("collection", collectionId)
.addFilter('kind', LoanKind_1.LoanKind.Loan)
.run(connection);
}
const loans = query.map((x) => {
return Object.assign({ accountAddress: x.pubkey.toString() }, parseCustomLoanAccount(accounts.Request.fromAccountInfo(x.account)[0].pretty()));
});
return loans;
});
}
exports.getCustomLoanOffersFromCollectionId = getCustomLoanOffersFromCollectionId;
/**
* Fetch pool for a given pool owner address.
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param address owner of the pool pubkey
*
*/
function getPoolFromOwnerAddress(connection, address) {
return __awaiter(this, void 0, void 0, function* () {
const poolAddress = (0, pda_utils_1.findPoolPda)(address);
const poolData = yield connection.getAccountInfo(poolAddress);
if (!poolData)
throw new Error("Pool don't exist");
const exchange = generated_1.Pool.fromAccountInfo(poolData)[0];
const poolDataParsed = parsePoolAccount(exchange.pretty());
const pool = Object.assign(Object.assign({ poolAddress: poolAddress.toString() }, poolDataParsed), { collectionsData: yield getWhitelistedCollectionFromIds(connection, poolDataParsed.collections, poolDataParsed) });
return pool;
});
}
exports.getPoolFromOwnerAddress = getPoolFromOwnerAddress;
function fetchPoolFromOwner(connection, owner) {
return __awaiter(this, void 0, void 0, function* () {
const poolAddress = (0, pda_utils_1.findPoolPda)(owner);
return yield parsePoolAccount((yield generated_1.Pool.fromAccountAddress(connection, poolAddress)).pretty());
});
}
exports.fetchPoolFromOwner = fetchPoolFromOwner;
/**
* Fetch the user stats account of a given user.
*
* @param userPubkey user pubkey.
*
*/
function getUserStats(userPubkey) {
return __awaiter(this, void 0, void 0, function* () {
if (!this.connection) {
throw new Error('Missing wallet, connection or owner');
}
const data = (yield accounts.UserStats.fromAccountAddress(this.connection, yield (0, pda_utils_1.findSignerStatsPda)(userPubkey))).pretty();
delete data.padding;
return data;
});
}
exports.getUserStats = getUserStats;
// /**
// * Fetch the user mint stats account of a given user and a given mint.
// *
// * @param userPubkey user pubkey.
// * @param mint mint pubkey.
// *
// */
// export async function getUserMintStats(userPubkey: PublicKey, mint: PublicKey): Promise<UserMintStats> {
// if (!this.connection) {
// throw new Error('Missing wallet, connection or owner')
// }
// const data = (await accounts.UserMintStats.fromAccountAddress(this.connection, await findSignerMintStatsPda(userPubkey, mint))).pretty() as any
// delete data.padding
// return data
// }
/**
* get full user stats.
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param user owner of the pool pubkey
* @param mints Array of string | pubkey mint address to check, ex: ["So11111111111111111111111111111111111111112", "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"]
*
*/
function getFullUserStats(connection_1, user_1) {
return __awaiter(this, arguments, void 0, function* (connection, user, mints = ["So11111111111111111111111111111111111111112"]) {
try {
const userStats = (yield accounts.UserStats.fromAccountAddress(connection, (0, pda_utils_1.findSignerStatsPda)(user))).pretty();
return userStats;
}
catch (err) {
return null;
}
});
}
exports.getFullUserStats = getFullUserStats;
/**
* Fetch all users stats account.
*
* @param connection A connection to a fullnode JSON RPC endpoint
*
*/
function getAllUsersStats(connection) {
return __awaiter(this, void 0, void 0, function* () {
const response = yield connection.getProgramAccounts(constant_1.RAIN_PROGRAM, { filters: [{ memcmp: { offset: 0, bytes: bs58_1.default.encode([176, 223, 136, 27, 122, 79, 32, 227]) } }] });
const usersStats = yield Promise.all(response.map((x) => __awaiter(this, void 0, void 0, function* () {
return Object.assign({ accountAddress: x.pubkey.toString() }, accounts.UserStats.fromAccountInfo(x.account)[0].pretty());
})));
return usersStats;
});
}
exports.getAllUsersStats = getAllUsersStats;
// /**
// * Fetch all users mint stats account.
// *
// * @param connection A connection to a fullnode JSON RPC endpoint
// *
// */
// export async function getAllUsersMintStats(
// connection: Connection,
// ): Promise<UserMintStats[]> {
// const response = await connection.getProgramAccounts(RAIN_PROGRAM, { filters: [{ memcmp: { offset: 0, bytes: base58.encode([155, 52, 151, 180, 7, 32, 253, 29]) } }] })
// const usersMintStats = await Promise.all(
// response.map(async (x: any) => {
// return {
// accountAddress: x.pubkey.toString(),
// ...accounts.UserMintStats.fromAccountInfo(x.account)[0].pretty() as any
// }
// })
// )
// return usersMintStats
// }
/**
* fetch collection data for a specific pool.
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param poolOwner owner of the pool pubkey
*
*/
function getWhitelistedCollectionFromPool(connection, poolOwner) {
return __awaiter(this, void 0, void 0, function* () {
const poolAddress = (0, pda_utils_1.findPoolPda)(poolOwner);
const poolData = yield connection.getAccountInfo(poolAddress);
if (!poolData)
throw new Error("Pool don't exist");
const exchange = generated_1.Pool.fromAccountInfo(poolData)[0];
const poolAccountParsed = parsePoolAccount(exchange.pretty());
const addresses = yield Promise.all(poolAccountParsed.collections.filter(x => x.collection).map((x) => __awaiter(this, void 0, void 0, function* () { return (0, pda_utils_1.findCollectionPda)(x.collection); })));
const collectionAccounts = yield connection.getMultipleAccountsInfo(addresses);
const collections = yield Promise.all(collectionAccounts.map((x, i) => __awaiter(this, void 0, void 0, function* () {
var _a;
if (!x)
return null;
const collection = parseCollectionAccount(accounts.Collection.fromAccountInfo(x)[0].pretty());
return Object.assign(Object.assign({ collectionAddress: addresses[i].toBase58() }, collection), { maxAmountToBorrow: (0, exports.computeAmount)(((_a = poolAccountParsed.collections.find(y => y.collection == (collection === null || collection === void 0 ? void 0 : collection.collectionId))) === null || _a === void 0 ? void 0 : _a.collectionLtv) || 0, collection.floorPrice) });
})));
return (0, tools_utils_1.removeNull)(collections);
});
}
exports.getWhitelistedCollectionFromPool = getWhitelistedCollectionFromPool;
/**
* fetch collection data from array of rain collection Ids.
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param collectionsIds array of collection Ids
*
*/
function getWhitelistedCollectionFromIds(connection, collectionsIds, pool) {
return __awaiter(this, void 0, void 0, function* () {
const addresses = collectionsIds.filter(x => x.collection).map(x => (0, pda_utils_1.findCollectionPda)(x.collection));
const collectionAccounts = yield connection.getMultipleAccountsInfo(addresses);
const collections = yield Promise.all(collectionAccounts.map((x, i) => __awaiter(this, void 0, void 0, function* () {
var _a;
if (!x)
return null;
const collection = parseCollectionAccount(accounts.Collection.fromAccountInfo(x)[0].pretty());
return Object.assign(Object.assign({ collectionAddress: addresses[i].toBase58() }, collection), { maxAmountToBorrow: pool ? (0, exports.computeAmount)(((_a = pool.collections.find(y => y.collection == (collection === null || collection === void 0 ? void 0 : collection.collectionId))) === null || _a === void 0 ? void 0 : _a.collectionLtv) || 0, collection.floorPrice) : undefined });
})));
return (0, tools_utils_1.removeNull)(collections);
});
}
exports.getWhitelistedCollectionFromIds = getWhitelistedCollectionFromIds;
/**
* Fetch all collections existing in Rain.
*
* @param connection A connection to a fullnode JSON RPC endpoint
*
*/
function getAvailableCollections(connection) {
return __awaiter(this, void 0, void 0, function* () {
const collectionsRaw = yield accounts.Collection.gpaBuilder()
.addFilter("accountDiscriminator", Collection_1.collectionDiscriminator)
.run(connection);
const collections = yield Promise.all(collectionsRaw.map((x) => __awaiter(this, void 0, void 0, function* () {
return Object.assign({ collectionAddress: x.pubkey.toString() }, parseCollectionAccount(accounts.Collection.fromAccountInfo(x.account)[0].pretty()));
})));
return collections;
});
}
exports.getAvailableCollections = getAvailableCollections;
/**
* Get collection data parsed from collection address or collection Id.
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param arg collection address or collection Id
*
*/
function getCollection(connection, arg) {
return __awaiter(this, void 0, void 0, function* () {
let collectionAddress = web3_js_1.PublicKey.default;
if (typeof arg === 'number') {
collectionAddress = (0, pda_utils_1.findCollectionPda)(arg);
}
else if ((0, tools_utils_1.isValidPubkey)(arg)) {
collectionAddress = arg;
}
else {
throw new Error('Invalid argument, must be a valid collectionId or collection Address');
}
const account = yield connection.getAccountInfo(collectionAddress);
if (!account)
throw new Error("Collection doesn't exist");
return Object.assign({ collectionAddress: collectionAddress.toBase58() }, parseCollectionAccount(generated_1.Collection.fromAccountInfo(account)[0].pretty()));
});
}
exports.getCollection = getCollection;
/**
* Fetch mortgage for a specific address.
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param address mortgage account
* @param from pool or borrower
* @param ongoingMortgageOnly *OPTIONAL* default to true
* @param customFilter Optional custom filter to fetch depending on custom condition
*
*/
function getMortgageFromAddress(connection_1, address_1, as_1) {
return __awaiter(this, arguments, void 0, function* (connection, address, as, ongoingMortgageOnly = true, customFilter) {
const poolAddress = (0, pda_utils_1.findPoolPda)(address);
const pubkey = as == 'pool' ? poolAddress : address;
let query;
if (customFilter) {
let param = accounts.Loan.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator)
.addFilter('kind', LoanKind_1.LoanKind.Mortgage);
customFilter && (customFilter === null || customFilter === void 0 ? void 0 : customFilter.map(x => {
param = param.addFilter(x.key, x.value);
}));
query = yield param.run(connection);
}
else {
ongoingMortgageOnly ?
query = yield accounts.Loan.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator)
.addFilter(as, pubkey)
.addFilter("status", LoanStatus_1.LoanStatus.Ongoing)
.addFilter('kind', LoanKind_1.LoanKind.Mortgage)
.run(connection)
:
query = yield accounts.Loan.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator)
.addFilter(as, pubkey)
.addFilter('kind', LoanKind_1.LoanKind.Mortgage)
.run(connection);
}
// const payload = ongoingMortgageOnly ? [{ key: as, value: pubkey }, { key: "status", value: base58.encode(new BN(0).toArrayLike(Buffer, 'le', 1)) }]
// : [{ key: as, value: pubkey }]
// const exchange = await connection.getProgramAccounts(RAIN_PROGRAM, { filters: getFiltersMortgage(customFilter ? customFilter : payload) })
const loans = query.map((x) => {
return Object.assign({ accountAddress: x.pubkey.toString() }, parseLoanAccount(accounts.Loan.fromAccountInfo(x.account)[0].pretty()));
});
return loans;
});
}
exports.getMortgageFromAddress = getMortgageFromAddress;
/**
* Fetch ALL mortgages for a given collectionId.
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param collectionId collection number/id
* @param customFilter Optional custom filter to fetch depending on custom condition
*
*/
function getMortgagesFromCollectionId(connection, collectionId, customFilter) {
return __awaiter(this, void 0, void 0, function* () {
let query;
if (customFilter) {
let param = accounts.Loan.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator)
.addFilter("collection", collectionId)
.addFilter('kind', LoanKind_1.LoanKind.Mortgage);
customFilter && (customFilter === null || customFilter === void 0 ? void 0 : customFilter.map(x => {
param = param.addFilter(x.key, x.value);
}));
query = yield param.run(connection);
}
else {
query = yield accounts.Loan.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator)
.addFilter("collection", collectionId)
.addFilter('kind', LoanKind_1.LoanKind.Mortgage)
.run(connection);
}
// const collectionIdBuffer = new BN(collectionId).toArrayLike(Buffer, 'le', 4);
// const exchange = await connection.getProgramAccounts(RAIN_PROGRAM, { filters: getFiltersLoan(customFilter ? [{ key: "collection", value: base58.encode(collectionIdBuffer) }, ...customFilter] : [{ key: "collection", value: base58.encode(collectionIdBuffer) }]) })
const loans = yield Promise.all(query.map((x) => __awaiter(this, void 0, void 0, function* () {
return Object.assign({ accountAddress: x.pubkey.toString() }, parseLoanAccount(accounts.Loan.fromAccountInfo(x.account)[0].pretty()));
})));
return loans;
});
}
exports.getMortgagesFromCollectionId = getMortgagesFromCollectionId;
/**
* get loans and mortgages for sale.
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param collectionId OPTIONAL collection ID
* @param customFilter Optional custom filter to fetch depending on custom condition
*
*/
function getDebtsForSale(connection, collectionId, customFilter) {
return __awaiter(this, void 0, void 0, function* () {
let query;
if (customFilter) {
let param = accounts.Loan.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator)
.addInnerFilter('listing.isListed', true);
// .addFilter('kind', LoanKind.Mortgage)
customFilter && (customFilter === null || customFilter === void 0 ? void 0 : customFilter.map(x => {
param = param.addFilter(x.key, x.value);
}));
query = yield param.run(connection);
}
else {
collectionId ?
query = yield accounts.Loan.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator)
.addInnerFilter('listing.isListed', true)
// .addFilter('kind', LoanKind.Mortgage)
.addFilter('collection', collectionId)
.run(connection)
:
query = yield accounts.Loan.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator)
.addInnerFilter('listing.isListed', true)
// .addFilter('kind', LoanKind.Mortgage)
.run(connection);
}
// const exchange = await connection.getProgramAccounts(RAIN_PROGRAM, {
// filters:
// getFiltersMortgage(customFilter ?
// [{ key: 'is_for_sale', value: base58.encode(new BN(1).toArrayLike(Buffer, 'le', 1)) }, ...customFilter]
// :
// collectionId ?
// [{ key: 'is_for_sale', value: base58.encode(new BN(1).toArrayLike(Buffer, 'le', 1)) }, { key: "collection", value: base58.encode(new BN(collectionId).toArrayLike(Buffer, 'le', 4)) }]
// :
// [{ key: 'is_for_sale', value: base58.encode(new BN(1).toArrayLike(Buffer, 'le', 1)) }]
// )
// })
const loans = query.map((x) => {
return Object.assign({ accountAddress: x.pubkey.toString() }, parseLoanAccount(accounts.Loan.fromAccountInfo(x.account)[0].pretty()));
});
return loans;
});
}
exports.getDebtsForSale = getDebtsForSale;
/**
* get sold loans and mortgages.
*
* @param connection A connection to a fullnode JSON RPC endpoint
* @param customFilter Optional custom filter to fetch depending on custom condition
*
*/
function getDebtsSold(connection, customFilter) {
return __awaiter(this, void 0, void 0, function* () {
let query;
if (customFilter) {
let param = accounts.Loan.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator)
.addFilter("status", LoanStatus_1.LoanStatus.Sold);
// .addFilter('kind', LoanKind.Mortgage)
customFilter && (customFilter === null || customFilter === void 0 ? void 0 : customFilter.map(x => {
param = param.addFilter(x.key, x.value);
}));
query = yield param.run(connection);
}
else {
query = yield accounts.Loan.gpaBuilder(constant_1.RAIN_PROGRAM)
.addFilter("accountDiscriminator", Loan_1.loanDiscriminator)
.addFilter("status", LoanStatus_1.LoanStatus.Sold)
// .addFilter('kind', LoanKind.Mortgage)
.run(connection);
}
// const exchange = await connection.getProgramAccounts(RAIN_PROGRAM, {
// filters:
// getFiltersMortgage(customFilter ?
// [{ key: 'status', value: base58.encode(new BN(3).toArrayLike(Buffer, 'le', 1)) }, ...customFilter]
// :
// [{ key: 'status', value: base58.encode(new BN(3).toArrayLike(Buffer, 'le', 1)) }])
// })
const loans = query.map((x) => {
return Object.assign({ accountAddress: x.pubkey.toString() }, parseLoanAccount(accounts.Loan.fromAccountInfo(x.account)[0].pretty()));
});
return loans;
});
}
exports.getDebtsSold = getDebtsSold;
/**
* Get pool filter for getProgramAccount function.
*
* @param payload
*
*/
function getFiltersPool(payload) {
const memcmp = [
{ memcmp: { offset: 0, bytes: bs58_1.default.encode(generated_1.poolDiscriminator) } }
];
payload.map(x => memcmp.push({ memcmp: { offset: layout_utils_1.POOL_ACCOUNT_LAYOUT.offsetOf(x.key), bytes: x.value } }));
return memcmp;
}
exports.getFiltersPool = getFiltersPool;
/**
* Get more readable pool account.
*
* @param data prettyfied pool account
*
*/
function parsePoolAccount(data) {
var _a, _b, _c, _d, _e, _f, _g;
delete data.padding1;
delete data.padding2;
delete data.padding3;
delete data.padding4;
const loanCurve = Object.assign(Object.assign({}, data.loanCurve), { maxAmount: (_a = data.loanCurve.maxAmount) === null || _a === void 0 ? void 0 : _a.toNumber(), maxDuration: (_b = data.loanCurve.maxDuration) === null || _b === void 0 ? void 0 : _b.toNumber() });
const mortgageCurve = Object.assign(Object.assign({}, data.mortgageCurve), { maxAmount: (_c = data.mortgageCurve.maxAmount) === null || _c === void 0 ? void 0 : _c.toNumber(), maxDuration: (_d = data.mortgageCurve.maxDuration) === null || _d === void 0 ? void 0 : _d.toNumber() });
const conditions = Object.assign(Object.assign({}, data.conditions), { minAge: (_e = data.conditions.minAge) === null || _e === void 0 ? void 0 : _e.toNumber(), minLoan: (_f = data.conditions.minLoan) === null || _f === void 0 ? void 0 : _f.toNumber(), minVolume: (_g = data.conditions.minVolume) === null || _g === void 0 ? void 0 : _g.toNumber() });
return Object.assign(Object.assign({}, data), { collections: data.collections.filter((x) => x.collection), status: data.status == "PoolStatus.Disabled" ? "disabled" : data.status == "PoolStatus.Ready" ? "ready" : "pending", loanCurve,
mortgageCurve,
conditions
// interestRate: isDynamic ? dynamicInterestRate : parseInt(data.interestRate),
});
}
exports.parsePoolAccount = parsePoolAccount;
/**
* Get more readable Loan account.
*
* @param data prettyfied loan account
*
*/
function parseLoanAccount(data) {
var _a, _b;
const parsedStatus = {
"LoanStatus.Ongoing": "ongoing",
"LoanStatus.Repaid": "repaid",
"LoanStatus.Liquidated": "liquidated",
"LoanStatus.Sold": "sold",
};
const kind = {
"LoanKind.Loan": "loan",
"LoanKind.Mortgage": "mortgage",
};
const parsedMarketplace = {
"Marketplace.None": "none",
"Marketplace.Solanart": "solanart",
"Marketplace.Auctionhouse": "auctionhouse",
"Marketplace.Hyperspace": "hyperspace",
"Marketplace.Hadeswap": "hadeswap",
"Marketplace.TensorswapOrder": "tensorswap_order",
"Marketplace.TensorswapListing": "tensorswap_listing",
"Marketplace.Rain": "rain",
"Marketplace.Yaww": "yaww",
"Marketplace.MagicEden": "magiceden-v2",
};
const sale = Object.assign(Object.assign({}, data.sale), { currency: data.sale.currency && ((_a = data.sale.currency) === null || _a === void 0 ? void 0 : _a.toBase58()), salePrice: (_b = data.sale.salePrice) === null || _b === void 0 ? void 0 : _b.toNumber() });
delete data.padding;
const loan = Object.assign(Object.assign({}, data), { platform: 'Rain', status: parsedStatus[data.status], kind: kind[data.kind], marketplace: parsedMarketplace[data.marketplace], sale });
return loan;
}
exports.parseLoanAccount = parseLoanAccount;
/**
* Get more readable Custom Loan account.
*
* @param data prettyfied custom loan account
*
*/
function parseCustomLoanAccount(data) {
const parsedStatus = {
"RequestStatus.Pending": "pending",
"RequestStatus.Accepted": "accepted",
};
const kind = {
"LoanKind.Loan": "loan",
"LoanKind.Mortgage": "mortgage",
};
const parsedMarketplace = {
"Marketplace.None": "none",
"Marketplace.Solanart": "solanart",
"Marketplace.Auctionhouse": "auctionhouse",
"Marketplace.Hyperspace": "hyperspace",
"Marketplace.Hadeswap": "hadeswap",
"Marketplace.TensorswapOrder": "tensorswap_order",
"Marketplace.TensorswapListing": "tensorswap_listing",
"Marketplace.Rain": "rain",
"Marketplace.Yaww": "yaww",
"Marketplace.MagicEden": "magiceden-v2",
};
delete data.padding;
const loan = Object.assign(Object.assign({}, data), { status: parsedStatus[data.status], marketplace: parsedMarketplace[data.marketplace], kind: kind[data.kind] });
return loan;
}
exports.parseCustomLoanAccount = parseCustomLoanAccount;
/**
* Get more readable Collection account.
*
* @param data prettyfied Collection account
*
*/
function parseCollectionAccount(data) {
delete data.padding;
return Object.assign(Object.assign({}, data), { name: bytes_1.utf8.decode(Buffer.from(data.name)).replace(REPLACE, '') });
}
exports.parseCollectionAccount = parseCollectionAccount;
const roundDown = (number, decimals) => {
decimals = decimals || 0;
return (Math.floor(number * Math.pow(10, decimals)) / Math.pow(10, decimals));
};
// /**
// * Get the interest that user will pay with given parameter.
// *
// * @param data Pool account
// * @param amount amount user want to borrow in Lamports, must be lower than floorPrice * (loanToValue / 10000)
// * @param floorPrice floorPrice of the collection
// * @param durationAmount duration in days chosen by the user of the loan, default to 1 day
// *
// */
// export const computeInterest = (data: Pool, floorPrice: number, amount: number, durationAmount = 1) => {
// if (amount > computeAmount(data.loanToValue, floorPrice)) { //throw new Error("Amount too high")
// return data.interestType === "dynamic" ? Math.round((data.interestRate * 100000 / ((((durationAmount * 10) + data.interestGap) / 365) * 100)) + ((data.interestCurve * ((((data.borrowedAmount + floorPrice) * 100000) / (data.borrowedAmount + data.availableAmount))) * ((durationAmount * 1000000) / 365))) / 10000)
// : data.interestRate / 100
// } else {
// return data.interestType === "dynamic" ? Math.round((data.interestRate * 100000 / ((((durationAmount * 10) + data.interestGap) / 365) * 100)) + ((data.interestCurve * ((((data.borrowedAmount + amount) * 100000) / (data.borrowedAmount + data.availableAmount))) * ((durationAmount * 1000000) / 365))) / 10000)
// : data.interestRate / 100
// }
// }
/**
* Get the amount argument to pass to Borrow function.
*
* @param ltv loanToValue from the pool
* @param amount amount to borrow, must be lower than floorPrice * (loanToValue / 10000)
*
*/
const computeAmount = (ltv, amount) => {
return amount * (ltv / 10000);
};
exports.computeAmount = computeAmount;
/**
* Get the interest that user will pay with given parameter.
*
* @param amount amount user want to borrow in LAMPORTS
* @param duration duration in days chosen by the user of the loan
* @param interestRate param from pool in basis points, 100 points means 1%
* @param totalAmount total liquidity of the pool in LAMPORTS
* @param borrowedAmount currently borrowed liquidity of the pool in LAMPORTS
* @param baseInterest param from pool in basis points, 100 points means 1%
* @param maxDuration max duration of the loan in days
* @param curveRate param from pool in basis points, 100 points means 1%
* @param curveRateDay param from pool in basis points, 100 points means 1%
*
*/
const computeInterest = (amount, duration, interestRate, totalAmount, borrowedAmount, baseInterest, maxDuration, curveRate, curveRateDay, kind = 'loan') => {
const PRECISION = new bn_js_1.default(1e9);
const interestRatePrecise = new bn_js_1.default(interestRate)
.mul(PRECISION)
.div(new bn_js_1.default(100));
const curveRateDayPrecise = new bn_js_1.default(curveRateDay)
.mul(PRECISION)
.div(new bn_js_1.default(100));
const liquidity = totalAmount.sub(borrowedAmount).sub(amount);
const ratio = PRECISION.sub(liquidity.mul(PRECISION).div(totalAmount));
const curveRateV2 = interestRatePrecise.add(ratio.mul(new bn_js_1.default(curveRate)).div(new bn_js_1.default(100)));
let x = curveRateDayPrecise.mul(new bn_js_1.default(duration)).div(new bn_js_1.default(maxDuration));
let approxExp = PRECISION.clone();
let factorial = PRECISION.clone();
let poweredX = x.mul(PRECISION);
for (let i = 1; i <= 10; i++) {
factorial = factorial.mul(new bn_js_1.default(i));
approxExp = approxExp.add(poweredX.div(factorial));
poweredX = poweredX.mul(x).div(PRECISION);
}
const baseInterestPrecise = new bn_js_1.default(baseInterest)
.mul(PRECISION)
.div(new bn_js_1.default(100));
const interest = bn_js_1.default.max(baseInterestPrecise, curveRateV2.mul(approxExp).div(PRECISION));
const interestLamports = interest.mul(amount).div(new bn_js_1.default(100)).div(PRECISION);
const fees = kind == 'loan' ? 15 : 20;
return {
interestLamports: interestLamports,
interestPercentage: interest,
rainFees: interestLamports.mul(new bn_js_1.default(fees)).div(new bn_js_1.default(100))
};
};
exports.computeInterest = computeInterest;
/**
* Get the interest for a LOAN that user will pay with given parameters.
*
* @param pool Pool account that will fund the loan
* @param amount amount user want to borrow in lamports
* @param duration duration in days chosen by the user of the loan
*
*/
const getFeesDetailed = (pool, amount, duration) => {
const emptyResponse = {
feesInPercentage: 0,
feesInSol: 0,
rainFees: 0,
amountToLoan: 0,
duration: 0,
};
try {
if (!(pool === null || pool === void 0 ? void 0 : pool.totalAmount) || !amount || !duration) {
return emptyResponse;
}
const { interestLamports, interestPercentage, rainFees } = (0, exports.computeInterest)(new bn_js_1.default(Math.round(amount)), duration, pool.loanCurve.interestRate, new bn_js_1.default(pool.totalAmount), new bn_js_1.default(pool.borrowedAmount), pool.loanCurve.baseInterest, pool.loanCurve.maxDuration / (60 * 60 * 24), pool.loanCurve.curveRate, pool.loanCurve.curveRateDay);
return {
feesInPercentage: (interestPercentage === null || interestPercentage === void 0 ? void 0 : interestPercentage.toNumber()) / 1e9,
feesInSol: interestLamports === null || interestLamports === void 0 ? void 0 : interestLamports.toNumber(),
rainFees: rainFees === null || rainFees === void 0 ? void 0 : rainFees.toNumber(),
amountToLoan: Math.floor(amount),
duration,
};
}
catch (_a) {
return emptyResponse;
}
};
exports.getFeesDetailed = getFeesDetailed;
/**
* Get the interest for a MORTGAGE that user will pay with given parameters.
*
* @param pool Pool account that will finance the mortgage
* @param amount amount user want to borrow in lamports, should be 50% of the NFT price
* @param duration duration in days chosen by the user of the loan
*
*/
const getMortgagesFeesDetailed = (pool, amount, duration) => {
const emptyResponse = {
feesInPercentage: 0,
feesInSol: 0,
rainFees: 0,
amountToLoan: 0,
duration: 0,
};
try {
if (!(pool === null || pool === void 0 ? void 0 : pool.totalAmount) || !amount || !duration) {
return emptyResponse;
}