@unloc-xyz/unloc-sdk
Version:
952 lines • 39 kB
JavaScript
import * as anchor from '@project-serum/anchor';
import { bool, publicKey, struct, u32, u64, u8 } from '@project-serum/borsh';
import { NATIVE_MINT, Token, TOKEN_PROGRAM_ID } from '@solana/spl-token';
import { IDL as idl } from '../types/unloc_loan';
import { NFT_LOAN_PID, TOKEN_META_PID, UNLOC_MINT } from '../global-config';
import { Keypair, PublicKey, SystemProgram } from '@solana/web3.js';
import { bs58 } from '@project-serum/anchor/dist/cjs/utils/bytes';
import { Edition, MasterEdition, Metadata, MetadataData, MetadataKey } from '@metaplex-foundation/mpl-token-metadata';
import axios from 'axios';
import { VOTING_ITEM_TAG, VOTING_TAG } from './../voting';
import { defaults, discriminatorLen, pda, SOLANA_CONNECTION } from './../utils';
export let program = null;
export let programProvider = null;
export let programId = null;
const GLOBAL_STATE_TAG = Buffer.from('GLOBAL_STATE_SEED');
const OFFER_TAG = Buffer.from('OFFER_SEED');
const SUB_OFFER_TAG = Buffer.from('SUB_OFFER_SEED');
const NFT_VAULT_TAG = Buffer.from('NFT_VAULT_SEED');
const OFFER_VAULT_TAG = Buffer.from('OFFER_VAULT_SEED');
const TREASURY_VAULT_TAG = Buffer.from('TREASURY_VAULT_SEED');
const REWARD_VAULT_TAG = Buffer.from('REWARD_VAULT_SEED');
const USER_REWARD_TAG = Buffer.from('LENDER_REWARD_SEED');
const WSOL_MINT = new anchor.web3.PublicKey('So11111111111111111111111111111111111111112');
// This command makes an Lottery
export const initLoanProgram = (wallet, connection = SOLANA_CONNECTION, pid = NFT_LOAN_PID) => {
if (program != null) {
return;
}
programId = pid;
const provider = new anchor.AnchorProvider(connection, wallet, { skipPreflight: true });
programProvider = provider;
// Generate the program client from IDL.
program = new anchor.Program(idl, programId, provider);
};
// initLoanProgram(SOLANA_CONNECTION, Keypair.generate())
export const getLoanGlobalState = async () => {
const globalState = await pda([GLOBAL_STATE_TAG], programId);
return await program.account.globalState.fetchNullable(globalState);
};
export const getCurrentVotingKey = async () => {
const globalState = await pda([GLOBAL_STATE_TAG], programId);
const globalStateData = await program.account.globalState.fetch(globalState);
const currentVotingNum = globalStateData?.currentVotingNum;
const votingPid = globalStateData?.votingPid;
const voting = await pda([VOTING_TAG, currentVotingNum.toArrayLike(Buffer, "be", 8)], votingPid);
return voting;
};
export const getLoanOfferList = async () => {
return await program.account.offer.all();
};
export const getLoanOfferMultiple = async (keys) => {
return await program.account.offer.fetchMultiple(keys);
};
export const getLoanOffer = async (key) => {
return await program.account.offer.fetchNullable(key);
};
export const getLoanOffersBy = async (owner, nftMint, state) => {
const filters = [];
if (owner) {
const filter = {
memcmp: {
offset: discriminatorLen,
bytes: owner.toBase58()
}
};
filters.push(filter);
}
if (nftMint) {
const filter = {
memcmp: {
offset: discriminatorLen + 32,
bytes: nftMint.toBase58()
}
};
filters.push(filter);
}
if (state) {
const filter = {
memcmp: {
offset: discriminatorLen + 96,
bytes: bs58.encode([state])
}
};
filters.push(filter);
}
return await program.account.offer.all(filters);
};
export const getLoanSubOffer = async (key) => {
const subOffer = await program.account.subOffer.fetchNullable(key);
if (subOffer) {
const offerKey = subOffer.offer;
const offerData = await program.account.offer.fetch(offerKey);
if (offerData.startSubOfferNum.toNumber() > subOffer.subOfferNumber.toNumber()) {
return null;
}
}
return subOffer;
};
export const getLoanSubOfferList = async (offer, nftMint, state) => {
const accountName = 'subOffer';
const filters = [];
if (offer) {
const filter = {
memcmp: {
offset: discriminatorLen + 97,
bytes: offer.toBase58()
}
};
filters.push(filter);
}
if (nftMint) {
const filter = {
memcmp: {
offset: discriminatorLen + 32,
bytes: nftMint.toBase58()
}
};
filters.push(filter);
}
if (state) {
const filter = {
memcmp: {
offset: discriminatorLen + 96,
bytes: bs58.encode([state])
}
};
filters.push(filter);
}
const subOffers = await program.account.subOffer.all(filters);
const result = [];
for (let i = 0; i < subOffers.length; i++) {
const offerKey = subOffers[i].account.offer;
const offerData = await program.account.offer.fetch(offerKey);
if (offerData.startSubOfferNum.toNumber() <= subOffers[i].account.subOfferNumber.toNumber()) {
result.push(subOffers[i]);
}
}
return result;
};
export const getAllLoanSubOffers = async () => {
const subOffers = await program.account.subOffer.all();
const result = [];
for (let i = 0; i < subOffers.length; i++) {
const offerKey = subOffers[i].account.offer;
const offerData = await program.account.offer.fetch(offerKey);
if (offerData.startSubOfferNum.toNumber() <= subOffers[i].account.subOfferNumber.toNumber()) {
result.push(subOffers[i]);
}
}
return result;
};
export const getLoanSubOfferMultiple = async (keys, offerState) => {
const subOffers = await program.account.subOffer.fetchMultiple(keys);
// Filter for nulls and get the offer address
const offerAddresses = subOffers.filter((subOffer) => subOffer).map((subOffer) => subOffer.offer);
// Get all offer data in a single call instead of a loop, filter for nulls
const offers = (await program.account.offer.fetchMultiple(offerAddresses)).filter((data) => data);
const result = [];
for (let i = 0; i < subOffers.length; i++) {
if (subOffers[i]) {
const offerKey = subOffers[i].offer;
const offerKeyIndex = offerAddresses.findIndex((address) => address.equals(offerKey));
const offerData = offers[offerKeyIndex];
// If we want to, check the offer state too.
// Suboffer can be proposed when the offer isn't, so that's why we check the state on the offer, not just the suboffer.
// Be careful, offerState = 0 can coerce to false.
if (typeof offerState === 'number') {
if (offerData.startSubOfferNum.toNumber() <= subOffers[i].subOfferNumber.toNumber() && offerData.state === offerState) {
result.push({ ...subOffers[i], subOfferKey: keys[i] });
}
}
else {
if (offerData.startSubOfferNum.toNumber() <= subOffers[i].subOfferNumber.toNumber()) {
result.push({ ...subOffers[i], subOfferKey: keys[i] });
}
}
}
}
return result;
};
export const setLoanGlobalState = async (accruedInterestNumerator, denominator, aprNumerator, expireLoanDuration, rewardPerSol, rewardPerUsdc, lenderRewardsPercentage, rewardMint, treasury, signer = programProvider.wallet.publicKey, signers = []) => {
const globalState = await pda([GLOBAL_STATE_TAG], programId);
const rewardVault = await pda([REWARD_VAULT_TAG], programId);
const superOwner = signer;
try {
const tx = await program.rpc.setGlobalState(accruedInterestNumerator, denominator, aprNumerator, expireLoanDuration, rewardPerSol, rewardPerUsdc, lenderRewardsPercentage, {
accounts: {
superOwner,
globalState,
rewardMint,
rewardVault,
newSuperOwner: superOwner,
treasuryWallet: treasury,
...defaults
},
signers
});
// eslint-disable-next-line no-console
console.log('setGlobalState tx = ', tx);
}
catch (e) {
console.log(e);
}
};
export const setLoanInternalInfo = async (unlocStakingPid, unlocStakingPoolId, votingPid, tokenMetadataPid, currentVotingNum, signer = programProvider.wallet.publicKey, signers = []) => {
const globalState = await pda([GLOBAL_STATE_TAG], programId);
const superOwner = signer;
try {
const tx = await program.rpc.setInternalInfo(unlocStakingPid, unlocStakingPoolId, votingPid, tokenMetadataPid, currentVotingNum, {
accounts: {
superOwner,
globalState,
},
signers
});
// eslint-disable-next-line no-console
console.log('setInternalInfo tx = ', tx);
}
catch (e) {
console.log(e);
}
};
export const depositRewards = async (amount, signer = programProvider.wallet.publicKey, signers = []) => {
const rewardMint = UNLOC_MINT;
const rewardVault = await pda([REWARD_VAULT_TAG], programId);
const authority = signer;
const userRewardVault = await checkWalletATA(rewardMint.toBase58(), programProvider.connection, authority);
const tx = await program.rpc.depositRewards(amount, {
accounts: {
authority,
rewardVault,
userRewardVault,
...defaults
},
signers
});
// eslint-disable-next-line no-console
console.log('deposit rewards tx = ', tx);
};
export const claimRewards = async (userReward, signer = programProvider.wallet.publicKey, signers = []) => {
const globalState = await pda([GLOBAL_STATE_TAG], programId);
const rewardMint = UNLOC_MINT;
const rewardVault = await pda([REWARD_VAULT_TAG], programId);
const authority = signer;
let userRewardVault = await checkWalletATA(rewardMint.toBase58(), programProvider.connection, authority);
const preInstructions = [];
if (!userRewardVault) {
userRewardVault = await addTokenAccountInstruction(rewardMint, authority, preInstructions, signer, signers);
}
const tx = await program.rpc.claimRewards({
accounts: {
authority,
globalState,
rewardVault,
userReward,
userRewardVault,
...defaults
},
preInstructions,
signers
});
// eslint-disable-next-line no-console
console.log('claim rewards tx = ', tx);
};
export const setLoanOffer = async (nftMint, signer = programProvider.wallet.publicKey, signers = []) => {
const globalState = await pda([GLOBAL_STATE_TAG], programId);
const borrowerNftVault = await checkWalletATA(nftMint.toBase58(), programProvider.connection, signer);
const borrower = signer;
const offer = await pda([OFFER_TAG, borrower.toBuffer(), nftMint.toBuffer()], programId);
const nftMetadata = await Metadata.getPDA(nftMint);
const edition = await Edition.getPDA(nftMint);
try {
const tx = await program.rpc.setOffer({
accounts: {
borrower,
globalState,
offer,
nftMint,
nftMetadata,
edition,
userVault: borrowerNftVault,
metadataProgram: TOKEN_META_PID,
...defaults
},
signers
});
// eslint-disable-next-line no-console
console.log('setOffer tx = ', tx);
}
catch (e) {
console.log(e);
}
};
export const cancelLoanOffer = async (nftMint, signer = programProvider.wallet.publicKey, signers = []) => {
const borrower = signer;
const offer = await pda([OFFER_TAG, borrower.toBuffer(), nftMint.toBuffer()], programId);
let borrowerNftVault = await checkWalletATA(nftMint.toBase58(), programProvider.connection, borrower);
const preInstructions = [];
if (!borrowerNftVault) {
borrowerNftVault = await addTokenAccountInstruction(nftMint, borrower, preInstructions, signer, signers);
}
const edition = await Edition.getPDA(nftMint);
try {
const tx = await program.rpc.cancelOffer({
accounts: {
borrower,
offer,
nftMint,
edition,
userVault: borrowerNftVault,
metadataProgram: TOKEN_META_PID,
...defaults
},
preInstructions,
signers
});
// eslint-disable-next-line no-console
console.log('cancelOffer tx = ', tx);
}
catch (e) {
console.log(e);
}
};
export const createLoanSubOffer = async (offerAmount, loanDuration, minRepaidNumerator, aprNumerator, nftMint, offerMint, signer = programProvider.wallet.publicKey, signers = []) => {
if (offerMint.equals(NATIVE_MINT)) {
offerMint = WSOL_MINT;
}
const globalState = await pda([GLOBAL_STATE_TAG], programId);
const borrower = signer;
const offer = await pda([OFFER_TAG, borrower.toBuffer(), nftMint.toBuffer()], programId);
const offerData = await program.account.offer.fetch(offer);
const subOfferNumer = offerData.subOfferCount;
const subOffer = await pda([SUB_OFFER_TAG, offer.toBuffer(), subOfferNumer.toArrayLike(Buffer, 'be', 8)], programId);
const treasuryVault = await pda([TREASURY_VAULT_TAG, offerMint.toBuffer()], programId);
const globalStateData = await program.account.globalState.fetch(globalState);
const treasuryWallet = globalStateData.treasuryWallet;
try {
const tx = await program.rpc.setSubOffer(offerAmount, subOfferNumer, loanDuration, minRepaidNumerator, aprNumerator, {
accounts: {
borrower,
globalState,
offer,
subOffer,
offerMint,
treasuryWallet,
treasuryVault,
...defaults
},
signers
});
// eslint-disable-next-line no-console
console.log('createSubOffer tx = ', tx);
}
catch (e) {
console.log(e);
}
};
export const createLoanSubOfferByStaking = async (offerAmount, loanDuration, minRepaidNumerator, aprNumerator, nftMint, offerMint, signer = programProvider.wallet.publicKey, signers = []) => {
if (offerMint.equals(NATIVE_MINT)) {
offerMint = WSOL_MINT;
}
const globalState = await pda([GLOBAL_STATE_TAG], programId);
const borrower = signer;
const offer = await pda([OFFER_TAG, borrower.toBuffer(), nftMint.toBuffer()], programId);
const offerData = await program.account.offer.fetch(offer);
const subOfferNumer = offerData.subOfferCount;
const subOffer = await pda([SUB_OFFER_TAG, offer.toBuffer(), subOfferNumer.toArrayLike(Buffer, 'be', 8)], programId);
const treasuryVault = await pda([TREASURY_VAULT_TAG, offerMint.toBuffer()], programId);
const globalStateData = await program.account.globalState.fetch(globalState);
const stakingUser = await pda([globalStateData.unlocStakingPoolId.toBuffer(), borrower.toBuffer()], globalStateData.unlocStakingPid);
const treasuryWallet = globalStateData.treasuryWallet;
try {
const tx = await program.rpc.setSubOfferByStaking(offerAmount, subOfferNumer, loanDuration, minRepaidNumerator, aprNumerator, {
accounts: {
borrower,
globalState,
offer,
subOffer,
stakingUser,
offerMint,
treasuryWallet,
treasuryVault,
...defaults
},
signers
});
// eslint-disable-next-line no-console
console.log('createSubOffer tx = ', tx);
}
catch (e) {
console.log(e);
}
};
export const updateLoanSubOffer = async (offerAmount, loanDuration, minRepaidNumerator, aprNumerator, subOffer, signer = programProvider.wallet.publicKey, signers = []) => {
const globalState = await pda([GLOBAL_STATE_TAG], programId);
const borrower = signer;
const subOfferData = await program.account.subOffer.fetch(subOffer);
const offer = await pda([OFFER_TAG, borrower.toBuffer(), subOfferData.nftMint.toBuffer()], programId);
const subOfferNumer = subOfferData.subOfferNumber;
const offerMint = subOfferData.offerMint;
const treasuryVault = await pda([TREASURY_VAULT_TAG, subOfferData.offerMint.toBuffer()], programId);
const globalStateData = await program.account.globalState.fetch(globalState);
const treasuryWallet = globalStateData.treasuryWallet;
try {
const tx = await program.rpc.setSubOffer(offerAmount, subOfferNumer, loanDuration, minRepaidNumerator, aprNumerator, {
accounts: {
borrower,
globalState,
offer,
subOffer,
offerMint,
treasuryWallet,
treasuryVault,
...defaults
},
signers
});
// eslint-disable-next-line no-console
console.log('updateSubOffer tx = ', tx);
}
catch (e) {
console.log(e);
}
};
export const updateLoanSubOfferByStaking = async (offerAmount, loanDuration, minRepaidNumerator, aprNumerator, subOffer, signer = programProvider.wallet.publicKey, signers = []) => {
const globalState = await pda([GLOBAL_STATE_TAG], programId);
const borrower = signer;
const subOfferData = await program.account.subOffer.fetch(subOffer);
const offer = await pda([OFFER_TAG, borrower.toBuffer(), subOfferData.nftMint.toBuffer()], programId);
const subOfferNumer = subOfferData.subOfferNumber;
const offerMint = subOfferData.offerMint;
const treasuryVault = await pda([TREASURY_VAULT_TAG, subOfferData.offerMint.toBuffer()], programId);
const globalStateData = await program.account.globalState.fetch(globalState);
const stakingUser = await pda([globalStateData.unlocStakingPoolId.toBuffer(), borrower.toBuffer()], globalStateData.unlocStakingPid);
const treasuryWallet = globalStateData.treasuryWallet;
try {
const tx = await program.rpc.setSubOfferByStaking(offerAmount, subOfferNumer, loanDuration, minRepaidNumerator, aprNumerator, {
accounts: {
borrower,
globalState,
offer,
subOffer,
stakingUser,
offerMint,
treasuryWallet,
treasuryVault,
...defaults
},
signers
});
// eslint-disable-next-line no-console
console.log('updateSubOffer tx = ', tx);
}
catch (e) {
console.log(e);
}
};
export const cancelLoanSubOffer = async (subOffer, signer = programProvider.wallet.publicKey, signers = []) => {
const borrower = signer;
const subOfferData = await program.account.subOffer.fetch(subOffer);
const offer = await pda([OFFER_TAG, borrower.toBuffer(), subOfferData.nftMint.toBuffer()], programId);
try {
const tx = await program.rpc.cancelSubOffer({
accounts: {
borrower,
offer,
subOffer
},
signers
});
// eslint-disable-next-line no-console
console.log('cancelSubOffer tx = ', tx);
}
catch (e) {
console.log(e);
}
};
export const acceptLoanOfferByVoting = async (subOffer, signer = programProvider.wallet.publicKey, signers = []) => {
const lender = signer;
const subOfferData = await program.account.subOffer.fetch(subOffer);
const offer = subOfferData.offer;
const offerMint = subOfferData.offerMint;
const offerData = await program.account.offer.fetch(subOfferData.offer);
const borrower = offerData.borrower;
let borrowerOfferVault = await checkWalletATA(offerMint.toBase58(), programProvider.connection, borrower);
let lenderOfferVault = await checkWalletATA(offerMint.toBase58(), programProvider.connection, lender);
const preInstructions = [];
const postInstructions = [];
if (offerMint.equals(WSOL_MINT)) {
const treasuryVault = await pda([TREASURY_VAULT_TAG, offerMint.toBuffer()], programId);
lenderOfferVault = treasuryVault;
borrowerOfferVault = treasuryVault;
}
else {
if (!borrowerOfferVault) {
console.log("borrower doesn't have offer token account!");
borrowerOfferVault = await addTokenAccountInstruction(offerMint, borrower, preInstructions, signer, signers);
}
if (!lenderOfferVault) {
console.log("lender doesn't have offer token!");
return;
}
}
const globalState = await pda([GLOBAL_STATE_TAG], programId);
const globalStateData = await program.account.globalState.fetch(globalState);
const userReward = await pda([USER_REWARD_TAG, lender.toBuffer(), borrower.toBuffer(), subOffer.toBuffer()], programId);
const votingPid = globalStateData.votingPid;
const currentVotingKey = await getCurrentVotingKey();
const votingItemKey = await pda([VOTING_ITEM_TAG, currentVotingKey.toBuffer(), offerData.collection.toBuffer()], votingPid);
try {
const tx = await program.rpc.acceptOfferByVoting({
accounts: {
lender,
borrower,
globalState,
offer,
subOffer,
offerMint,
userReward,
voting: currentVotingKey,
votingItem: votingItemKey,
borrowerOfferVault,
lenderOfferVault,
...defaults
},
preInstructions,
postInstructions,
signers
});
// eslint-disable-next-line no-console
console.log('acceptOffer tx = ', tx);
}
catch (e) {
console.log(e);
}
};
export const acceptLoanOffer = async (subOffer, signer = programProvider.wallet.publicKey, signers = []) => {
const lender = signer;
const subOfferData = await program.account.subOffer.fetch(subOffer);
const offer = subOfferData.offer;
const offerMint = subOfferData.offerMint;
const offerData = await program.account.offer.fetch(subOfferData.offer);
const borrower = offerData.borrower;
let borrowerOfferVault = await checkWalletATA(offerMint.toBase58(), programProvider.connection, borrower);
let lenderOfferVault = await checkWalletATA(offerMint.toBase58(), programProvider.connection, lender);
const preInstructions = [];
const postInstructions = [];
if (offerMint.equals(WSOL_MINT)) {
const treasuryVault = await pda([TREASURY_VAULT_TAG, offerMint.toBuffer()], programId);
lenderOfferVault = treasuryVault;
borrowerOfferVault = treasuryVault;
}
else {
if (!borrowerOfferVault) {
console.log("borrower doesn't have offer token account!");
borrowerOfferVault = await addTokenAccountInstruction(offerMint, borrower, preInstructions, signer, signers);
}
if (!lenderOfferVault) {
console.log("lender doesn't have offer token!");
return;
}
}
const globalState = await pda([GLOBAL_STATE_TAG], programId);
const userReward = await pda([USER_REWARD_TAG, lender.toBuffer(), borrower.toBuffer(), subOffer.toBuffer()], programId);
try {
const tx = await program.rpc.acceptOffer({
accounts: {
lender,
borrower,
globalState,
offer,
subOffer,
offerMint,
userReward,
borrowerOfferVault,
lenderOfferVault,
...defaults
},
preInstructions,
postInstructions,
signers
});
// eslint-disable-next-line no-console
console.log('acceptOffer tx = ', tx);
}
catch (e) {
console.log(e);
}
};
export const repayLoan = async (subOffer, signer = programProvider.wallet.publicKey, signers = []) => {
const globalState = await pda([GLOBAL_STATE_TAG], programId);
const borrower = signer;
const globalStateData = await program.account.globalState.fetch(globalState);
const treasuryWallet = globalStateData.treasuryWallet;
const subOfferData = await program.account.subOffer.fetch(subOffer);
const offer = subOfferData.offer;
const lender = subOfferData.lender;
const offerData = await program.account.offer.fetch(offer);
const offerMint = subOfferData.offerMint;
const nftMint = offerData.nftMint;
const treasuryVault = await pda([TREASURY_VAULT_TAG, offerMint.toBuffer()], programId);
let borrowerOfferVault = await checkWalletATA(offerMint.toBase58(), programProvider.connection, borrower);
let borrowerNftVault = await checkWalletATA(nftMint.toBase58(), programProvider.connection, borrower);
let lenderOfferVault = await checkWalletATA(offerMint.toBase58(), programProvider.connection, lender);
const preInstructions = [];
const postInstructions = [];
if (offerMint.equals(WSOL_MINT)) {
const treasuryVault = await pda([TREASURY_VAULT_TAG, offerMint.toBuffer()], programId);
lenderOfferVault = treasuryVault;
borrowerOfferVault = treasuryVault;
}
if (!borrowerOfferVault) {
console.log("borrower doesn't have offer token!");
return;
}
if (!borrowerNftVault) {
console.log("borrower doesn't have nft token token account!");
borrowerNftVault = await addTokenAccountInstruction(nftMint, borrower, preInstructions, signer, signers);
}
if (!lenderOfferVault) {
console.log("lender doesn't have offer token account!", lender.toBase58());
lenderOfferVault = await addTokenAccountInstruction(offerMint, lender, preInstructions, signer, signers);
}
const userReward = await pda([USER_REWARD_TAG, lender.toBuffer(), borrower.toBuffer(), subOffer.toBuffer()], programId);
const edition = await Edition.getPDA(nftMint);
try {
const tx = await program.rpc.repayLoan({
accounts: {
borrower,
lender,
globalState,
treasuryWallet,
offer,
subOffer,
nftMint,
edition,
userReward,
borrowerNftVault,
lenderOfferVault,
borrowerOfferVault,
treasuryVault,
metadataProgram: TOKEN_META_PID,
...defaults
},
preInstructions,
postInstructions,
signers
});
// eslint-disable-next-line no-console
console.log('repayLoan tx = ', tx);
}
catch (e) {
console.log(e);
}
};
export const claimLoanCollateral = async (subOffer, signer = programProvider.wallet.publicKey, signers = []) => {
const globalState = await pda([GLOBAL_STATE_TAG], programId);
const lender = signer;
const globalStateData = await program.account.globalState.fetch(globalState);
const treasuryWallet = globalStateData.treasuryWallet;
const subOfferData = await program.account.subOffer.fetch(subOffer);
const offer = subOfferData.offer;
const offerData = await program.account.offer.fetch(offer);
const offerMint = subOfferData.offerMint;
const nftMint = offerData.nftMint;
const treasuryVault = await pda([TREASURY_VAULT_TAG, offerMint.toBuffer()], programId);
let borrowerNftVault = await checkWalletATA(nftMint.toBase58(), programProvider.connection, offerData.borrower);
let lenderNftVault = await checkWalletATA(nftMint.toBase58(), programProvider.connection, lender);
let lenderOfferVault = await checkWalletATA(offerMint.toBase58(), programProvider.connection, lender);
const preInstructions = [];
const postInstructions = [];
if (offerMint.equals(WSOL_MINT)) {
const treasuryVault = await pda([TREASURY_VAULT_TAG, offerMint.toBuffer()], programId);
lenderOfferVault = treasuryVault;
}
if (!lenderNftVault) {
console.log("lender doesn't have nft token token account!");
lenderNftVault = await addTokenAccountInstruction(nftMint, lender, preInstructions, signer, signers);
}
if (!lenderOfferVault) {
console.log("lender doesn't have offer token account!", lender.toBase58());
lenderOfferVault = await addTokenAccountInstruction(offerMint, lender, preInstructions, signer, signers);
}
const edition = await Edition.getPDA(nftMint);
try {
const tx = await program.rpc.claimCollateral({
accounts: {
lender,
globalState,
treasuryWallet,
offer,
subOffer,
nftMint,
edition,
lenderNftVault,
borrowerNftVault,
lenderOfferVault,
treasuryVault,
metadataProgram: TOKEN_META_PID,
...defaults
},
preInstructions,
postInstructions,
signers
});
// eslint-disable-next-line no-console
console.log('claimCollateral tx = ', tx);
}
catch (e) {
console.log(e);
}
};
// super owner can call this function
export const claimExpiredCollateral = async (subOffer, signer = programProvider.wallet.publicKey, signers = []) => {
const globalState = await pda([GLOBAL_STATE_TAG], programId);
const globalStateData = await program.account.globalState.fetch(globalState);
const treasuryWallet = globalStateData.treasuryWallet;
const subOfferData = await program.account.subOffer.fetch(subOffer);
const offer = subOfferData.offer;
const offerData = await program.account.offer.fetch(offer);
const nftMint = offerData.nftMint;
let userNftVault = await checkWalletATA(nftMint.toBase58(), programProvider.connection, signer);
const preInstructions = [];
if (!userNftVault) {
userNftVault = await addTokenAccountInstruction(nftMint, signer, preInstructions, signer, signers);
}
let borrowerNftVault = await checkWalletATA(nftMint.toBase58(), programProvider.connection, offerData.borrower);
const edition = await Edition.getPDA(nftMint);
try {
const tx = await program.rpc.claimExpiredCollateral({
accounts: {
superOwner: signer,
globalState,
treasuryWallet,
offer,
subOffer,
nftMint,
edition,
userNftVault,
borrowerNftVault,
metadataProgram: TOKEN_META_PID,
...defaults
},
preInstructions,
signers
});
// eslint-disable-next-line no-console
console.log('claimExpiredCollateral tx = ', tx);
}
catch (e) {
console.log(e);
}
};
export var OfferState;
(function (OfferState) {
OfferState[OfferState["Proposed"] = 0] = "Proposed";
OfferState[OfferState["Accepted"] = 1] = "Accepted";
OfferState[OfferState["Expired"] = 2] = "Expired";
OfferState[OfferState["Fulfilled"] = 3] = "Fulfilled";
OfferState[OfferState["NFTClaimed"] = 4] = "NFTClaimed";
OfferState[OfferState["Canceled"] = 5] = "Canceled";
})(OfferState || (OfferState = {}));
export var SubOfferState;
(function (SubOfferState) {
SubOfferState[SubOfferState["Proposed"] = 0] = "Proposed";
SubOfferState[SubOfferState["Accepted"] = 1] = "Accepted";
SubOfferState[SubOfferState["Expired"] = 2] = "Expired";
SubOfferState[SubOfferState["Fulfilled"] = 3] = "Fulfilled";
SubOfferState[SubOfferState["LoanPaymentClaimed"] = 4] = "LoanPaymentClaimed";
SubOfferState[SubOfferState["Canceled"] = 5] = "Canceled";
})(SubOfferState || (SubOfferState = {}));
export const getLoanSubOffersKeysByState = async (state) => {
try {
const accountName = 'subOffer';
const data = await programProvider.connection.getProgramAccounts(programId, {
dataSlice: { offset: 0, length: 0 },
filters: [
{ memcmp: program.account.subOffer.coder.accounts.memcmp(accountName) },
{ memcmp: { offset: discriminatorLen + 96, bytes: bs58.encode(state) } } // add filters for nftMint, APR, Duration, Amount and this function will be glorious
]
});
return data.map((offer) => offer.pubkey);
}
catch (e) {
console.log(e);
}
};
export const addTokenAccountInstruction = async (mint, owner, instructions, signer, signers, rent = 0) => {
const newKeypair = Keypair.generate();
const rentForTokenAccount = await Token.getMinBalanceRentForExemptAccount(programProvider.connection);
instructions.push(SystemProgram.createAccount({
fromPubkey: signer,
newAccountPubkey: newKeypair.publicKey,
lamports: rent > 0 ? rent : rentForTokenAccount,
space: ACCOUNT_LAYOUT.span,
programId: TOKEN_PROGRAM_ID
}));
const instruction = Token.createInitAccountInstruction(TOKEN_PROGRAM_ID, mint, newKeypair.publicKey, owner);
instructions.push(instruction);
signers.push(newKeypair);
return newKeypair.publicKey;
};
export const checkWalletATA = async (mint, connection = programProvider.connection, walletPubkey = programProvider.wallet.publicKey) => {
const parsedTokenAccounts = await connection.getParsedTokenAccountsByOwner(walletPubkey, {
programId: TOKEN_PROGRAM_ID
}, 'confirmed');
let result = null;
let maxAmount = 0;
parsedTokenAccounts.value.forEach(async (tokenAccountInfo) => {
const tokenAccountPubkey = tokenAccountInfo.pubkey;
const parsedInfo = tokenAccountInfo.account.data.parsed.info;
const mintAddress = parsedInfo.mint;
const amount = parsedInfo.tokenAmount.uiAmount;
if (mintAddress === mint && amount >= maxAmount) {
result = tokenAccountPubkey;
maxAmount = amount;
}
});
return result;
};
export const logObject = (title, obj) => {
console.log(title, obj);
};
export const getOfferBalance = async (subOffer, connection = programProvider.connection, walletPubkey = programProvider.wallet.publicKey) => {
try {
const tokenAccount = await pda([OFFER_VAULT_TAG, subOffer.toBuffer()], programId);
const balance = (await connection.getTokenAccountBalance(tokenAccount)).value.uiAmount;
return balance;
}
catch (e) {
console.log(e);
}
return 0;
};
// --------------------utilities---------------------
export class MultipleNFT {
mints;
metadatas = [];
constructor(keys) {
this.mints = keys;
}
async initialize() {
if (this.metadatas.length == 0) {
const metadataPDAs = [];
for (let i = 0; i < this.mints.length; i++) {
const metadataPDA = await Metadata.getPDA(this.mints[i]);
metadataPDAs.push(metadataPDA);
}
let i = 0;
const metaInfos = await Metadata.getInfos(programProvider.connection, metadataPDAs);
metaInfos.forEach((value, key, map) => {
const metadata = MetadataData.deserialize(value.data);
const nftMetadata = new NFTMetadata(this.mints[i], metadataPDAs[i], metadata);
this.metadatas.push(nftMetadata);
i++;
});
}
}
getNftMeta(mint) {
for (let i = 0; i < this.metadatas.length; i++) {
if (this.metadatas[i].mint === mint.toBase58()) {
return this.metadatas[i];
}
}
return null;
}
async initArweavedata() {
for (let i = 0; i < this.metadatas.length; i++) {
await this.metadatas[i].getAreaveMetadata();
}
}
}
export class NFTMetadata {
mint = '';
metadataPDA = '';
onChainMetadata = null;
arweaveMetadata = null;
masterEdition = null;
constructor(mint, metadataPDA, onChainMetadata) {
this.mint = mint.toBase58();
this.metadataPDA = metadataPDA.toBase58();
this.onChainMetadata = onChainMetadata;
}
async getAreaveMetadata() {
if (this.arweaveMetadata === null) {
try {
const arweaveData = await axios.get(this.onChainMetadata.data.uri);
if (arweaveData) {
this.arweaveMetadata = arweaveData.data;
}
}
catch (e) {
// eslint-disable-next-line no-console
console.log(e);
}
}
return this.arweaveMetadata;
}
currentAreaveMetadata() {
return this.arweaveMetadata;
}
async getMasterEdition() {
if (this.arweaveMetadata === null) {
const masterEditionPDA = await Edition.getPDA(new PublicKey(this.mint));
const editionAccountInfo = await programProvider.connection.getAccountInfo(masterEditionPDA);
if (editionAccountInfo) {
const key = editionAccountInfo?.data[0];
let masterEditionData;
let data;
switch (key) {
case MetadataKey.MasterEditionV1:
case MetadataKey.MasterEditionV2:
;
({ data } = new MasterEdition(masterEditionPDA, editionAccountInfo));
masterEditionData = data;
break;
default:
masterEditionData = undefined;
break;
}
this.masterEdition = {
masterEditionPDA: masterEditionPDA.toString(),
masterEditionData
};
}
}
return this.masterEdition;
}
}
export const ACCOUNT_LAYOUT = struct([
publicKey('mint'),
publicKey('owner'),
u64('amount'),
u32('delegateOption'),
publicKey('delegate'),
u8('state'),
u32('isNativeOption'),
u64('isNative'),
u64('delegatedAmount'),
u32('closeAuthorityOption'),
publicKey('closeAuthority')
]);
export const MINT_LAYOUT = struct([
u32('mintAuthorityOption'),
publicKey('mintAuthority'),
u64('supply'),
u8('decimals'),
bool('initialized'),
u32('freezeAuthorityOption'),
publicKey('freezeAuthority')
]);
//# sourceMappingURL=index.js.map