Pool
Pool is a class representing one, particular pool in the Rain protocol. It gives you access to all pool's properties, utility functions and instructions.
Every address can only have one pool. SDK takes advantage of that, and lets you initialize new pool having only the owner account.
Initialization
To initialize new Pool
class, you'll need three things:
- connection to the Solana blockchain (
Connection
object), - pool owner (as mentioned, each owner can only have one pool. All pool data will be fetched by pool's owner),
- address, which will interact with the pool. SDK needs it to generate instructions, which will be later signed and executed by the user.
To initialize Pool
class, use snippet below:
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
collections()
collections()
function allows you to fetch all collections
whitelisted by particular Pool.
Function will also return all collections' data - creators, floor price, supply, etc.
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
const collections = await pool.collections();
console.log({collections});
fetch()
When you initialize new Pool()
class, you get access to instructions and functions.
However, if you want to explore pool's properties/data, you have to use fetch()
function.
It will return all data assigned to particular pool.
Usage:
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
const poolData = await pool.fetch();
console.log({poolData});
fees()
Each pool initialized inside Rain protocol can customize its fees. fees()
function gives you
ability to estimate fees that particular pool will take for a loan.
Function accepts loan's properties (duration, amount, etc) as parameters. Usage:
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
const rain = new Rain(
connection,
publicKey,
);
// See docs for utils documentation.
const { getCollection } = rain.utils;
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
// See docs for `fetch()` documentation.
const poolData = await pool.fetch();
const feesBreakdown = await pool.fees(
50 * LAMPORTS_PER_SOL, // Borrowed amount.
poolData.maxDuration / 60 / 60 / 24 // Loan duration in days.
);
console.log({feesBreakdown});
repay()
repay()
function is used to generate transaction instructions for repaying ongoing loan. The function accepts two parameters -
loan account address, and borrowed amount (in lamports).
As a result, function returns array of Solana SDK-compatibile TransactionInstruction
.
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
const rain = new Rain(
connection,
publicKey,
);
// See docs for utils documentation.
const { getLoansFromPool } = rain.utils;
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
const poolData = await pool.fetch();
const loans = await getLoansFromPool(
connection,
new PublicKey(poolData.poolAddress),
[{
key: "borrower",
value: publicKey.toString()
}]
);
const loanToRepay = loans[0];
const { accountAddress, amount } = loanToRepay;
const ix = await pool.repay(
new PublicKey(accountAddress),
amount
);
const tx = new Transaction();
tx.add(...ix);
// Transaction is ready to be signed & sent to the Solana network.
// Visit https://solanacookbook.com/ to see how to sign & send transactions.
borrow()
As the name suggests, borrow()
function will return transaction instruction(s) necessary to borrow an amount of Solana, from particular Pool, against an NFT as a collateral.
Function accepts PublicKey
representing collateralized NFT as the first parameter. Then comes loan duration, loan amount and fees calculated using getFeesDetailed()
function.
The last argument is slippage - customize it yourself. Recommended slippage is 20, which is equal to 0.2%.
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
const rain = new Rain(
connection,
publicKey,
);
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
const { getCollection, getFeesDetailed, computeAmount } = rain.utils;
const poolData = await pool.fetch();
const duration = poolData.maxDuration / 60 / 60 / 24;
const { floorPrice } = await getCollection(
connection,
517
);
const amount = computeAmount(poolData.loanToValue, floorPrice);
const { feesInSol } = getFeesDetailed(
poolData,
amount,
duration,
);
const { instruction, signers } = await pool.borrow(
new PublicKey("<NFT Mint Address>"), // NFT you want to collateralize.
duration, // Loan duration.
amount, // Amount of Solana you want to borrow (in lamports).
feesInSol, // Interest returned from getFeesDetailed() function.
50 // Slippage. 500 = 5%
);
const transaction = new Transaction();
transaction.add(...instruction);
transaction.partialSign(signers);
// Transaction is ready to be signed & sent to the Solana network.
// Visit https://solanacookbook.com/ to see how to sign & send transactions.
mortgagesFees()
Each pool initialized inside Rain protocol can customize its fees on both loans and mortgages. fees()
function, which we've discussed earlier, gives you
ability to estimate fees that particular pool will take for a loan.
mortgagesFees()
is similar - it allows you to calculate fees that particular pool will take for a mortgage.
Function accepts mortgage's properties (duration, amount) as parameters. Usage:
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
const rain = new Rain(
connection,
publicKey,
);
// See docs for utils documentation.
const { getCollection } = rain.utils;
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
// See docs for `fetch()` documentation.
const poolData = await pool.fetch();
const feesBreakdown = await pool.mortgagesFees(
50 * LAMPORTS_PER_SOL, // Borrowed amount.
poolData.mortgageMaxDuration / 60 / 60 / 24 // Loan duration in days.
);
console.log({feesBreakdown});
loans()
loans()
function allows you to fetch loans from particular pool.
To start using it, you first have to
specify status of loans you want to fetch. To fetch all, use "all"
. To only get ended (repaid/liquidated) loans, use "history"
. To
only fetch ongoing loans, use "ongoing"
.
As the second parameter, you can (don't have to) specify particular borrower. If specified, only loans taken by this given address will be returned.
Usage:
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
const rain = new Rain(
connection,
publicKey,
);
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
const {loans} = pool;
const allLoans = await loans(
"all", // or "history" or "ongoing"
new PublicKey("<Borrower>") // optional
);
console.log({allLoans});
buy()
buy()
function generates
transaction instructions allowing you to buy an NFT from a marketplace
with partial Rain.fi funding.
Currently supported marketplaces are Solanart, Hadeswap and Metaplex Auction House.
To start using the function, let's go through all of the parameters. Function accepts 8 of them:
nftMint
- mint address of the NFT you want to buy.marketplace
- marketplace, where ^ above NFT is listed. You can find currently supported marketplaces above.price
- listing price of the ^ above NFT.seller
- address of the seller.interest
- fees paid by buyer.slippage
- price slippage. 500 = 5%amount
- amount (in SOL) that will be paid by the signer. Currently Rain only supports 50/50 payments, so make sure to set it toprice / 2
. (In coming weeks, Rain will also start supporting other payment ratio.)duration
- mortgage duration in days. If mortgage won't be sold/repaid on time, it will be liquidated.
Usage:
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
const rain = new Rain(
connection,
publicKey,
);
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
const {
buy,
fetch
} = pool;
const poolData = await fetch();
// Example listing object. Can be fetched from on-chain or 3rd party APIs, like Helius, Hyperspace, etc...
const listing = {
seller: new PublicKey("<Seller>"), // Seller who listed the NFT
price: 200 * LAMPORTS_PER_SOL, // 200 SOL
}
const duration = 7; // Days
const { feesInSol } = getMortgagesFeesDetailed(
poolData,
listing.price / 2,
duration
)
const {instruction, signers} = await buy({
nftMint: new PublicKey("<NFT Mint Address>"),
interest: feesInSol,
slippage: 20,
marketplace: "hadeswap", // Marketplace where ^ above NFT is listed.
amount: listing.price / 2, // Amount that you want to pay, the rest will be paid by Rain.
price: listing.price, // Price of that particular listing, don't confuse it with floor price!
seller: listing.seller, // Address of the seller who listed above ^ NFT.
duration, // Mortgage duration in days. If not repaid until that time, mortgage will be liquidated.
});
const tx = new Transaction();
tx.add(...instruction);
tx.partialSign(...signers);
// Transaction is ready to be signed & sent to the Solana network.
// Visit https://solanacookbook.com/ to see how to sign & send transactions.
repayMortgage()
As the name suggests, this function allows you to generate transaction instructions needed to repay a mortgage. To repay it, you have to pass two parameters:
mortgageAccount
- on-chain account address of the mortgage.amount
- mortgage size in lamports.
To get mortgageAccount
, use getMortgageFromAddress()
utility function, as in the snippet below:
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
const rain = new Rain(
connection,
publicKey,
);
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
const {
repayMortgage
} = pool;
const {
getMortgageFromAddress
} = rain.utils;
const mortages = await getMortgageFromAddress(
connection,
new PublicKey("<Borrower Address>"),
"borrower",
true
);
const mortgageToBeRepaid = mortages[0];
const { accountAddress, amount } = mortgageToBeRepaid;
const ix = await repayMortgage(
new PublicKey(accountAddress),
amount
);
const tx = new Transaction();
tx.add(...ix);
loanRequests()
loanRequests()
function allows you to fetch loan requests sent by borrowers to a particular pool.
To start using it, you first have to
specify status of loan requests you want to fetch. To fetch all, use "all"
. To only get accepted requests, use "accepted"
. To
only fetch loans that are awaiting acceptance, use "pending"
.
As the second parameter, you can (don't have to) specify particular borrower. If specified, only loan requests sent by the given address will be returned.
Usage:
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
const rain = new Rain(
connection,
publicKey,
);
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
const {loanRequests} = pool;
const allLoanRequests = await loanRequests(
"all", // or "history" or "ongoing"
new PublicKey("<Borrower>") // optional
);
console.log({allLoanRequests});
makeLoanRequest()
After you initialize new Pool
instance, you can send loan requests to that pool. To do it, use makeLoanRequest()
function.
Function accepts three parameters:
nftMint
- mint address of the NFT you want to borrow Solana against.duration
- requested loan duration (in days).amount
- requested loan amount. In the snippet below, we're using 125% of the maximum amount accepted by the pool.
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
const rain = new Rain(
connection,
publicKey,
);
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
const {
utils,
} = rain;
const {
getCollection
} = utils;
const {
makeLoanRequest
} = pool;
const { loanToValue, maxDuration, collections } = await pool.fetch();
const maxDurationInDays = maxDuration / 60 / 60 / 24;
const duration = maxDurationInDays * 2; // We're requesting loan duration which is 2x longer than maximum accepted by the pool.
const { collection } = collections[0];
const { floorPrice } = await getCollection(
connection,
collection // First collection supported by the above pool.
);
const ix = await makeLoanRequest(
new PublicKey("<Mint Address Of The Collateralized NFT>"),
duration,
floorPrice * loanToValue * 1.25, // We're requesting loan amount which is 25% bigger than maximum accepted by the pool.
);
const tx = new Transaction();
tx.add(ix);
// Transaction is ready to be signed & sent to the Solana network.
// Visit https://solanacookbook.com/ to see how to sign & send transactions.
acceptLoanRequest()
Pool
class not only allows you to interact with other pools, but also gives you ability to manage your own pool. You can, for example, accept or cancel loan requests sent by borrowers to your pool.
To accept loan request, use acceptLoanRequest()
function. Function accepts one parameter - publicKey
of the loan request you want to accept. To get a particular loan request, use
getFiltersLoanRequest()
utility function, followed by Solana SDK-native getProgramAccounts()
function. This set of functions
allow you to filter all loan requests to get one that you are interested in, and get its publicKey
.
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
const rain = new Rain(
connection,
publicKey,
);
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
const {
utils
} = rain;
const {
getFiltersLoanRequest
} = utils;
const {
makeLoanRequest
} = pool;
const poolData = await pool.fetch();
const allLoanRequests = await getFiltersLoanRequest([{
key: "pool",
value: poolData.poolAddress
}]);
const loanRequestProgramAccounts = await connection.getProgramAccounts(
new PublicKey("RainEraPU5yDoJmTrHdYynK9739GkEfDsE4ffqce2BR"),
// ^ Address of official Rain.fi program deployment.
{filters: allLoanRequests}
);
const loanRequestToAccept = loanRequestProgramAccounts[0].pubkey;
const ix = await acceptLoanRequest(loanRequestToAccept);
const tx = new Transaction();
tx.add(ix);
// Transaction is ready to be signed & sent to the Solana network.
// Visit https://solanacookbook.com/ to see how to sign & send transactions.
poolCancelLoanProposal()
We've just discussed function used to accept by borrowers to your pool. poolCancelLoanProposal()
does the opposite - it gives you (pool owner) ability
to reject loan proposal.
Usage:
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
const rain = new Rain(
connection,
publicKey,
);
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
const {
poolCancelLoanProposal
} = pool;
const {
getFiltersLoanRequest
} = rain.utils;
// Fetch pool data.
const poolData = await pool.fetch()
// Fetch all pending loan requests from your pool.
const filters = await getFiltersLoanRequest([{
key: "pool",
value: poolData.poolAddress
}]);
// Fetch loan requests accounts & data.
const loanRequests = await connection.getProgramAccounts(
new PublicKey("RainEraPU5yDoJmTrHdYynK9739GkEfDsE4ffqce2BR"),
// ^ Address of official Rain.fi program deployment.
{filters}
);
// Select the first request to cancellation.
const loanRequestToCancel = loanRequests[0];
// Generate instructions.
const ix = await poolCancelLoanProposal(loanRequestToCancel.pubkey);
const tx = new Transaction();
tx.add(ix);
// Transaction is ready to be signed & sent to the Solana network.
// Visit https://solanacookbook.com/ to see how to sign & send transactions.
mortgages()
Similar to loans and loan requests, Pool
class gives you ability
to fetch all mortgages taken from particular pool.
To use mortgages()
function, you first have to
specify status of mortgages you want to fetch. To fetch all, use "all"
. To only get ended (repaid/liquidated) mortgages, use "history"
. To
only fetch ongoing mortgages, use "ongoing"
. You can also fetch mortgages listed for sale - use "for_sale"
.
As the second parameter, you can (don't have to) specify particular borrower. If specified, only mortgages taken by this given address will be returned.
Usage:
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
const rain = new Rain(
connection,
publicKey,
);
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
const { mortgages } = pool;
const allMortgages = await mortgages(
"all",
new PublicKey("<Borrower Address>")
);
console.log({allMortgages});
cancelLoanRequest()
Use this function to cancel pending loan request (sent by you as a borrower). cancelLoanRequest()
is useful especially in case
of major collection's floor price changes, that affets profitability of the loan.
Usage:
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
const rain = new Rain(
connection,
publicKey,
);
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
const { cancelLoanRequest } = pool;
const {
getFiltersLoanRequest
} = rain.utils;
const filters = getFiltersLoanRequest([{
key: "borrower",
value: "<Your Address Here>"
}]);
const loanRequests = await connection.getProgramAccounts(
new PublicKey("RainEraPU5yDoJmTrHdYynK9739GkEfDsE4ffqce2BR"),
// ^ Address of official Rain.fi program deployment.
{filters}
);
const loanToCancel = loanRequests[0];
const ix = await cancelLoanRequest(
loanToCancel.pubkey
);
const tx = new Transaction();
tx.add(ix);
// Transaction is ready to be signed & sent to the Solana network.
// Visit https://solanacookbook.com/ to see how to sign & send transactions.
executeLoanRequest()
Each loan request has three steps. First, request is sent by the borrower to the pool. Then, pool can reject or accept the request. If it gets accepted, it can (but doesn't have to) be executed.
Addidional execution step is added, because between request being sent and request being accepted, market conditions could change. Floor prices fluctuate all the time, so loan can get unprofitable in the meantime.
So, after loan request is accepted by the pool, you can use executeLoanRequest()
function to
execute it (as a borrower).
Usage:
const connection = new Connection(clusterApiUrl("mainnet-beta"));
const owner = new PublicKey("<Pool Owner's Address>");
const publicKey = Keypair.generate().publicKey; // Placeholder. On production, use real address.
const rain = new Rain(
connection,
publicKey,
);
// Pool - lets you interact with a single pool.
const pool = new Pool(
connection,
owner,
publicKey
);
const { executeLoanRequest } = pool;
const {
getFeesDetailed
} = rain.utils;
// Get pool data.
const poolData = await pool.fetch();
const { loans } = await pool.loanRequests(
"accepted", // Only accepted loan requests can be executed.
new PublicKey("<Borrower Address>")
);
// Select first loan request.
const loanRequestToExecute = loans[0];
const { amount, duration } = loanRequestToExecute;
// Calculate fees.
const { feesInSol } = getFeesDetailed(
poolData,
amount,
duration,
);
// Get instructions for loan request execution.
const { instruction, signers } = await executeLoanRequest({
loanRequestAddress: new PublicKey(loanRequestToExecute.accountAddress),
amount,
duration,
slippage: 50,
interest: feesInSol
});
const tx = new Transaction();
tx.add(...instruction);
tx.partialSign(signers);
// Transaction is ready to be signed & sent to the Solana network.
// Visit https://solanacookbook.com/ to see how to sign & send transactions.