From cee89c00d32982bdb963dfe7c39b4bca7b526d41 Mon Sep 17 00:00:00 2001 From: jordansexton Date: Thu, 6 May 2021 12:41:07 -0500 Subject: [PATCH] refactoring --- .../src/actions/borrowObligationLiquidity.tsx | 4 +- .../actions/depositObligationCollateral.tsx | 100 ++++++++++++++ .../src/actions/depositReserveLiquidity.tsx | 105 +++++---------- .../lending/src/actions/initObligation.tsx | 59 +++++++++ packages/lending/src/actions/initReserve.tsx | 122 +++++++++++++++++ .../src/actions/liquidateObligation.tsx | 4 +- .../src/actions/redeemReserveCollateral.tsx | 4 +- .../src/actions/repayObligationLiquidity.tsx | 4 +- .../actions/withdrawObligationCollateral.tsx | 106 +++++++++++++++ .../src/components/BorrowInput/index.tsx | 4 +- packages/lending/src/contexts/lending.tsx | 4 +- .../src/models/instructions/initReserve.ts | 25 +++- .../lending/src/models/state/lastUpdate.ts | 7 + .../lending/src/models/state/lendingMarket.ts | 14 +- .../lending/src/models/state/obligation.ts | 63 +++++---- packages/lending/src/models/state/reserve.ts | 125 +++++++++--------- 16 files changed, 553 insertions(+), 197 deletions(-) create mode 100644 packages/lending/src/actions/depositObligationCollateral.tsx create mode 100644 packages/lending/src/actions/initObligation.tsx create mode 100644 packages/lending/src/actions/initReserve.tsx create mode 100644 packages/lending/src/actions/withdrawObligationCollateral.tsx diff --git a/packages/lending/src/actions/borrowObligationLiquidity.tsx b/packages/lending/src/actions/borrowObligationLiquidity.tsx index dc754fb..9ea7e22 100644 --- a/packages/lending/src/actions/borrowObligationLiquidity.tsx +++ b/packages/lending/src/actions/borrowObligationLiquidity.tsx @@ -34,7 +34,7 @@ export const borrowObligationLiquidity = async ( obligation: ParsedAccount, ) => { notify({ - message: 'Borrowing funds...', + message: 'Borrowing liquidity...', description: 'Please review transactions to approve.', type: 'warn', }); @@ -146,7 +146,7 @@ export const borrowObligationLiquidity = async ( ); notify({ - message: 'Funds borrowed.', + message: 'Liquidity borrowed.', type: 'success', description: `Transaction - ${txid}`, }); diff --git a/packages/lending/src/actions/depositObligationCollateral.tsx b/packages/lending/src/actions/depositObligationCollateral.tsx new file mode 100644 index 0000000..d5fa3b1 --- /dev/null +++ b/packages/lending/src/actions/depositObligationCollateral.tsx @@ -0,0 +1,100 @@ +import { + contexts, + LENDING_PROGRAM_ID, + models, + notify, + TokenAccount, +} from '@oyster/common'; +import { + Account, + Connection, + PublicKey, + TransactionInstruction, +} from '@solana/web3.js'; +import { + depositObligationCollateralInstruction, + refreshReserveInstruction, + Reserve, +} from '../models'; + +const { approve } = models; +const { sendTransaction } = contexts.Connection; + +// @FIXME +export const depositObligationCollateral = async ( + connection: Connection, + wallet: any, + collateralAmount: number, + source: TokenAccount, + reserve: Reserve, + reserveAddress: PublicKey, + obligationAddress: PublicKey +) => { + notify({ + message: 'Depositing collateral...', + description: 'Please review transactions to approve.', + type: 'warn', + }); + + // user from account + const signers: Account[] = []; + const instructions: TransactionInstruction[] = []; + const cleanupInstructions: TransactionInstruction[] = []; + + const [lendingMarketAuthority] = await PublicKey.findProgramAddress( + [reserve.lendingMarket.toBuffer()], + LENDING_PROGRAM_ID, + ); + + const sourceCollateral = source.pubkey; + + // create approval for transfer transactions + const transferAuthority = approve( + instructions, + cleanupInstructions, + sourceCollateral, + wallet.publicKey, + collateralAmount, + ); + + signers.push(transferAuthority); + + instructions.push( + refreshReserveInstruction( + reserveAddress, + reserve.liquidity.oracleOption + ? reserve.liquidity.oraclePubkey + : undefined, + ), + depositObligationCollateralInstruction( + collateralAmount, + sourceCollateral, + reserve.collateral.mintPubkey, + reserveAddress, + obligationAddress, + reserve.lendingMarket, + lendingMarketAuthority, + // @FIXME: wallet must sign + wallet.publicKey, + transferAuthority.publicKey, + ), + ); + + try { + let { txid } = await sendTransaction( + connection, + wallet, + instructions.concat(cleanupInstructions), + signers, + true, + ); + + notify({ + message: 'Collateral deposited.', + type: 'success', + description: `Transaction - ${txid}`, + }); + } catch { + // TODO: + } +}; diff --git a/packages/lending/src/actions/depositReserveLiquidity.tsx b/packages/lending/src/actions/depositReserveLiquidity.tsx index 3d0eda8..ca7c95c 100644 --- a/packages/lending/src/actions/depositReserveLiquidity.tsx +++ b/packages/lending/src/actions/depositReserveLiquidity.tsx @@ -15,20 +15,14 @@ import { } from '@solana/web3.js'; import { depositReserveLiquidityInstruction, - initReserveInstruction, refreshReserveInstruction, Reserve, } from '../models'; const { sendTransaction } = contexts.Connection; -const { - createUninitializedAccount, - ensureSplAccount, - findOrCreateAccountByMint, -} = actions; +const { ensureSplAccount, findOrCreateAccountByMint } = actions; const { approve } = models; -// @FIXME: split up into deposit, and init which requires lending market owner export const depositReserveLiquidity = async ( connection: Connection, wallet: any, @@ -38,13 +32,11 @@ export const depositReserveLiquidity = async ( reserveAddress: PublicKey, ) => { notify({ - message: 'Depositing funds...', + message: 'Depositing liquidity...', description: 'Please review transactions to approve.', type: 'warn', }); - const isInitalized = true; // TODO: finish reserve init - // user from account const signers: Account[] = []; const instructions: TransactionInstruction[] = []; @@ -79,70 +71,35 @@ export const depositReserveLiquidity = async ( signers.push(transferAuthority); - let destinationCollateralAccount: PublicKey = isInitalized - ? await findOrCreateAccountByMint( - wallet.publicKey, - wallet.publicKey, - instructions, - cleanupInstructions, - accountRentExempt, - reserve.collateral.mintPubkey, - signers, - ) - : createUninitializedAccount( - instructions, - wallet.publicKey, - accountRentExempt, - signers, - ); + let destinationCollateralAccount: PublicKey = await findOrCreateAccountByMint( + wallet.publicKey, + wallet.publicKey, + instructions, + cleanupInstructions, + accountRentExempt, + reserve.collateral.mintPubkey, + signers, + ); - if (isInitalized) { - instructions.push( - refreshReserveInstruction( - reserveAddress, - reserve.liquidity.oracleOption - ? reserve.liquidity.oraclePubkey - : undefined, - ), - depositReserveLiquidityInstruction( - liquidityAmount, - sourceLiquidityAccount, - destinationCollateralAccount, - reserveAddress, - reserve.liquidity.supplyPubkey, - reserve.collateral.mintPubkey, - reserve.lendingMarket, - lendingMarketAuthority, - transferAuthority.publicKey, - ), - ); - } else { - // TODO: finish reserve init - // @FIXME: reserve config - const MAX_UTILIZATION_RATE = 80; - instructions.push( - initReserveInstruction( - liquidityAmount, - MAX_UTILIZATION_RATE, - sourceLiquidityAccount, - destinationCollateralAccount, - reserveAddress, - reserve.liquidity.mintPubkey, - reserve.liquidity.supplyPubkey, - reserve.liquidity.feeReceiver, - reserve.collateral.mintPubkey, - reserve.collateral.supplyPubkey, - reserve.lendingMarket, - lendingMarketAuthority, - // @FIXME: lending market owner - lendingMarketOwner, - transferAuthority.publicKey, - reserve.liquidity.oracleOption - ? reserve.liquidity.oraclePubkey - : undefined, - ), - ); - } + instructions.push( + refreshReserveInstruction( + reserveAddress, + reserve.liquidity.oracleOption + ? reserve.liquidity.oraclePubkey + : undefined, + ), + depositReserveLiquidityInstruction( + liquidityAmount, + sourceLiquidityAccount, + destinationCollateralAccount, + reserveAddress, + reserve.liquidity.supplyPubkey, + reserve.collateral.mintPubkey, + reserve.lendingMarket, + lendingMarketAuthority, + transferAuthority.publicKey, + ), + ); try { let { txid } = await sendTransaction( @@ -154,7 +111,7 @@ export const depositReserveLiquidity = async ( ); notify({ - message: 'Funds deposited.', + message: 'Liquidity deposited.', type: 'success', description: `Transaction - ${txid}`, }); diff --git a/packages/lending/src/actions/initObligation.tsx b/packages/lending/src/actions/initObligation.tsx new file mode 100644 index 0000000..7ed2d2c --- /dev/null +++ b/packages/lending/src/actions/initObligation.tsx @@ -0,0 +1,59 @@ +import { contexts, notify } from '@oyster/common'; +import { + Account, + Connection, + PublicKey, + TransactionInstruction, +} from '@solana/web3.js'; +import { initObligationInstruction, Obligation } from '../models'; + +const { sendTransaction } = contexts.Connection; + +export const initObligation = async ( + connection: Connection, + wallet: any, + obligation: Obligation, + obligationAddress: PublicKey +) => { + notify({ + message: 'Initializing obligation...', + description: 'Please review transactions to approve.', + type: 'warn', + }); + + // user from account + const signers: Account[] = []; + const instructions: TransactionInstruction[] = []; + const cleanupInstructions: TransactionInstruction[] = []; + + // @FIXME: obligation owner must sign + signers.push(wallet.info.account); + + instructions.push( + initObligationInstruction( + obligationAddress, + obligation.lendingMarket, + // @FIXME: need to sign with wallet + wallet.publicKey + ), + ); + + try { + let { txid } = await sendTransaction( + connection, + wallet, + instructions.concat(cleanupInstructions), + signers, + true, + ); + + notify({ + message: 'Obligation initialized.', + type: 'success', + description: `Transaction - ${txid}`, + }); + } catch { + // TODO: + throw new Error(); + } +}; diff --git a/packages/lending/src/actions/initReserve.tsx b/packages/lending/src/actions/initReserve.tsx new file mode 100644 index 0000000..1540b61 --- /dev/null +++ b/packages/lending/src/actions/initReserve.tsx @@ -0,0 +1,122 @@ +import { + actions, + contexts, + LENDING_PROGRAM_ID, + models, + notify, + TokenAccount, +} from '@oyster/common'; +import { AccountLayout } from '@solana/spl-token'; +import { + Account, + Connection, + PublicKey, + TransactionInstruction, +} from '@solana/web3.js'; +import { initReserveInstruction, Reserve } from '../models'; + +const { sendTransaction } = contexts.Connection; +const { + createUninitializedAccount, + ensureSplAccount, +} = actions; +const { approve } = models; + +export const initReserve = async ( + connection: Connection, + // @FIXME: wallet must be lending market owner + wallet: any, + liquidityAmount: number, + source: TokenAccount, + reserve: Reserve, + reserveAddress: PublicKey, +) => { + notify({ + message: 'Initializing reserve...', + 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( + [reserve.lendingMarket.toBuffer()], // which account should be authority + LENDING_PROGRAM_ID, + ); + + const sourceLiquidityAccount = ensureSplAccount( + instructions, + cleanupInstructions, + source, + wallet.publicKey, + liquidityAmount + accountRentExempt, + signers, + ); + + // create approval for transfer transactions + const transferAuthority = approve( + instructions, + cleanupInstructions, + sourceLiquidityAccount, + wallet.publicKey, + liquidityAmount, + ); + + signers.push(transferAuthority); + + let destinationCollateralAccount: PublicKey = createUninitializedAccount( + instructions, + wallet.publicKey, + accountRentExempt, + signers, + ); + + instructions.push( + initReserveInstruction( + liquidityAmount, + reserve.config, + sourceLiquidityAccount, + destinationCollateralAccount, + reserveAddress, + reserve.liquidity.mintPubkey, + reserve.liquidity.supplyPubkey, + reserve.liquidity.feeReceiver, + reserve.collateral.mintPubkey, + reserve.collateral.supplyPubkey, + reserve.lendingMarket, + lendingMarketAuthority, + // @FIXME: need to sign with wallet as lending market owner + wallet.publicKey, + transferAuthority.publicKey, + reserve.liquidity.oracleOption + ? reserve.liquidity.oraclePubkey + : undefined, + ), + ); + + try { + let { txid } = await sendTransaction( + connection, + wallet, + instructions.concat(cleanupInstructions), + signers, + true, + ); + + notify({ + message: 'Reserve initialized.', + type: 'success', + description: `Transaction - ${txid}`, + }); + } catch { + // TODO: + throw new Error(); + } +}; diff --git a/packages/lending/src/actions/liquidateObligation.tsx b/packages/lending/src/actions/liquidateObligation.tsx index f29a39e..4d607d8 100644 --- a/packages/lending/src/actions/liquidateObligation.tsx +++ b/packages/lending/src/actions/liquidateObligation.tsx @@ -39,7 +39,7 @@ export const liquidateObligation = async ( obligation: ParsedAccount, ) => { notify({ - message: 'Repaying funds...', + message: 'Liquidating obligation...', description: 'Please review transactions to approve.', type: 'warn', }); @@ -146,7 +146,7 @@ export const liquidateObligation = async ( ); notify({ - message: 'Funds liquidated.', + message: 'Obligation liquidated.', type: 'success', description: `Transaction - ${txid}`, }); diff --git a/packages/lending/src/actions/redeemReserveCollateral.tsx b/packages/lending/src/actions/redeemReserveCollateral.tsx index 9c36063..7ad6468 100644 --- a/packages/lending/src/actions/redeemReserveCollateral.tsx +++ b/packages/lending/src/actions/redeemReserveCollateral.tsx @@ -32,7 +32,7 @@ export const redeemReserveCollateral = async ( reserveAddress: PublicKey, ) => { notify({ - message: 'Withdrawing funds...', + message: 'Redeeming collateral...', description: 'Please review transactions to approve.', type: 'warn', }); @@ -105,7 +105,7 @@ export const redeemReserveCollateral = async ( ); notify({ - message: 'Funds deposited.', + message: 'Collateral redeemed.', type: 'success', description: `Transaction - ${txid}`, }); diff --git a/packages/lending/src/actions/repayObligationLiquidity.tsx b/packages/lending/src/actions/repayObligationLiquidity.tsx index ff075d4..7ba9ec6 100644 --- a/packages/lending/src/actions/repayObligationLiquidity.tsx +++ b/packages/lending/src/actions/repayObligationLiquidity.tsx @@ -35,7 +35,7 @@ export const repayObligationLiquidity = async ( obligation: ParsedAccount, ) => { notify({ - message: 'Repaying funds...', + message: 'Repaying liquidity...', description: 'Please review transactions to approve.', type: 'warn', }); @@ -117,7 +117,7 @@ export const repayObligationLiquidity = async ( ); notify({ - message: 'Funds repaid.', + message: 'Liquidity repaid.', type: 'success', description: `Transaction - ${txid}`, }); diff --git a/packages/lending/src/actions/withdrawObligationCollateral.tsx b/packages/lending/src/actions/withdrawObligationCollateral.tsx new file mode 100644 index 0000000..555e99f --- /dev/null +++ b/packages/lending/src/actions/withdrawObligationCollateral.tsx @@ -0,0 +1,106 @@ +import { + contexts, + findOrCreateAccountByMint, + LENDING_PROGRAM_ID, + models, + notify, + TokenAccount, +} from '@oyster/common'; +import { AccountLayout } from '@solana/spl-token'; +import { + Account, + Connection, + PublicKey, + TransactionInstruction, +} from '@solana/web3.js'; +import { + withdrawObligationCollateralInstruction, + refreshReserveInstruction, + Reserve, +} from '../models'; + +const { approve } = models; +const { sendTransaction } = contexts.Connection; + +// @FIXME +export const withdrawObligationCollateral = async ( + connection: Connection, + wallet: any, + collateralAmount: number, + source: TokenAccount, + reserve: Reserve, + reserveAddress: PublicKey, + obligationAddress: PublicKey, +) => { + notify({ + message: 'Withdrawing collateral...', + 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( + [reserve.lendingMarket.toBuffer()], + LENDING_PROGRAM_ID, + ); + + // @FIXME: wallet must sign as obligation owner + signers.push(wallet.info.account); + + // get destination account + const destinationCollateral = await findOrCreateAccountByMint( + wallet.publicKey, + wallet.publicKey, + instructions, + cleanupInstructions, + accountRentExempt, + reserve.collateral.mintPubkey, + signers, + ); + + instructions.push( + refreshReserveInstruction( + reserveAddress, + reserve.liquidity.oracleOption + ? reserve.liquidity.oraclePubkey + : undefined, + ), + withdrawObligationCollateralInstruction( + collateralAmount, + reserve.collateral.supplyPubkey, + destinationCollateral, + reserveAddress, + obligationAddress, + reserve.lendingMarket, + lendingMarketAuthority, + // @FIXME: wallet must sign + wallet.publicKey + ), + ); + + try { + let { txid } = await sendTransaction( + connection, + wallet, + instructions.concat(cleanupInstructions), + signers, + true, + ); + + notify({ + message: 'Collateral withdrawn.', + type: 'success', + description: `Transaction - ${txid}`, + }); + } catch { + // TODO: + } +}; diff --git a/packages/lending/src/components/BorrowInput/index.tsx b/packages/lending/src/components/BorrowInput/index.tsx index 83fae96..701f50c 100644 --- a/packages/lending/src/components/BorrowInput/index.tsx +++ b/packages/lending/src/components/BorrowInput/index.tsx @@ -145,9 +145,7 @@ export const BorrowInput = (props: { parseFloat(value), borrowReserve, // TODO: select existing obligations by collateral reserve - userObligationsByReserve.length > 0 - ? userObligationsByReserve[0].obligation.account - : undefined, + userObligationsByReserve[0].obligation.account ); setValue(''); diff --git a/packages/lending/src/contexts/lending.tsx b/packages/lending/src/contexts/lending.tsx index 3bcbff2..d9f7b47 100644 --- a/packages/lending/src/contexts/lending.tsx +++ b/packages/lending/src/contexts/lending.tsx @@ -45,13 +45,11 @@ export const useLending = () => { const processAccount = useCallback( (item: { pubkey: PublicKey; account: AccountInfo }) => { if (isReserve(item.account)) { - const reserve = cache.add( + return cache.add( item.pubkey.toBase58(), item.account, ReserveParser, ); - - return reserve; } else if (isLendingMarket(item.account)) { return cache.add( item.pubkey.toBase58(), diff --git a/packages/lending/src/models/instructions/initReserve.ts b/packages/lending/src/models/instructions/initReserve.ts index 0eb8293..6e19a55 100644 --- a/packages/lending/src/models/instructions/initReserve.ts +++ b/packages/lending/src/models/instructions/initReserve.ts @@ -8,6 +8,7 @@ import { import BN from 'bn.js'; import * as BufferLayout from 'buffer-layout'; import * as Layout from '../../utils/layout'; +import { ReserveConfig } from '../state'; import { LendingInstruction } from './instruction'; /// Initializes a new lending market reserve. @@ -42,8 +43,7 @@ import { LendingInstruction } from './instruction'; // }, export const initReserveInstruction = ( liquidityAmount: number | BN, - // @FIXME: reserve config - maxUtilizationRate: number, + config: ReserveConfig, sourceLiquidity: PublicKey, destinationCollateral: PublicKey, reserve: PublicKey, @@ -61,8 +61,15 @@ export const initReserveInstruction = ( const dataLayout = BufferLayout.struct([ BufferLayout.u8('instruction'), Layout.uint64('liquidityAmount'), - // @FIXME: reserve config - BufferLayout.u8('maxUtilizationRate'), + BufferLayout.u8('optimalUtilizationRate'), + BufferLayout.u8('loanToValueRatio'), + BufferLayout.u8('liquidationBonus'), + BufferLayout.u8('liquidationThreshold'), + BufferLayout.u8('minBorrowRate'), + BufferLayout.u8('optimalBorrowRate'), + BufferLayout.u8('maxBorrowRate'), + Layout.uint64('borrowFeeWad'), + BufferLayout.u8('hostFeePercentage'), ]); const data = Buffer.alloc(dataLayout.span); @@ -70,7 +77,15 @@ export const initReserveInstruction = ( { instruction: LendingInstruction.InitReserve, liquidityAmount: new BN(liquidityAmount), - maxUtilizationRate, + optimalUtilizationRate: config.optimalUtilizationRate, + loanToValueRatio: config.loanToValueRatio, + liquidationBonus: config.liquidationBonus, + liquidationThreshold: config.liquidationThreshold, + minBorrowRate: config.minBorrowRate, + optimalBorrowRate: config.optimalBorrowRate, + maxBorrowRate: config.maxBorrowRate, + borrowFeeWad: config.fees.borrowFeeWad, + hostFeePercentage: config.fees.hostFeePercentage, }, data, ); diff --git a/packages/lending/src/models/state/lastUpdate.ts b/packages/lending/src/models/state/lastUpdate.ts index b34660a..27e0eea 100644 --- a/packages/lending/src/models/state/lastUpdate.ts +++ b/packages/lending/src/models/state/lastUpdate.ts @@ -1,4 +1,11 @@ import BN from 'bn.js'; +import * as BufferLayout from 'buffer-layout'; +import * as Layout from '../../utils/layout'; + +export const LastUpdateLayout: typeof BufferLayout.Structure = BufferLayout.struct( + [Layout.uint64('slot'), BufferLayout.u8('stale')], + 'lastUpdate' +); export interface LastUpdate { slot: BN; diff --git a/packages/lending/src/models/state/lendingMarket.ts b/packages/lending/src/models/state/lendingMarket.ts index bdf6703..a633c73 100644 --- a/packages/lending/src/models/state/lendingMarket.ts +++ b/packages/lending/src/models/state/lendingMarket.ts @@ -2,6 +2,13 @@ import { AccountInfo, PublicKey } from '@solana/web3.js'; import * as BufferLayout from 'buffer-layout'; import * as Layout from '../../utils/layout'; +export interface LendingMarket { + version: number; + isInitialized: boolean; + quoteTokenMint: PublicKey; + tokenProgramId: PublicKey; +} + export const LendingMarketLayout: typeof BufferLayout.Structure = BufferLayout.struct( [ BufferLayout.u8('version'), @@ -14,13 +21,6 @@ export const LendingMarketLayout: typeof BufferLayout.Structure = BufferLayout.s ], ); -export interface LendingMarket { - version: number; - isInitialized: boolean; - quoteTokenMint: PublicKey; - tokenProgramId: PublicKey; -} - export const isLendingMarket = (info: AccountInfo) => { return info.data.length === LendingMarketLayout.span; }; diff --git a/packages/lending/src/models/state/obligation.ts b/packages/lending/src/models/state/obligation.ts index 58efd06..8922d30 100644 --- a/packages/lending/src/models/state/obligation.ts +++ b/packages/lending/src/models/state/obligation.ts @@ -2,16 +2,41 @@ import { AccountInfo, PublicKey } from '@solana/web3.js'; import BN from 'bn.js'; import * as BufferLayout from 'buffer-layout'; import * as Layout from '../../utils/layout'; -import { LastUpdate } from './lastUpdate'; +import { LastUpdate, LastUpdateLayout } from './lastUpdate'; + +export interface Obligation { + version: number; + lastUpdate: LastUpdate; + lendingMarket: PublicKey; + owner: PublicKey; + // @FIXME: check usages + deposits: ObligationCollateral[]; + // @FIXME: check usages + borrows: ObligationLiquidity[]; + depositedValue: BN; // decimals + borrowedValue: BN; // decimals + allowedBorrowValue: BN; // decimals + unhealthyBorrowValue: BN; // decimals +} + +export interface ObligationCollateral { + depositReserve: PublicKey; + depositedAmount: BN; + marketValue: BN; // decimals +} + +export interface ObligationLiquidity { + borrowReserve: PublicKey; + cumulativeBorrowRateWads: BN; // decimals + borrowedAmountWads: BN; // decimals + marketValue: BN; // decimals +} export const ObligationLayout: typeof BufferLayout.Structure = BufferLayout.struct( [ BufferLayout.u8('version'), - BufferLayout.struct( - [Layout.uint64('slot'), BufferLayout.u8('stale')], - 'lastUpdate', - ), + LastUpdateLayout, Layout.publicKey('lendingMarket'), Layout.publicKey('owner'), @@ -61,34 +86,6 @@ export interface ProtoObligation { dataFlat: Buffer; } -export interface Obligation { - version: number; - lastUpdate: LastUpdate; - lendingMarket: PublicKey; - owner: PublicKey; - // @FIXME: check usages - deposits: ObligationCollateral[]; - // @FIXME: check usages - borrows: ObligationLiquidity[]; - depositedValue: BN; // decimals - borrowedValue: BN; // decimals - allowedBorrowValue: BN; // decimals - unhealthyBorrowValue: BN; // decimals -} - -export interface ObligationCollateral { - depositReserve: PublicKey; - depositedAmount: BN; - marketValue: BN; // decimals -} - -export interface ObligationLiquidity { - borrowReserve: PublicKey; - cumulativeBorrowRateWads: BN; // decimals - borrowedAmountWads: BN; // decimals - marketValue: BN; // decimals -} - export const ObligationParser = ( pubkey: PublicKey, info: AccountInfo, diff --git a/packages/lending/src/models/state/reserve.ts b/packages/lending/src/models/state/reserve.ts index ee89bbb..17484b5 100644 --- a/packages/lending/src/models/state/reserve.ts +++ b/packages/lending/src/models/state/reserve.ts @@ -3,70 +3,7 @@ import { AccountInfo, PublicKey } from '@solana/web3.js'; import BN from 'bn.js'; import * as BufferLayout from 'buffer-layout'; import * as Layout from '../../utils/layout'; -import { LastUpdate } from './lastUpdate'; - -export const ReserveLayout: typeof BufferLayout.Structure = BufferLayout.struct( - [ - BufferLayout.u8('version'), - - BufferLayout.struct( - [Layout.uint64('slot'), BufferLayout.u8('stale')], - 'lastUpdate', - ), - - Layout.publicKey('lendingMarket'), - - BufferLayout.struct( - [ - Layout.publicKey('mintPubkey'), - BufferLayout.u8('mintDecimals'), - Layout.publicKey('supplyPubkey'), - Layout.publicKey('feeReceiver'), - // @FIXME: oracle option - // TODO: replace u32 option with generic equivalent - BufferLayout.u32('oracleOption'), - Layout.publicKey('oracle'), - Layout.uint64('availableAmount'), - Layout.uint128('borrowedAmountWads'), - Layout.uint128('cumulativeBorrowRateWads'), - Layout.uint64('marketPrice'), - ], - 'liquidity', - ), - - BufferLayout.struct( - [ - Layout.publicKey('mintPubkey'), - Layout.uint64('mintTotalSupply'), - Layout.publicKey('supplyPubkey'), - ], - 'collateral', - ), - - BufferLayout.struct( - [ - BufferLayout.u8('optimalUtilizationRate'), - BufferLayout.u8('loanToValueRatio'), - BufferLayout.u8('liquidationBonus'), - BufferLayout.u8('liquidationThreshold'), - BufferLayout.u8('minBorrowRate'), - BufferLayout.u8('optimalBorrowRate'), - BufferLayout.u8('maxBorrowRate'), - BufferLayout.struct( - [Layout.uint64('borrowFeeWad'), BufferLayout.u8('hostFeePercentage')], - 'fees', - ), - ], - 'config', - ), - - BufferLayout.blob(256, 'padding'), - ], -); - -export const isReserve = (info: AccountInfo) => { - return info.data.length === ReserveLayout.span; -}; +import { LastUpdate, LastUpdateLayout } from './lastUpdate'; export interface Reserve { version: number; @@ -111,6 +48,66 @@ export interface ReserveConfig { }; } +export const ReserveLayout: typeof BufferLayout.Structure = BufferLayout.struct( + [ + BufferLayout.u8('version'), + + LastUpdateLayout, + + Layout.publicKey('lendingMarket'), + + BufferLayout.struct( + [ + Layout.publicKey('mintPubkey'), + BufferLayout.u8('mintDecimals'), + Layout.publicKey('supplyPubkey'), + Layout.publicKey('feeReceiver'), + // @FIXME: oracle option + // TODO: replace u32 option with generic equivalent + BufferLayout.u32('oracleOption'), + Layout.publicKey('oracle'), + Layout.uint64('availableAmount'), + Layout.uint128('borrowedAmountWads'), + Layout.uint128('cumulativeBorrowRateWads'), + Layout.uint64('marketPrice'), + ], + 'liquidity', + ), + + BufferLayout.struct( + [ + Layout.publicKey('mintPubkey'), + Layout.uint64('mintTotalSupply'), + Layout.publicKey('supplyPubkey'), + ], + 'collateral' + ), + + BufferLayout.struct( + [ + BufferLayout.u8('optimalUtilizationRate'), + BufferLayout.u8('loanToValueRatio'), + BufferLayout.u8('liquidationBonus'), + BufferLayout.u8('liquidationThreshold'), + BufferLayout.u8('minBorrowRate'), + BufferLayout.u8('optimalBorrowRate'), + BufferLayout.u8('maxBorrowRate'), + BufferLayout.struct( + [Layout.uint64('borrowFeeWad'), BufferLayout.u8('hostFeePercentage')], + 'fees', + ), + ], + 'config' + ), + + BufferLayout.blob(256, 'padding'), + ], +); + +export const isReserve = (info: AccountInfo) => { + return info.data.length === ReserveLayout.span; +}; + export const ReserveParser = (pubkey: PublicKey, info: AccountInfo) => { const buffer = Buffer.from(info.data); const reserve = ReserveLayout.decode(buffer) as Reserve;