UNPKG

@rainfi/sdk

Version:

This package is used to interact with Rain.fi protocol on Solana

985 lines (984 loc) 51 kB
"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; }