mirror of https://github.com/certusone/oyster.git
actions
This commit is contained in:
parent
c4be0d78e5
commit
b9a71a423a
|
@ -1,293 +0,0 @@
|
||||||
import {
|
|
||||||
Account,
|
|
||||||
Connection,
|
|
||||||
PublicKey,
|
|
||||||
TransactionInstruction,
|
|
||||||
} from '@solana/web3.js';
|
|
||||||
import {
|
|
||||||
contexts,
|
|
||||||
utils,
|
|
||||||
actions,
|
|
||||||
models,
|
|
||||||
TokenAccount,
|
|
||||||
ParsedAccount,
|
|
||||||
} from '@oyster/common';
|
|
||||||
|
|
||||||
import {
|
|
||||||
accrueInterestInstruction,
|
|
||||||
LendingReserve,
|
|
||||||
} from './../models/lending/reserve';
|
|
||||||
import { AccountLayout, MintInfo, MintLayout } from '@solana/spl-token';
|
|
||||||
|
|
||||||
import { createUninitializedObligation } from './obligation';
|
|
||||||
|
|
||||||
import {
|
|
||||||
LendingObligationLayout,
|
|
||||||
borrowInstruction,
|
|
||||||
LendingMarket,
|
|
||||||
BorrowAmountType,
|
|
||||||
LendingObligation,
|
|
||||||
initObligationInstruction,
|
|
||||||
} from '../models';
|
|
||||||
|
|
||||||
const { approve } = models;
|
|
||||||
const { toLamports, LENDING_PROGRAM_ID, LEND_HOST_FEE_ADDRESS, notify } = utils;
|
|
||||||
const { cache, MintParser } = contexts.Accounts;
|
|
||||||
const { sendTransaction } = contexts.Connection;
|
|
||||||
const {
|
|
||||||
createTempMemoryAccount,
|
|
||||||
createUninitializedAccount,
|
|
||||||
createUninitializedMint,
|
|
||||||
ensureSplAccount,
|
|
||||||
findOrCreateAccountByMint,
|
|
||||||
} = actions;
|
|
||||||
|
|
||||||
export const borrow = async (
|
|
||||||
connection: Connection,
|
|
||||||
wallet: any,
|
|
||||||
|
|
||||||
from: TokenAccount,
|
|
||||||
amount: number,
|
|
||||||
amountType: BorrowAmountType,
|
|
||||||
|
|
||||||
borrowReserve: ParsedAccount<LendingReserve>,
|
|
||||||
|
|
||||||
depositReserve: ParsedAccount<LendingReserve>,
|
|
||||||
|
|
||||||
existingObligation?: ParsedAccount<LendingObligation>,
|
|
||||||
|
|
||||||
obligationAccount?: PublicKey,
|
|
||||||
) => {
|
|
||||||
notify({
|
|
||||||
message: 'Borrowing funds...',
|
|
||||||
description: 'Please review transactions to approve.',
|
|
||||||
type: 'warn',
|
|
||||||
});
|
|
||||||
|
|
||||||
let signers: Account[] = [];
|
|
||||||
let instructions: TransactionInstruction[] = [];
|
|
||||||
let cleanupInstructions: TransactionInstruction[] = [];
|
|
||||||
let finalCleanupInstructions: TransactionInstruction[] = [];
|
|
||||||
|
|
||||||
const [authority] = await PublicKey.findProgramAddress(
|
|
||||||
[depositReserve.info.lendingMarket.toBuffer()],
|
|
||||||
LENDING_PROGRAM_ID,
|
|
||||||
);
|
|
||||||
|
|
||||||
const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
|
|
||||||
AccountLayout.span,
|
|
||||||
);
|
|
||||||
|
|
||||||
const obligation = existingObligation
|
|
||||||
? existingObligation.pubkey
|
|
||||||
: createUninitializedObligation(
|
|
||||||
instructions,
|
|
||||||
wallet.publicKey,
|
|
||||||
await connection.getMinimumBalanceForRentExemption(
|
|
||||||
LendingObligationLayout.span,
|
|
||||||
),
|
|
||||||
signers,
|
|
||||||
);
|
|
||||||
|
|
||||||
const obligationMint = existingObligation
|
|
||||||
? existingObligation.info.tokenMint
|
|
||||||
: createUninitializedMint(
|
|
||||||
instructions,
|
|
||||||
wallet.publicKey,
|
|
||||||
await connection.getMinimumBalanceForRentExemption(MintLayout.span),
|
|
||||||
signers,
|
|
||||||
);
|
|
||||||
|
|
||||||
const obligationTokenOutput = obligationAccount
|
|
||||||
? obligationAccount
|
|
||||||
: createUninitializedAccount(
|
|
||||||
instructions,
|
|
||||||
wallet.publicKey,
|
|
||||||
accountRentExempt,
|
|
||||||
signers,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!obligationAccount) {
|
|
||||||
instructions.push(
|
|
||||||
initObligationInstruction(
|
|
||||||
depositReserve.pubkey,
|
|
||||||
borrowReserve.pubkey,
|
|
||||||
obligation,
|
|
||||||
obligationMint,
|
|
||||||
obligationTokenOutput,
|
|
||||||
wallet.publicKey,
|
|
||||||
depositReserve.info.lendingMarket,
|
|
||||||
authority,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates host fee account if it doesn't exsist
|
|
||||||
let hostFeeReceiver = LEND_HOST_FEE_ADDRESS
|
|
||||||
? findOrCreateAccountByMint(
|
|
||||||
wallet.publicKey,
|
|
||||||
LEND_HOST_FEE_ADDRESS,
|
|
||||||
instructions,
|
|
||||||
[],
|
|
||||||
accountRentExempt,
|
|
||||||
depositReserve.info.collateralMint,
|
|
||||||
signers,
|
|
||||||
)
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
let amountLamports: number = 0;
|
|
||||||
let fromLamports: number = 0;
|
|
||||||
if (amountType === BorrowAmountType.LiquidityBorrowAmount) {
|
|
||||||
// approve max transfer
|
|
||||||
// TODO: improve contrain by using dex market data
|
|
||||||
const approvedAmount = from.info.amount.toNumber();
|
|
||||||
|
|
||||||
fromLamports = approvedAmount - accountRentExempt;
|
|
||||||
|
|
||||||
const mint = (await cache.query(
|
|
||||||
connection,
|
|
||||||
borrowReserve.info.liquidityMint,
|
|
||||||
MintParser,
|
|
||||||
)) as ParsedAccount<MintInfo>;
|
|
||||||
|
|
||||||
amountLamports = toLamports(amount, mint?.info);
|
|
||||||
} else if (amountType === BorrowAmountType.CollateralDepositAmount) {
|
|
||||||
const mint = (await cache.query(
|
|
||||||
connection,
|
|
||||||
depositReserve.info.collateralMint,
|
|
||||||
MintParser,
|
|
||||||
)) as ParsedAccount<MintInfo>;
|
|
||||||
amountLamports = toLamports(amount, mint?.info);
|
|
||||||
fromLamports = amountLamports;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fromAccount = ensureSplAccount(
|
|
||||||
instructions,
|
|
||||||
finalCleanupInstructions,
|
|
||||||
from,
|
|
||||||
wallet.publicKey,
|
|
||||||
fromLamports + accountRentExempt,
|
|
||||||
signers,
|
|
||||||
);
|
|
||||||
|
|
||||||
let toAccount = await findOrCreateAccountByMint(
|
|
||||||
wallet.publicKey,
|
|
||||||
wallet.publicKey,
|
|
||||||
instructions,
|
|
||||||
finalCleanupInstructions,
|
|
||||||
accountRentExempt,
|
|
||||||
borrowReserve.info.liquidityMint,
|
|
||||||
signers,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (instructions.length > 0) {
|
|
||||||
// create all accounts in one transaction
|
|
||||||
let { txid } = await sendTransaction(connection, wallet, instructions, [
|
|
||||||
...signers,
|
|
||||||
]);
|
|
||||||
|
|
||||||
notify({
|
|
||||||
message: 'Obligation accounts created',
|
|
||||||
description: `Transaction ${txid}`,
|
|
||||||
type: 'success',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
notify({
|
|
||||||
message: 'Borrowing funds...',
|
|
||||||
description: 'Please review transactions to approve.',
|
|
||||||
type: 'warn',
|
|
||||||
});
|
|
||||||
|
|
||||||
signers = [];
|
|
||||||
instructions = [];
|
|
||||||
cleanupInstructions = [...finalCleanupInstructions];
|
|
||||||
|
|
||||||
// create approval for transfer transactions
|
|
||||||
const transferAuthority = approve(
|
|
||||||
instructions,
|
|
||||||
cleanupInstructions,
|
|
||||||
fromAccount,
|
|
||||||
wallet.publicKey,
|
|
||||||
fromLamports,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
signers.push(transferAuthority);
|
|
||||||
|
|
||||||
const dexMarketAddress = borrowReserve.info.dexMarketOption
|
|
||||||
? borrowReserve.info.dexMarket
|
|
||||||
: depositReserve.info.dexMarket;
|
|
||||||
const dexMarket = cache.get(dexMarketAddress);
|
|
||||||
|
|
||||||
if (!dexMarket) {
|
|
||||||
throw new Error(`Dex market doesn't exist.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const market = cache.get(
|
|
||||||
depositReserve.info.lendingMarket,
|
|
||||||
) as ParsedAccount<LendingMarket>;
|
|
||||||
const dexOrderBookSide = market.info.quoteMint.equals(
|
|
||||||
depositReserve.info.liquidityMint,
|
|
||||||
)
|
|
||||||
? dexMarket?.info.asks
|
|
||||||
: dexMarket?.info.bids;
|
|
||||||
|
|
||||||
const memory = createTempMemoryAccount(
|
|
||||||
instructions,
|
|
||||||
wallet.publicKey,
|
|
||||||
signers,
|
|
||||||
LENDING_PROGRAM_ID,
|
|
||||||
);
|
|
||||||
|
|
||||||
instructions.push(
|
|
||||||
accrueInterestInstruction(depositReserve.pubkey, borrowReserve.pubkey),
|
|
||||||
);
|
|
||||||
// borrow
|
|
||||||
instructions.push(
|
|
||||||
borrowInstruction(
|
|
||||||
amountLamports,
|
|
||||||
amountType,
|
|
||||||
fromAccount,
|
|
||||||
toAccount,
|
|
||||||
depositReserve.pubkey,
|
|
||||||
depositReserve.info.collateralSupply,
|
|
||||||
depositReserve.info.collateralFeesReceiver,
|
|
||||||
|
|
||||||
borrowReserve.pubkey,
|
|
||||||
borrowReserve.info.liquiditySupply,
|
|
||||||
|
|
||||||
obligation,
|
|
||||||
obligationMint,
|
|
||||||
obligationTokenOutput,
|
|
||||||
|
|
||||||
depositReserve.info.lendingMarket,
|
|
||||||
authority,
|
|
||||||
transferAuthority.publicKey,
|
|
||||||
|
|
||||||
dexMarketAddress,
|
|
||||||
dexOrderBookSide,
|
|
||||||
|
|
||||||
memory,
|
|
||||||
|
|
||||||
hostFeeReceiver,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
let { txid } = await sendTransaction(
|
|
||||||
connection,
|
|
||||||
wallet,
|
|
||||||
instructions.concat(cleanupInstructions),
|
|
||||||
signers,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
notify({
|
|
||||||
message: 'Funds borrowed.',
|
|
||||||
type: 'success',
|
|
||||||
description: `Transaction - ${txid}`,
|
|
||||||
});
|
|
||||||
} catch (ex) {
|
|
||||||
console.error(ex);
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
import {
|
||||||
|
contexts,
|
||||||
|
findOrCreateAccountByMint,
|
||||||
|
LEND_HOST_FEE_ADDRESS,
|
||||||
|
LENDING_PROGRAM_ID,
|
||||||
|
notify,
|
||||||
|
ParsedAccount,
|
||||||
|
toLamports,
|
||||||
|
} from '@oyster/common';
|
||||||
|
import { AccountLayout, MintInfo } from '@solana/spl-token';
|
||||||
|
import {
|
||||||
|
Account,
|
||||||
|
Connection,
|
||||||
|
PublicKey,
|
||||||
|
TransactionInstruction,
|
||||||
|
} from '@solana/web3.js';
|
||||||
|
import {
|
||||||
|
borrowObligationLiquidityInstruction,
|
||||||
|
Obligation,
|
||||||
|
refreshObligationInstruction,
|
||||||
|
refreshReserveInstruction,
|
||||||
|
Reserve,
|
||||||
|
} from '../models';
|
||||||
|
|
||||||
|
const { cache, MintParser } = contexts.Accounts;
|
||||||
|
const { sendTransaction } = contexts.Connection;
|
||||||
|
|
||||||
|
// @FIXME
|
||||||
|
export const borrowObligationLiquidity = async (
|
||||||
|
connection: Connection,
|
||||||
|
wallet: any,
|
||||||
|
liquidityAmount: number,
|
||||||
|
borrowReserve: ParsedAccount<Reserve>,
|
||||||
|
obligation: ParsedAccount<Obligation>,
|
||||||
|
) => {
|
||||||
|
notify({
|
||||||
|
message: 'Borrowing funds...',
|
||||||
|
description: 'Please review transactions to approve.',
|
||||||
|
type: 'warn',
|
||||||
|
});
|
||||||
|
|
||||||
|
let signers: Account[] = [];
|
||||||
|
let instructions: TransactionInstruction[] = [];
|
||||||
|
let cleanupInstructions: TransactionInstruction[] = [];
|
||||||
|
let finalCleanupInstructions: TransactionInstruction[] = [];
|
||||||
|
|
||||||
|
const [lendingMarketAuthority] = await PublicKey.findProgramAddress(
|
||||||
|
[borrowReserve.info.lendingMarket.toBuffer()],
|
||||||
|
LENDING_PROGRAM_ID,
|
||||||
|
);
|
||||||
|
|
||||||
|
const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
|
||||||
|
AccountLayout.span,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Creates host fee account if it doesn't exist
|
||||||
|
let hostFeeReceiver = LEND_HOST_FEE_ADDRESS
|
||||||
|
? findOrCreateAccountByMint(
|
||||||
|
wallet.publicKey,
|
||||||
|
LEND_HOST_FEE_ADDRESS,
|
||||||
|
instructions,
|
||||||
|
[],
|
||||||
|
accountRentExempt,
|
||||||
|
borrowReserve.info.liquidity.mint,
|
||||||
|
signers,
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const mint = (await cache.query(
|
||||||
|
connection,
|
||||||
|
borrowReserve.info.liquidity.mint,
|
||||||
|
MintParser,
|
||||||
|
)) as ParsedAccount<MintInfo>;
|
||||||
|
|
||||||
|
// @TODO: handle 100% -> u64::MAX
|
||||||
|
const amountLamports = toLamports(liquidityAmount, mint?.info);
|
||||||
|
|
||||||
|
let destinationLiquidity = await findOrCreateAccountByMint(
|
||||||
|
wallet.publicKey,
|
||||||
|
wallet.publicKey,
|
||||||
|
instructions,
|
||||||
|
finalCleanupInstructions,
|
||||||
|
accountRentExempt,
|
||||||
|
borrowReserve.info.liquidity.mint,
|
||||||
|
signers,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (instructions.length > 0) {
|
||||||
|
// create all accounts in one transaction
|
||||||
|
let { txid } = await sendTransaction(connection, wallet, instructions, [
|
||||||
|
...signers,
|
||||||
|
]);
|
||||||
|
|
||||||
|
notify({
|
||||||
|
// @TODO: change message
|
||||||
|
message: 'Obligation accounts created',
|
||||||
|
description: `Transaction ${txid}`,
|
||||||
|
type: 'success',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
notify({
|
||||||
|
message: 'Borrowing funds...',
|
||||||
|
description: 'Please review transactions to approve.',
|
||||||
|
type: 'warn',
|
||||||
|
});
|
||||||
|
|
||||||
|
// @FIXME: signers
|
||||||
|
signers = [];
|
||||||
|
instructions = [];
|
||||||
|
cleanupInstructions = [...finalCleanupInstructions];
|
||||||
|
|
||||||
|
instructions.push(
|
||||||
|
refreshReserveInstruction(
|
||||||
|
borrowReserve.pubkey,
|
||||||
|
borrowReserve.info.liquidity.aggregatorOption
|
||||||
|
? borrowReserve.info.liquidity.aggregator
|
||||||
|
: undefined,
|
||||||
|
),
|
||||||
|
refreshObligationInstruction(
|
||||||
|
obligation.pubkey,
|
||||||
|
obligation.info.deposits.map((collateral) => collateral.depositReserve),
|
||||||
|
obligation.info.borrows.map((liquidity) => liquidity.borrowReserve),
|
||||||
|
),
|
||||||
|
borrowObligationLiquidityInstruction(
|
||||||
|
amountLamports,
|
||||||
|
borrowReserve.info.liquidity.supply,
|
||||||
|
destinationLiquidity,
|
||||||
|
borrowReserve.pubkey,
|
||||||
|
borrowReserve.info.liquidity.feeReceiver,
|
||||||
|
obligation.pubkey,
|
||||||
|
borrowReserve.info.lendingMarket,
|
||||||
|
lendingMarketAuthority,
|
||||||
|
obligation.info.owner,
|
||||||
|
hostFeeReceiver,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
let { txid } = await sendTransaction(
|
||||||
|
connection,
|
||||||
|
wallet,
|
||||||
|
instructions.concat(cleanupInstructions),
|
||||||
|
signers,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
notify({
|
||||||
|
message: 'Funds borrowed.',
|
||||||
|
type: 'success',
|
||||||
|
description: `Transaction - ${txid}`,
|
||||||
|
});
|
||||||
|
} catch (ex) {
|
||||||
|
console.error(ex);
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,25 +1,26 @@
|
||||||
|
import { LENDING_PROGRAM_ID } from '@oyster/common';
|
||||||
import {
|
import {
|
||||||
Account,
|
Account,
|
||||||
PublicKey,
|
PublicKey,
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
TransactionInstruction,
|
TransactionInstruction,
|
||||||
} from '@solana/web3.js';
|
} from '@solana/web3.js';
|
||||||
import { utils } from '@oyster/common';
|
|
||||||
import { LendingObligationLayout } from '../models';
|
export function createAccount(
|
||||||
const { LENDING_PROGRAM_ID } = utils;
|
|
||||||
export function createUninitializedObligation(
|
|
||||||
instructions: TransactionInstruction[],
|
instructions: TransactionInstruction[],
|
||||||
payer: PublicKey,
|
payer: PublicKey,
|
||||||
amount: number,
|
amount: number,
|
||||||
signers: Account[],
|
signers: Account[],
|
||||||
|
space: number,
|
||||||
) {
|
) {
|
||||||
const account = new Account();
|
const account = new Account();
|
||||||
|
|
||||||
instructions.push(
|
instructions.push(
|
||||||
SystemProgram.createAccount({
|
SystemProgram.createAccount({
|
||||||
fromPubkey: payer,
|
fromPubkey: payer,
|
||||||
newAccountPubkey: account.publicKey,
|
newAccountPubkey: account.publicKey,
|
||||||
lamports: amount,
|
lamports: amount,
|
||||||
space: LendingObligationLayout.span,
|
space,
|
||||||
programId: LENDING_PROGRAM_ID,
|
programId: LENDING_PROGRAM_ID,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Account, PublicKey, TransactionInstruction } from '@solana/web3.js';
|
||||||
|
import { ObligationLayout } from '../models';
|
||||||
|
import { createAccount } from './createAccount';
|
||||||
|
|
||||||
|
export function createObligation(
|
||||||
|
instructions: TransactionInstruction[],
|
||||||
|
payer: PublicKey,
|
||||||
|
amount: number,
|
||||||
|
signers: Account[],
|
||||||
|
) {
|
||||||
|
return createAccount(
|
||||||
|
instructions,
|
||||||
|
payer,
|
||||||
|
amount,
|
||||||
|
signers,
|
||||||
|
ObligationLayout.span,
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Account, PublicKey, TransactionInstruction } from '@solana/web3.js';
|
||||||
|
import { ReserveLayout } from '../models';
|
||||||
|
import { createAccount } from './createAccount';
|
||||||
|
|
||||||
|
export function createReserve(
|
||||||
|
instructions: TransactionInstruction[],
|
||||||
|
payer: PublicKey,
|
||||||
|
amount: number,
|
||||||
|
signers: Account[],
|
||||||
|
) {
|
||||||
|
return createAccount(
|
||||||
|
instructions,
|
||||||
|
payer,
|
||||||
|
amount,
|
||||||
|
signers,
|
||||||
|
ReserveLayout.span,
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,20 +1,26 @@
|
||||||
|
import {
|
||||||
|
actions,
|
||||||
|
contexts,
|
||||||
|
LENDING_PROGRAM_ID,
|
||||||
|
models,
|
||||||
|
notify,
|
||||||
|
TokenAccount,
|
||||||
|
} from '@oyster/common';
|
||||||
|
import { AccountLayout } from '@solana/spl-token';
|
||||||
import {
|
import {
|
||||||
Account,
|
Account,
|
||||||
Connection,
|
Connection,
|
||||||
PublicKey,
|
PublicKey,
|
||||||
TransactionInstruction,
|
TransactionInstruction,
|
||||||
} from '@solana/web3.js';
|
} from '@solana/web3.js';
|
||||||
import { contexts, utils, models, actions, TokenAccount } from '@oyster/common';
|
|
||||||
import {
|
import {
|
||||||
accrueInterestInstruction,
|
depositReserveLiquidityInstruction,
|
||||||
depositInstruction,
|
|
||||||
initReserveInstruction,
|
initReserveInstruction,
|
||||||
LendingReserve,
|
refreshReserveInstruction,
|
||||||
} from './../models/lending';
|
Reserve,
|
||||||
import { AccountLayout } from '@solana/spl-token';
|
} from '../models';
|
||||||
|
|
||||||
const { sendTransaction } = contexts.Connection;
|
const { sendTransaction } = contexts.Connection;
|
||||||
const { LENDING_PROGRAM_ID, notify } = utils;
|
|
||||||
const {
|
const {
|
||||||
createUninitializedAccount,
|
createUninitializedAccount,
|
||||||
ensureSplAccount,
|
ensureSplAccount,
|
||||||
|
@ -22,13 +28,14 @@ const {
|
||||||
} = actions;
|
} = actions;
|
||||||
const { approve } = models;
|
const { approve } = models;
|
||||||
|
|
||||||
export const deposit = async (
|
// @FIXME: split up into deposit, and init which requires lending market owner
|
||||||
from: TokenAccount,
|
export const depositReserveLiquidity = async (
|
||||||
amountLamports: number,
|
|
||||||
reserve: LendingReserve,
|
|
||||||
reserveAddress: PublicKey,
|
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
wallet: any,
|
wallet: any,
|
||||||
|
liquidityAmount: number,
|
||||||
|
source: TokenAccount,
|
||||||
|
reserve: Reserve,
|
||||||
|
reserveAddress: PublicKey,
|
||||||
) => {
|
) => {
|
||||||
notify({
|
notify({
|
||||||
message: 'Depositing funds...',
|
message: 'Depositing funds...',
|
||||||
|
@ -47,17 +54,17 @@ export const deposit = async (
|
||||||
AccountLayout.span,
|
AccountLayout.span,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [authority] = await PublicKey.findProgramAddress(
|
const [lendingMarketAuthority] = await PublicKey.findProgramAddress(
|
||||||
[reserve.lendingMarket.toBuffer()], // which account should be authority
|
[reserve.lendingMarket.toBuffer()], // which account should be authority
|
||||||
LENDING_PROGRAM_ID,
|
LENDING_PROGRAM_ID,
|
||||||
);
|
);
|
||||||
|
|
||||||
const fromAccount = ensureSplAccount(
|
const sourceLiquidityAccount = ensureSplAccount(
|
||||||
instructions,
|
instructions,
|
||||||
cleanupInstructions,
|
cleanupInstructions,
|
||||||
from,
|
source,
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
amountLamports + accountRentExempt,
|
liquidityAmount + accountRentExempt,
|
||||||
signers,
|
signers,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -65,75 +72,80 @@ export const deposit = async (
|
||||||
const transferAuthority = approve(
|
const transferAuthority = approve(
|
||||||
instructions,
|
instructions,
|
||||||
cleanupInstructions,
|
cleanupInstructions,
|
||||||
fromAccount,
|
sourceLiquidityAccount,
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
amountLamports,
|
liquidityAmount,
|
||||||
);
|
);
|
||||||
|
|
||||||
signers.push(transferAuthority);
|
signers.push(transferAuthority);
|
||||||
|
|
||||||
let toAccount: PublicKey;
|
let destinationCollateralAccount: PublicKey = isInitalized
|
||||||
if (isInitalized) {
|
? await findOrCreateAccountByMint(
|
||||||
// get destination account
|
wallet.publicKey,
|
||||||
toAccount = await findOrCreateAccountByMint(
|
wallet.publicKey,
|
||||||
wallet.publicKey,
|
instructions,
|
||||||
wallet.publicKey,
|
cleanupInstructions,
|
||||||
instructions,
|
accountRentExempt,
|
||||||
cleanupInstructions,
|
reserve.collateral.mint,
|
||||||
accountRentExempt,
|
signers,
|
||||||
reserve.collateralMint,
|
)
|
||||||
signers,
|
: createUninitializedAccount(
|
||||||
);
|
instructions,
|
||||||
} else {
|
wallet.publicKey,
|
||||||
toAccount = createUninitializedAccount(
|
accountRentExempt,
|
||||||
instructions,
|
signers,
|
||||||
wallet.publicKey,
|
);
|
||||||
accountRentExempt,
|
|
||||||
signers,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isInitalized) {
|
if (isInitalized) {
|
||||||
instructions.push(accrueInterestInstruction(reserveAddress));
|
|
||||||
|
|
||||||
// deposit
|
|
||||||
instructions.push(
|
instructions.push(
|
||||||
depositInstruction(
|
refreshReserveInstruction(
|
||||||
amountLamports,
|
|
||||||
fromAccount,
|
|
||||||
toAccount,
|
|
||||||
reserve.lendingMarket,
|
|
||||||
authority,
|
|
||||||
transferAuthority.publicKey,
|
|
||||||
reserveAddress,
|
reserveAddress,
|
||||||
reserve.liquiditySupply,
|
reserve.liquidity.aggregatorOption
|
||||||
reserve.collateralMint,
|
? reserve.liquidity.aggregator
|
||||||
|
: undefined,
|
||||||
|
),
|
||||||
|
depositReserveLiquidityInstruction(
|
||||||
|
liquidityAmount,
|
||||||
|
sourceLiquidityAccount,
|
||||||
|
destinationCollateralAccount,
|
||||||
|
reserveAddress,
|
||||||
|
reserve.liquidity.supply,
|
||||||
|
reserve.collateral.mint,
|
||||||
|
reserve.lendingMarket,
|
||||||
|
lendingMarketAuthority,
|
||||||
|
transferAuthority.publicKey,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// TODO: finish reserve init
|
// TODO: finish reserve init
|
||||||
|
// @FIXME: reserve config
|
||||||
const MAX_UTILIZATION_RATE = 80;
|
const MAX_UTILIZATION_RATE = 80;
|
||||||
instructions.push(
|
instructions.push(
|
||||||
initReserveInstruction(
|
initReserveInstruction(
|
||||||
amountLamports,
|
liquidityAmount,
|
||||||
MAX_UTILIZATION_RATE,
|
MAX_UTILIZATION_RATE,
|
||||||
fromAccount,
|
sourceLiquidityAccount,
|
||||||
toAccount,
|
destinationCollateralAccount,
|
||||||
reserveAddress,
|
reserveAddress,
|
||||||
reserve.liquidityMint,
|
reserve.liquidity.mint,
|
||||||
reserve.liquiditySupply,
|
reserve.liquidity.supply,
|
||||||
reserve.collateralMint,
|
reserve.liquidity.feeReceiver,
|
||||||
reserve.collateralSupply,
|
reserve.collateral.mint,
|
||||||
|
reserve.collateral.supply,
|
||||||
reserve.lendingMarket,
|
reserve.lendingMarket,
|
||||||
authority,
|
lendingMarketAuthority,
|
||||||
|
// @FIXME: lending market owner
|
||||||
|
lendingMarketOwner,
|
||||||
transferAuthority.publicKey,
|
transferAuthority.publicKey,
|
||||||
reserve.dexMarket,
|
reserve.liquidity.aggregatorOption
|
||||||
|
? reserve.liquidity.aggregator
|
||||||
|
: undefined,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let { txid } = await sendTransaction(
|
let { txid } = await sendTransaction(
|
||||||
connection,
|
connection,
|
||||||
wallet,
|
wallet,
|
||||||
instructions.concat(cleanupInstructions),
|
instructions.concat(cleanupInstructions),
|
|
@ -1,5 +1,7 @@
|
||||||
export { borrow } from './borrow';
|
export { borrowObligationLiquidity } from './borrowObligationLiquidity';
|
||||||
export { deposit } from './deposit';
|
export { depositReserveLiquidity } from './depositReserveLiquidity';
|
||||||
export { repay } from './repay';
|
export { repayObligationLiquidity } from './repayObligationLiquidity';
|
||||||
export { withdraw } from './withdraw';
|
export { redeemReserveCollateral } from './redeemReserveCollateral';
|
||||||
export { liquidate } from './liquidate';
|
export { liquidateObligation } from './liquidateObligation';
|
||||||
|
|
||||||
|
// @TODO: add actions for other instructions
|
||||||
|
|
|
@ -1,3 +1,15 @@
|
||||||
|
import {
|
||||||
|
contexts,
|
||||||
|
createTempMemoryAccount,
|
||||||
|
ensureSplAccount,
|
||||||
|
findOrCreateAccountByMint,
|
||||||
|
LENDING_PROGRAM_ID,
|
||||||
|
models,
|
||||||
|
notify,
|
||||||
|
ParsedAccount,
|
||||||
|
TokenAccount,
|
||||||
|
} from '@oyster/common';
|
||||||
|
import { AccountLayout } from '@solana/spl-token';
|
||||||
import {
|
import {
|
||||||
Account,
|
Account,
|
||||||
Connection,
|
Connection,
|
||||||
|
@ -5,42 +17,26 @@ import {
|
||||||
TransactionInstruction,
|
TransactionInstruction,
|
||||||
} from '@solana/web3.js';
|
} from '@solana/web3.js';
|
||||||
import {
|
import {
|
||||||
contexts,
|
LendingMarket,
|
||||||
utils,
|
liquidateObligationInstruction,
|
||||||
actions,
|
Obligation,
|
||||||
models,
|
refreshReserveInstruction,
|
||||||
ParsedAccount,
|
Reserve,
|
||||||
TokenAccount,
|
} from '../models';
|
||||||
} from '@oyster/common';
|
|
||||||
import {
|
|
||||||
accrueInterestInstruction,
|
|
||||||
LendingReserve,
|
|
||||||
} from './../models/lending/reserve';
|
|
||||||
import { liquidateInstruction } from './../models/lending/liquidate';
|
|
||||||
import { AccountLayout } from '@solana/spl-token';
|
|
||||||
import { LendingMarket, LendingObligation } from '../models';
|
|
||||||
const { cache } = contexts.Accounts;
|
const { cache } = contexts.Accounts;
|
||||||
const { approve } = models;
|
const { approve } = models;
|
||||||
const {
|
|
||||||
createTempMemoryAccount,
|
|
||||||
ensureSplAccount,
|
|
||||||
findOrCreateAccountByMint,
|
|
||||||
} = actions;
|
|
||||||
const { sendTransaction } = contexts.Connection;
|
const { sendTransaction } = contexts.Connection;
|
||||||
const { LENDING_PROGRAM_ID, notify } = utils;
|
|
||||||
|
|
||||||
export const liquidate = async (
|
// @FIXME
|
||||||
|
export const liquidateObligation = async (
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
wallet: any,
|
wallet: any,
|
||||||
from: TokenAccount, // liquidity account
|
liquidityAmount: number,
|
||||||
amountLamports: number, // in liquidty token (lamports)
|
source: TokenAccount,
|
||||||
|
repayReserve: ParsedAccount<Reserve>,
|
||||||
// which loan to repay
|
withdrawReserve: ParsedAccount<Reserve>,
|
||||||
obligation: ParsedAccount<LendingObligation>,
|
obligation: ParsedAccount<Obligation>,
|
||||||
|
|
||||||
repayReserve: ParsedAccount<LendingReserve>,
|
|
||||||
|
|
||||||
withdrawReserve: ParsedAccount<LendingReserve>,
|
|
||||||
) => {
|
) => {
|
||||||
notify({
|
notify({
|
||||||
message: 'Repaying funds...',
|
message: 'Repaying funds...',
|
||||||
|
@ -57,17 +53,17 @@ export const liquidate = async (
|
||||||
AccountLayout.span,
|
AccountLayout.span,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [authority] = await PublicKey.findProgramAddress(
|
const [lendingMarketAuthority] = await PublicKey.findProgramAddress(
|
||||||
[repayReserve.info.lendingMarket.toBuffer()],
|
[repayReserve.info.lendingMarket.toBuffer()],
|
||||||
LENDING_PROGRAM_ID,
|
LENDING_PROGRAM_ID,
|
||||||
);
|
);
|
||||||
|
|
||||||
const fromAccount = ensureSplAccount(
|
const sourceAccount = ensureSplAccount(
|
||||||
instructions,
|
instructions,
|
||||||
cleanupInstructions,
|
cleanupInstructions,
|
||||||
from,
|
source,
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
amountLamports + accountRentExempt,
|
liquidityAmount + accountRentExempt,
|
||||||
signers,
|
signers,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -75,9 +71,9 @@ export const liquidate = async (
|
||||||
const transferAuthority = approve(
|
const transferAuthority = approve(
|
||||||
instructions,
|
instructions,
|
||||||
cleanupInstructions,
|
cleanupInstructions,
|
||||||
fromAccount,
|
sourceAccount,
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
amountLamports,
|
liquidityAmount,
|
||||||
);
|
);
|
||||||
signers.push(transferAuthority);
|
signers.push(transferAuthority);
|
||||||
|
|
||||||
|
@ -88,16 +84,17 @@ export const liquidate = async (
|
||||||
instructions,
|
instructions,
|
||||||
cleanupInstructions,
|
cleanupInstructions,
|
||||||
accountRentExempt,
|
accountRentExempt,
|
||||||
withdrawReserve.info.collateralMint,
|
withdrawReserve.info.collateral.mint,
|
||||||
signers,
|
signers,
|
||||||
);
|
);
|
||||||
|
|
||||||
const dexMarketAddress = repayReserve.info.dexMarketOption
|
// @FIXME: aggregator
|
||||||
? repayReserve.info.dexMarket
|
const aggregatorAddress = repayReserve.info.liquidity.aggregatorOption
|
||||||
: withdrawReserve.info.dexMarket;
|
? repayReserve.info.liquidity.aggregator
|
||||||
const dexMarket = cache.get(dexMarketAddress);
|
: withdrawReserve.info.liquidity.aggregator;
|
||||||
|
const aggregator = cache.get(aggregatorAddress);
|
||||||
|
|
||||||
if (!dexMarket) {
|
if (!aggregator) {
|
||||||
throw new Error(`Dex market doesn't exist.`);
|
throw new Error(`Dex market doesn't exist.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,11 +102,11 @@ export const liquidate = async (
|
||||||
withdrawReserve.info.lendingMarket,
|
withdrawReserve.info.lendingMarket,
|
||||||
) as ParsedAccount<LendingMarket>;
|
) as ParsedAccount<LendingMarket>;
|
||||||
|
|
||||||
const dexOrderBookSide = market.info.quoteMint.equals(
|
const dexOrderBookSide = market.info.quoteTokenMint.equals(
|
||||||
repayReserve.info.liquidityMint,
|
repayReserve.info.liquidity.mint,
|
||||||
)
|
)
|
||||||
? dexMarket?.info.asks
|
? aggregator?.info.asks
|
||||||
: dexMarket?.info.bids;
|
: aggregator?.info.bids;
|
||||||
|
|
||||||
const memory = createTempMemoryAccount(
|
const memory = createTempMemoryAccount(
|
||||||
instructions,
|
instructions,
|
||||||
|
@ -119,25 +116,24 @@ export const liquidate = async (
|
||||||
);
|
);
|
||||||
|
|
||||||
instructions.push(
|
instructions.push(
|
||||||
accrueInterestInstruction(repayReserve.pubkey, withdrawReserve.pubkey),
|
// @FIXME: aggregator needed
|
||||||
|
refreshReserveInstruction(repayReserve.pubkey),
|
||||||
|
refreshReserveInstruction(withdrawReserve.pubkey),
|
||||||
);
|
);
|
||||||
|
|
||||||
instructions.push(
|
instructions.push(
|
||||||
liquidateInstruction(
|
liquidateObligationInstruction(
|
||||||
amountLamports,
|
liquidityAmount,
|
||||||
fromAccount,
|
sourceAccount,
|
||||||
toAccount,
|
toAccount,
|
||||||
repayReserve.pubkey,
|
repayReserve.pubkey,
|
||||||
repayReserve.info.liquiditySupply,
|
repayReserve.info.liquidity.supply,
|
||||||
withdrawReserve.pubkey,
|
withdrawReserve.pubkey,
|
||||||
withdrawReserve.info.collateralSupply,
|
withdrawReserve.info.collateral.supply,
|
||||||
obligation.pubkey,
|
obligation.pubkey,
|
||||||
repayReserve.info.lendingMarket,
|
repayReserve.info.lendingMarket,
|
||||||
authority,
|
lendingMarketAuthority,
|
||||||
transferAuthority.publicKey,
|
transferAuthority.publicKey,
|
||||||
dexMarketAddress,
|
|
||||||
dexOrderBookSide,
|
|
||||||
memory,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,28 +1,35 @@
|
||||||
|
import {
|
||||||
|
contexts,
|
||||||
|
findOrCreateAccountByMint,
|
||||||
|
LENDING_PROGRAM_ID,
|
||||||
|
models,
|
||||||
|
notify,
|
||||||
|
TokenAccount,
|
||||||
|
} from '@oyster/common';
|
||||||
|
import { AccountLayout } from '@solana/spl-token';
|
||||||
import {
|
import {
|
||||||
Account,
|
Account,
|
||||||
Connection,
|
Connection,
|
||||||
PublicKey,
|
PublicKey,
|
||||||
TransactionInstruction,
|
TransactionInstruction,
|
||||||
} from '@solana/web3.js';
|
} from '@solana/web3.js';
|
||||||
import { contexts, utils, actions, models, TokenAccount } from '@oyster/common';
|
|
||||||
import {
|
import {
|
||||||
accrueInterestInstruction,
|
redeemReserveCollateralInstruction,
|
||||||
LendingReserve,
|
refreshReserveInstruction,
|
||||||
withdrawInstruction,
|
Reserve,
|
||||||
} from './../models/lending';
|
} from '../models';
|
||||||
import { AccountLayout } from '@solana/spl-token';
|
|
||||||
const { approve } = models;
|
|
||||||
const { findOrCreateAccountByMint } = actions;
|
|
||||||
const { sendTransaction } = contexts.Connection;
|
|
||||||
const { LENDING_PROGRAM_ID, notify } = utils;
|
|
||||||
|
|
||||||
export const withdraw = async (
|
const { approve } = models;
|
||||||
from: TokenAccount, // CollateralAccount
|
const { sendTransaction } = contexts.Connection;
|
||||||
amountLamports: number, // in collateral token (lamports)
|
|
||||||
reserve: LendingReserve,
|
// @FIXME
|
||||||
reserveAddress: PublicKey,
|
export const redeemReserveCollateral = async (
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
wallet: any,
|
wallet: any,
|
||||||
|
collateralAmount: number,
|
||||||
|
source: TokenAccount,
|
||||||
|
reserve: Reserve,
|
||||||
|
reserveAddress: PublicKey,
|
||||||
) => {
|
) => {
|
||||||
notify({
|
notify({
|
||||||
message: 'Withdrawing funds...',
|
message: 'Withdrawing funds...',
|
||||||
|
@ -39,53 +46,57 @@ export const withdraw = async (
|
||||||
AccountLayout.span,
|
AccountLayout.span,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [authority] = await PublicKey.findProgramAddress(
|
const [lendingMarketAuthority] = await PublicKey.findProgramAddress(
|
||||||
[reserve.lendingMarket.toBuffer()],
|
[reserve.lendingMarket.toBuffer()],
|
||||||
LENDING_PROGRAM_ID,
|
LENDING_PROGRAM_ID,
|
||||||
);
|
);
|
||||||
|
|
||||||
const fromAccount = from.pubkey;
|
const sourceCollateral = source.pubkey;
|
||||||
|
|
||||||
// create approval for transfer transactions
|
// create approval for transfer transactions
|
||||||
const transferAuthority = approve(
|
const transferAuthority = approve(
|
||||||
instructions,
|
instructions,
|
||||||
cleanupInstructions,
|
cleanupInstructions,
|
||||||
fromAccount,
|
sourceCollateral,
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
amountLamports,
|
collateralAmount,
|
||||||
);
|
);
|
||||||
|
|
||||||
signers.push(transferAuthority);
|
signers.push(transferAuthority);
|
||||||
|
|
||||||
// get destination account
|
// get destination account
|
||||||
const toAccount = await findOrCreateAccountByMint(
|
const destinationLiquidity = await findOrCreateAccountByMint(
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
instructions,
|
instructions,
|
||||||
cleanupInstructions,
|
cleanupInstructions,
|
||||||
accountRentExempt,
|
accountRentExempt,
|
||||||
reserve.liquidityMint,
|
reserve.liquidity.mint,
|
||||||
signers,
|
signers,
|
||||||
);
|
);
|
||||||
|
|
||||||
instructions.push(accrueInterestInstruction(reserveAddress));
|
|
||||||
|
|
||||||
instructions.push(
|
instructions.push(
|
||||||
withdrawInstruction(
|
refreshReserveInstruction(
|
||||||
amountLamports,
|
|
||||||
fromAccount,
|
|
||||||
toAccount,
|
|
||||||
reserveAddress,
|
reserveAddress,
|
||||||
reserve.collateralMint,
|
reserve.liquidity.aggregatorOption
|
||||||
reserve.liquiditySupply,
|
? reserve.liquidity.aggregator
|
||||||
|
: undefined,
|
||||||
|
),
|
||||||
|
redeemReserveCollateralInstruction(
|
||||||
|
collateralAmount,
|
||||||
|
sourceCollateral,
|
||||||
|
destinationLiquidity,
|
||||||
|
reserveAddress,
|
||||||
|
reserve.collateral.mint,
|
||||||
|
reserve.liquidity.supply,
|
||||||
reserve.lendingMarket,
|
reserve.lendingMarket,
|
||||||
authority,
|
lendingMarketAuthority,
|
||||||
transferAuthority.publicKey,
|
transferAuthority.publicKey,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let { txid } = await sendTransaction(
|
let { txid } = await sendTransaction(
|
||||||
connection,
|
connection,
|
||||||
wallet,
|
wallet,
|
||||||
instructions.concat(cleanupInstructions),
|
instructions.concat(cleanupInstructions),
|
|
@ -1,157 +0,0 @@
|
||||||
import {
|
|
||||||
Account,
|
|
||||||
Connection,
|
|
||||||
PublicKey,
|
|
||||||
TransactionInstruction,
|
|
||||||
} from '@solana/web3.js';
|
|
||||||
import {
|
|
||||||
contexts,
|
|
||||||
utils,
|
|
||||||
actions,
|
|
||||||
models,
|
|
||||||
ParsedAccount,
|
|
||||||
TokenAccount,
|
|
||||||
} from '@oyster/common';
|
|
||||||
|
|
||||||
import {
|
|
||||||
accrueInterestInstruction,
|
|
||||||
LendingReserve,
|
|
||||||
} from './../models/lending/reserve';
|
|
||||||
import { repayInstruction } from './../models/lending/repay';
|
|
||||||
import { AccountLayout, Token, NATIVE_MINT } from '@solana/spl-token';
|
|
||||||
|
|
||||||
import { LendingObligation } from '../models';
|
|
||||||
const { approve } = models;
|
|
||||||
const { createTokenAccount, findOrCreateAccountByMint } = actions;
|
|
||||||
const { sendTransaction } = contexts.Connection;
|
|
||||||
const { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID, notify } = utils;
|
|
||||||
|
|
||||||
export const repay = async (
|
|
||||||
from: TokenAccount,
|
|
||||||
repayAmount: number,
|
|
||||||
|
|
||||||
// which loan to repay
|
|
||||||
obligation: ParsedAccount<LendingObligation>,
|
|
||||||
|
|
||||||
obligationToken: TokenAccount,
|
|
||||||
|
|
||||||
repayReserve: ParsedAccount<LendingReserve>,
|
|
||||||
|
|
||||||
withdrawReserve: ParsedAccount<LendingReserve>,
|
|
||||||
|
|
||||||
connection: Connection,
|
|
||||||
wallet: any,
|
|
||||||
) => {
|
|
||||||
notify({
|
|
||||||
message: 'Repaying funds...',
|
|
||||||
description: 'Please review transactions to approve.',
|
|
||||||
type: 'warn',
|
|
||||||
});
|
|
||||||
|
|
||||||
// user from account
|
|
||||||
const signers: Account[] = [];
|
|
||||||
const instructions: TransactionInstruction[] = [];
|
|
||||||
const cleanupInstructions: TransactionInstruction[] = [];
|
|
||||||
|
|
||||||
const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
|
|
||||||
AccountLayout.span,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [authority] = await PublicKey.findProgramAddress(
|
|
||||||
[repayReserve.info.lendingMarket.toBuffer()],
|
|
||||||
LENDING_PROGRAM_ID,
|
|
||||||
);
|
|
||||||
|
|
||||||
let fromAccount = from.pubkey;
|
|
||||||
if (
|
|
||||||
wallet.publicKey.equals(fromAccount) &&
|
|
||||||
repayReserve.info.liquidityMint.equals(NATIVE_MINT)
|
|
||||||
) {
|
|
||||||
fromAccount = createTokenAccount(
|
|
||||||
instructions,
|
|
||||||
wallet.publicKey,
|
|
||||||
accountRentExempt + repayAmount,
|
|
||||||
NATIVE_MINT,
|
|
||||||
wallet.publicKey,
|
|
||||||
signers,
|
|
||||||
);
|
|
||||||
cleanupInstructions.push(
|
|
||||||
Token.createCloseAccountInstruction(
|
|
||||||
TOKEN_PROGRAM_ID,
|
|
||||||
fromAccount,
|
|
||||||
wallet.publicKey,
|
|
||||||
wallet.publicKey,
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create approval for transfer transactions
|
|
||||||
const transferAuthority = approve(
|
|
||||||
instructions,
|
|
||||||
cleanupInstructions,
|
|
||||||
fromAccount,
|
|
||||||
wallet.publicKey,
|
|
||||||
repayAmount,
|
|
||||||
);
|
|
||||||
signers.push(transferAuthority);
|
|
||||||
|
|
||||||
// get destination account
|
|
||||||
const toAccount = await findOrCreateAccountByMint(
|
|
||||||
wallet.publicKey,
|
|
||||||
wallet.publicKey,
|
|
||||||
instructions,
|
|
||||||
cleanupInstructions,
|
|
||||||
accountRentExempt,
|
|
||||||
withdrawReserve.info.collateralMint,
|
|
||||||
signers,
|
|
||||||
);
|
|
||||||
|
|
||||||
// create approval for transfer transactions
|
|
||||||
approve(
|
|
||||||
instructions,
|
|
||||||
cleanupInstructions,
|
|
||||||
obligationToken.pubkey,
|
|
||||||
wallet.publicKey,
|
|
||||||
obligationToken.info.amount.toNumber(),
|
|
||||||
true,
|
|
||||||
// reuse transfer authority
|
|
||||||
transferAuthority.publicKey,
|
|
||||||
);
|
|
||||||
|
|
||||||
instructions.push(
|
|
||||||
accrueInterestInstruction(repayReserve.pubkey, withdrawReserve.pubkey),
|
|
||||||
);
|
|
||||||
|
|
||||||
instructions.push(
|
|
||||||
repayInstruction(
|
|
||||||
repayAmount,
|
|
||||||
fromAccount,
|
|
||||||
toAccount,
|
|
||||||
repayReserve.pubkey,
|
|
||||||
repayReserve.info.liquiditySupply,
|
|
||||||
withdrawReserve.pubkey,
|
|
||||||
withdrawReserve.info.collateralSupply,
|
|
||||||
obligation.pubkey,
|
|
||||||
obligation.info.tokenMint,
|
|
||||||
obligationToken.pubkey,
|
|
||||||
repayReserve.info.lendingMarket,
|
|
||||||
authority,
|
|
||||||
transferAuthority.publicKey,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
let { txid } = await sendTransaction(
|
|
||||||
connection,
|
|
||||||
wallet,
|
|
||||||
instructions.concat(cleanupInstructions),
|
|
||||||
signers,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
notify({
|
|
||||||
message: 'Funds repaid.',
|
|
||||||
type: 'success',
|
|
||||||
description: `Transaction - ${txid}`,
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
import {
|
||||||
|
contexts,
|
||||||
|
createTokenAccount,
|
||||||
|
LENDING_PROGRAM_ID,
|
||||||
|
models,
|
||||||
|
notify,
|
||||||
|
ParsedAccount,
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
TokenAccount,
|
||||||
|
} from '@oyster/common';
|
||||||
|
import { AccountLayout, NATIVE_MINT, Token } from '@solana/spl-token';
|
||||||
|
import {
|
||||||
|
Account,
|
||||||
|
Connection,
|
||||||
|
PublicKey,
|
||||||
|
TransactionInstruction,
|
||||||
|
} from '@solana/web3.js';
|
||||||
|
import {
|
||||||
|
Obligation,
|
||||||
|
refreshReserveInstruction,
|
||||||
|
repayObligationLiquidityInstruction,
|
||||||
|
Reserve,
|
||||||
|
} from '../models';
|
||||||
|
|
||||||
|
const { approve } = models;
|
||||||
|
const { sendTransaction } = contexts.Connection;
|
||||||
|
|
||||||
|
// @FIXME
|
||||||
|
export const repayObligationLiquidity = async (
|
||||||
|
connection: Connection,
|
||||||
|
wallet: any,
|
||||||
|
liquidityAmount: number,
|
||||||
|
source: TokenAccount,
|
||||||
|
repayReserve: ParsedAccount<Reserve>,
|
||||||
|
obligation: ParsedAccount<Obligation>,
|
||||||
|
) => {
|
||||||
|
notify({
|
||||||
|
message: 'Repaying funds...',
|
||||||
|
description: 'Please review transactions to approve.',
|
||||||
|
type: 'warn',
|
||||||
|
});
|
||||||
|
|
||||||
|
// user from account
|
||||||
|
const signers: Account[] = [];
|
||||||
|
const instructions: TransactionInstruction[] = [];
|
||||||
|
const cleanupInstructions: TransactionInstruction[] = [];
|
||||||
|
|
||||||
|
const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
|
||||||
|
AccountLayout.span,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [lendingMarketAuthority] = await PublicKey.findProgramAddress(
|
||||||
|
[repayReserve.info.lendingMarket.toBuffer()],
|
||||||
|
LENDING_PROGRAM_ID,
|
||||||
|
);
|
||||||
|
|
||||||
|
let sourceLiquidity = source.pubkey;
|
||||||
|
if (
|
||||||
|
wallet.publicKey.equals(sourceLiquidity) &&
|
||||||
|
repayReserve.info.liquidity.mint.equals(NATIVE_MINT)
|
||||||
|
) {
|
||||||
|
sourceLiquidity = createTokenAccount(
|
||||||
|
instructions,
|
||||||
|
wallet.publicKey,
|
||||||
|
accountRentExempt + liquidityAmount,
|
||||||
|
NATIVE_MINT,
|
||||||
|
wallet.publicKey,
|
||||||
|
signers,
|
||||||
|
);
|
||||||
|
cleanupInstructions.push(
|
||||||
|
Token.createCloseAccountInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
sourceLiquidity,
|
||||||
|
wallet.publicKey,
|
||||||
|
wallet.publicKey,
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create approval for transfer transactions
|
||||||
|
const transferAuthority = approve(
|
||||||
|
instructions,
|
||||||
|
cleanupInstructions,
|
||||||
|
sourceLiquidity,
|
||||||
|
wallet.publicKey,
|
||||||
|
liquidityAmount,
|
||||||
|
);
|
||||||
|
|
||||||
|
signers.push(transferAuthority);
|
||||||
|
|
||||||
|
instructions.push(
|
||||||
|
refreshReserveInstruction(
|
||||||
|
repayReserve.pubkey,
|
||||||
|
repayReserve.info.liquidity.aggregatorOption
|
||||||
|
? repayReserve.info.liquidity.aggregator
|
||||||
|
: undefined,
|
||||||
|
),
|
||||||
|
repayObligationLiquidityInstruction(
|
||||||
|
liquidityAmount,
|
||||||
|
sourceLiquidity,
|
||||||
|
repayReserve.info.liquidity.mint,
|
||||||
|
repayReserve.pubkey,
|
||||||
|
obligation.pubkey,
|
||||||
|
repayReserve.info.lendingMarket,
|
||||||
|
lendingMarketAuthority,
|
||||||
|
transferAuthority.publicKey,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
let { txid } = await sendTransaction(
|
||||||
|
connection,
|
||||||
|
wallet,
|
||||||
|
instructions.concat(cleanupInstructions),
|
||||||
|
signers,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
notify({
|
||||||
|
message: 'Funds repaid.',
|
||||||
|
type: 'success',
|
||||||
|
description: `Transaction - ${txid}`,
|
||||||
|
});
|
||||||
|
};
|
Loading…
Reference in New Issue