feat: borrow instruction

This commit is contained in:
bartosz-lipinski 2020-11-20 14:14:06 -06:00
parent 3554e8ce86
commit 42af776ee8
14 changed files with 211 additions and 118 deletions

View File

@ -44,6 +44,31 @@ export function ensureSplAccount(
return account; return account;
} }
export const DEFAULT_TEMP_MEM_SPACE = 65528;
export function createTempMemoryAccount(
instructions: TransactionInstruction[],
payer: PublicKey,
signers: Account[],
space = DEFAULT_TEMP_MEM_SPACE) {
const account = new Account();
instructions.push(
SystemProgram.createAccount({
fromPubkey: payer,
newAccountPubkey: account.publicKey,
// 0 will evict/clost account since it cannot pay rent
lamports: 0,
space: space,
programId: TOKEN_PROGRAM_ID,
})
);
signers.push(account);
return account.publicKey;
}
export function createUninitializedAccount( export function createUninitializedAccount(
instructions: TransactionInstruction[], instructions: TransactionInstruction[],
payer: PublicKey, payer: PublicKey,

View File

@ -9,10 +9,11 @@ import { notify } from "../utils/notifications";
import { LendingReserve } from "./../models/lending/reserve"; import { LendingReserve } from "./../models/lending/reserve";
import { AccountLayout, MintInfo, MintLayout, Token } from "@solana/spl-token"; import { AccountLayout, MintInfo, MintLayout, Token } from "@solana/spl-token";
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids"; import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids";
import { createUninitializedAccount, ensureSplAccount, findOrCreateAccountByMint } from "./account"; import { createTempMemoryAccount, createUninitializedAccount, ensureSplAccount, findOrCreateAccountByMint } from "./account";
import { cache, MintParser, ParsedAccount } from "../contexts/accounts"; import { cache, MintParser, ParsedAccount } from "../contexts/accounts";
import { TokenAccount, LendingObligationLayout, borrowInstruction, LendingMarket } from "../models"; import { TokenAccount, LendingObligationLayout, borrowInstruction, LendingMarket } from "../models";
import { toLamports } from "../utils/utils"; import { toLamports } from "../utils/utils";
import { DexMarketParser } from "../models/dex";
export const borrow = async ( export const borrow = async (
from: TokenAccount, from: TokenAccount,
@ -33,14 +34,68 @@ export const borrow = async (
type: "warn", type: "warn",
}); });
const signers: Account[] = []; let signers: Account[] = [];
const instructions: TransactionInstruction[] = []; let instructions: TransactionInstruction[] = [];
const cleanupInstructions: TransactionInstruction[] = []; let cleanupInstructions: TransactionInstruction[] = [];
const accountRentExempt = await connection.getMinimumBalanceForRentExemption( const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
AccountLayout.span AccountLayout.span
); );
const obligation = createUninitializedAccount(
instructions,
wallet.publicKey,
await connection.getMinimumBalanceForRentExemption(
LendingObligationLayout.span
),
signers,
);
const obligationMint = createUninitializedAccount(
instructions,
wallet.publicKey,
await connection.getMinimumBalanceForRentExemption(
MintLayout.span
),
signers,
);
const obligationTokenOutput = createUninitializedAccount(
instructions,
wallet.publicKey,
accountRentExempt,
signers,
);
let toAccount = await findOrCreateAccountByMint(
wallet.publicKey,
wallet.publicKey,
instructions,
cleanupInstructions,
accountRentExempt,
borrowReserve.liquidityMint,
signers
);
// create all accounts in one transaction
let tx = await sendTransaction(connection, wallet, instructions, [...signers]);
notify({
message: "Obligation accounts created",
description: `Transaction ${tx}`,
type: "success",
});
notify({
message: "Adding Liquidity...",
description: "Please review transactions to approve.",
type: "warn",
});
signers = [];
instructions = [];
cleanupInstructions = [];
const [authority] = await PublicKey.findProgramAddress( const [authority] = await PublicKey.findProgramAddress(
[depositReserve.lendingMarket.toBuffer()], // which account should be authority [depositReserve.lendingMarket.toBuffer()], // which account should be authority
LENDING_PROGRAM_ID LENDING_PROGRAM_ID
@ -70,56 +125,25 @@ export const borrow = async (
) )
); );
let toAccount = await findOrCreateAccountByMint(
wallet.publicKey,
wallet.publicKey,
instructions,
cleanupInstructions,
accountRentExempt,
borrowReserve.liquidityMint,
signers
);
const obligation = createUninitializedAccount(
instructions,
wallet.publicKey,
await connection.getMinimumBalanceForRentExemption(
LendingObligationLayout.span
),
signers,
);
const obligationMint = createUninitializedAccount(
instructions,
wallet.publicKey,
await connection.getMinimumBalanceForRentExemption(
MintLayout.span
),
signers,
);
const obligationTokenOutput = createUninitializedAccount(
instructions,
wallet.publicKey,
accountRentExempt,
signers,
);
const market = cache.get(depositReserve.lendingMarket) as ParsedAccount<LendingMarket>; const market = cache.get(depositReserve.lendingMarket) as ParsedAccount<LendingMarket>;
const dexMarketAddress = borrowReserve.dexMarketOption ? borrowReserve.dexMarket : depositReserve.dexMarket;
const dexMarketAddress = market.info.quoteMint.equals(borrowReserve.liquidityMint) ?
borrowReserve.dexMarket :
depositReserve.dexMarket;
const dexMarket = cache.get(dexMarketAddress); const dexMarket = cache.get(dexMarketAddress);
debugger;
if(!dexMarket) { if(!dexMarket) {
throw new Error(`Dex market doesn't exsists.`) throw new Error(`Dex market doesn't exsists.`)
} }
const dexOrderBookSide = market.info.quoteMint.equals(borrowReserve.liquidityMint) ? const dexOrderBookSide = market.info.quoteMint.equals(depositReserve.liquidityMint) ?
dexMarket.info.bids : dexMarket?.info.bids :
dexMarket.info.asks; dexMarket?.info.asks
const memory = createTempMemoryAccount(
instructions,
wallet.publicKey,
signers,
);
// deposit // deposit
instructions.push( instructions.push(
@ -128,7 +152,7 @@ export const borrow = async (
fromAccount, fromAccount,
toAccount, toAccount,
depositReserveAddress, depositReserveAddress,
depositReserve.liquiditySupply, depositReserve.collateralSupply,
borrowReserveAddress, borrowReserveAddress,
borrowReserve.liquiditySupply, borrowReserve.liquiditySupply,
@ -141,6 +165,8 @@ export const borrow = async (
dexMarketAddress, dexMarketAddress,
dexOrderBookSide, dexOrderBookSide,
memory,
) )
); );
try { try {

View File

@ -1,18 +1,17 @@
import React, { useCallback, useEffect, useMemo, useState } from "react"; import React, { useCallback, useMemo, useState } from "react";
import { useCollateralBalance, useLendingReserve, useLendingReserves, useTokenName, useUserAccounts, useUserBalance } from '../../hooks'; import { useCollateralBalance, useLendingReserves, useTokenName, useUserBalance } from '../../hooks';
import { LendingReserve, LendingReserveParser } from "../../models"; import { LendingReserve, LendingReserveParser } from "../../models";
import { TokenIcon } from "../TokenIcon"; import { TokenIcon } from "../TokenIcon";
import { formatNumber, getTokenName } from "../../utils/utils"; import { getTokenName } from "../../utils/utils";
import { Button, Card, Select } from "antd"; import { Button, Card, Select } from "antd";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import { cache, ParsedAccount, useAccount } from "../../contexts/accounts"; import { cache, ParsedAccount } from "../../contexts/accounts";
import { NumericInput } from "../Input/numeric"; import { NumericInput } from "../Input/numeric";
import { useConnection, useConnectionConfig } from "../../contexts/connection"; import { useConnection, useConnectionConfig } from "../../contexts/connection";
import { useWallet } from "../../contexts/wallet"; import { useWallet } from "../../contexts/wallet";
import { borrow } from '../../actions'; import { borrow } from '../../actions';
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import './style.less'; import './style.less';
import { Token } from "@solana/spl-token";
const { Option } = Select; const { Option } = Select;
@ -55,7 +54,6 @@ const CollateralSelector = (props: {
export const BorrowInput = (props: { className?: string, reserve: LendingReserve, address: PublicKey }) => { export const BorrowInput = (props: { className?: string, reserve: LendingReserve, address: PublicKey }) => {
const connection = useConnection(); const connection = useConnection();
const { wallet } = useWallet(); const { wallet } = useWallet();
const { id } = useParams<{ id: string }>();
const [value, setValue] = useState(''); const [value, setValue] = useState('');
const borrowReserve = props.reserve; const borrowReserve = props.reserve;
@ -71,26 +69,18 @@ export const BorrowInput = (props: { className?: string, reserve: LendingReserve
return cache.get(id) as ParsedAccount<LendingReserve>; return cache.get(id) as ParsedAccount<LendingReserve>;
}, [collateralReserveMint]) }, [collateralReserveMint])
const collateral = useCollateralBalance(collateralReserve?.info);
const name = useTokenName(borrowReserve?.liquidityMint); const name = useTokenName(borrowReserve?.liquidityMint);
const { const {
balance: tokenBalance,
accounts: fromAccounts accounts: fromAccounts
} = useUserBalance(collateralReserve?.info.collateralMint); } = useUserBalance(collateralReserve?.info.collateralMint);
// const collateralBalance = useUserBalance(reserve?.collateralMint); // const collateralBalance = useUserBalance(reserve?.collateralMint);
if(collateral) {
debugger;
}
const onBorrow = useCallback(() => { const onBorrow = useCallback(() => {
if (!collateralReserve) { if (!collateralReserve) {
return; return;
} }
debugger;
borrow( borrow(
fromAccounts[0], fromAccounts[0],
parseFloat(value), parseFloat(value),

View File

@ -42,6 +42,7 @@ export const DepositInput = (props: { className?: string, reserve: LendingReserv
console.log(`liquidityMint: ${reserve.liquidityMint.toBase58()}`); console.log(`liquidityMint: ${reserve.liquidityMint.toBase58()}`);
console.log(`collateralSupply: ${reserve.collateralSupply.toBase58()}`); console.log(`collateralSupply: ${reserve.collateralSupply.toBase58()}`);
console.log(`collateralMint: ${reserve.collateralMint.toBase58()}`); console.log(`collateralMint: ${reserve.collateralMint.toBase58()}`);
console.log(`dexMarket: ${reserve.dexMarket.toBase58()}`);
})(); })();
}, [reserve]) }, [reserve])

View File

@ -24,7 +24,7 @@ export interface ParsedAccountBase {
export type AccountParser = ( export type AccountParser = (
pubkey: PublicKey, pubkey: PublicKey,
data: AccountInfo<Buffer> data: AccountInfo<Buffer>
) => ParsedAccountBase; ) => ParsedAccountBase | undefined;
export interface ParsedAccount<T> extends ParsedAccountBase { export interface ParsedAccount<T> extends ParsedAccountBase {
info: T; info: T;
@ -47,7 +47,7 @@ export const MintParser = (pubKey: PublicKey, info: AccountInfo<Buffer>) => {
}; };
export const TokenAccountParser = (pubKey: PublicKey, info: AccountInfo<Buffer>) => { export const TokenAccountParser = (pubKey: PublicKey, info: AccountInfo<Buffer>) => {
const buffer = Buffer.from(info.data); const buffer = Buffer.from(info.data);
const data = deserializeAccount(buffer); const data = deserializeAccount(buffer);
const details = { const details = {
@ -130,6 +130,10 @@ export const cache = {
cache.registerParser(id, deserialize); cache.registerParser(id, deserialize);
pendingCalls.delete(address); pendingCalls.delete(address);
const account = deserialize(new PublicKey(address), obj); const account = deserialize(new PublicKey(address), obj);
if (!account) {
return;
}
genericCache.set(address, account); genericCache.set(address, account);
cache.emitter.raiseCacheUpdated(address, deserialize); cache.emitter.raiseCacheUpdated(address, deserialize);
return account; return account;
@ -146,8 +150,8 @@ export const cache = {
}, },
byParser: (parser: AccountParser) => { byParser: (parser: AccountParser) => {
const result: string[] = []; const result: string[] = [];
for(const id of keyToAccountParser.keys()) { for (const id of keyToAccountParser.keys()) {
if(keyToAccountParser.get(id) === parser) { if (keyToAccountParser.get(id) === parser) {
result.push(id); result.push(id);
} }
} }
@ -155,8 +159,12 @@ export const cache = {
return result; return result;
}, },
registerParser: (pubkey: PublicKey | string, parser: AccountParser) => { registerParser: (pubkey: PublicKey | string, parser: AccountParser) => {
const address = typeof pubkey === 'string' ? pubkey : pubkey?.toBase58(); if (pubkey) {
keyToAccountParser.set(address, parser); const address = typeof pubkey === 'string' ? pubkey : pubkey?.toBase58();
keyToAccountParser.set(address, parser);
}
return pubkey;
}, },
}; };
@ -200,7 +208,7 @@ const UseNativeAccount = () => {
const updateCache = useCallback((account) => { const updateCache = useCallback((account) => {
const wrapped = wrapNativeAccount(wallet.publicKey, account); const wrapped = wrapNativeAccount(wallet.publicKey, account);
if(wrapped !== undefined && wallet) { if (wrapped !== undefined && wallet) {
cache.registerParser(wallet.publicKey.toBase58(), TokenAccountParser); cache.registerParser(wallet.publicKey.toBase58(), TokenAccountParser);
genericCache.set(wallet.publicKey.toBase58(), wrapped as TokenAccount); genericCache.set(wallet.publicKey.toBase58(), wrapped as TokenAccount);
} }
@ -352,7 +360,7 @@ export const getMultipleAccounts = async (
.map( .map(
(a) => (a) =>
a.array.map((acc) => { a.array.map((acc) => {
if(!acc) { if (!acc) {
return; return;
} }

View File

@ -12,16 +12,20 @@ import { notify } from "./../utils/notifications";
import { ExplorerLink } from "../components/ExplorerLink"; import { ExplorerLink } from "../components/ExplorerLink";
import LocalTokens from '../config/tokens.json'; import LocalTokens from '../config/tokens.json';
export type ENV = "mainnet-beta" | "testnet" | "devnet" | "localnet"; export type ENV = "mainnet-beta" | "testnet" | "devnet" | "localnet" | "lending";
export const ENDPOINTS = [ export const ENDPOINTS = [
{
name: 'lending' as ENV,
endpoint: "https://tln.solana.com",
},
{ {
name: "mainnet-beta" as ENV, name: "mainnet-beta" as ENV,
endpoint: "https://solana-api.projectserum.com/", endpoint: "https://solana-api.projectserum.com/",
}, },
{ name: "testnet" as ENV, endpoint: clusterApiUrl("testnet") }, { name: "testnet" as ENV, endpoint: clusterApiUrl("testnet") },
{ name: "devnet" as ENV, endpoint: clusterApiUrl("devnet") }, { name: "devnet" as ENV, endpoint: clusterApiUrl("devnet") },
{ name: "localnet" as ENV, endpoint: "http://35.206.228.142:8899" }, { name: "localnet" as ENV, endpoint: "http://127.0.0.1:8899" },
]; ];
const DEFAULT = ENDPOINTS[0].endpoint; const DEFAULT = ENDPOINTS[0].endpoint;

View File

@ -2,8 +2,9 @@ import React, { useCallback, useEffect, useState } from "react";
import { useConnection } from "./connection"; import { useConnection } from "./connection";
import { LENDING_PROGRAM_ID } from "./../constants/ids"; import { LENDING_PROGRAM_ID } from "./../constants/ids";
import { LendingMarketParser, isLendingReserve, isLendingMarket, LendingReserveParser, LendingReserve } from "./../models/lending"; import { LendingMarketParser, isLendingReserve, isLendingMarket, LendingReserveParser, LendingReserve } from "./../models/lending";
import { cache, getMultipleAccounts } from "./accounts"; import { cache, getMultipleAccounts, MintParser, ParsedAccount } from "./accounts";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import { DexMarketParser } from "../models/dex";
export interface LendingContextState { export interface LendingContextState {
@ -51,20 +52,25 @@ export const useLending = () => {
const toQuery = [ const toQuery = [
...accounts.filter(acc => (acc?.info as LendingReserve).lendingMarket !== undefined) ...accounts.filter(acc => (acc?.info as LendingReserve).lendingMarket !== undefined)
.map(acc => [ .map(acc => acc as ParsedAccount<LendingReserve>)
(acc?.info as LendingReserve).collateralMint.toBase58(), .map(acc => {
(acc?.info as LendingReserve).liquidityMint.toBase58(), const result = [
(acc?.info as LendingReserve).dexMarket.toBase58(), cache.registerParser(acc?.info.collateralMint.toBase58(), MintParser),
]) cache.registerParser(acc?.info.liquidityMint.toBase58(), MintParser),
].flat().filter((p) => p) as string[]; // ignore dex if its not set
cache.registerParser(acc?.info.dexMarketOption ? acc?.info.dexMarket.toBase58() : '', DexMarketParser),
].filter(_ => _);
return result;
})
].flat() as string[];
// This will pre-cache all accounts used by pools // This will pre-cache all accounts used by pools
// All those accounts are updated whenever there is a change // All those accounts are updated whenever there is a change
await getMultipleAccounts(connection, toQuery, "single").then( await getMultipleAccounts(connection, toQuery, "single").then(
({ keys, array }) => { ({ keys, array }) => {
return array.map((obj, index) => { return array.map((obj, index) => {
// TODO: add to cache const address = keys[index];
cache.add(address, obj);
return obj; return obj;
}) as any[]; }) as any[];
} }

View File

@ -15,6 +15,8 @@ import { AccountInfo, Connection, PublicKey } from "@solana/web3.js";
import { useMemo } from "react"; import { useMemo } from "react";
import { EventEmitter } from "./../utils/eventEmitter"; import { EventEmitter } from "./../utils/eventEmitter";
import { DexMarketParser } from "./../models/dex";
export interface MarketsContextState { export interface MarketsContextState {
midPriceInUSD: (mint: string) => number; midPriceInUSD: (mint: string) => number;
marketEmitter: EventEmitter; marketEmitter: EventEmitter;
@ -39,7 +41,9 @@ export function MarketProvider({ children = null as any }) {
]); ]);
// TODO: identify which markets to query ... // TODO: identify which markets to query ...
const mints = useMemo(() => [] as PublicKey[], []); const mints = useMemo(() => [
] as PublicKey[], []);
const marketByMint = useMemo(() => { const marketByMint = useMemo(() => {
return [ return [
@ -107,24 +111,7 @@ export function MarketProvider({ children = null as any }) {
if (market) { if (market) {
const programId = market.marketInfo.programId; const programId = market.marketInfo.programId;
const id = market.marketInfo.address; const id = market.marketInfo.address;
cache.add(id, item, (id, acc) => { cache.add(id, item, DexMarketParser);
const decoded = Market.getLayout(programId).decode(acc.data);
const details = {
pubkey: id,
account: {
...acc,
},
info: decoded,
} as ParsedAccountBase;
cache.registerParser(details.info.baseMint, MintParser);
cache.registerParser(details.info.quoteMint, MintParser);
cache.registerParser(details.info.bids, OrderBookParser);
cache.registerParser(details.info.asks, OrderBookParser);
return details;
});
} }
} }
@ -256,19 +243,7 @@ export const useMidPriceInUSD = (mint: string) => {
return { price, isBase: price === 1.0 }; return { price, isBase: price === 1.0 };
}; };
const OrderBookParser = (id: PublicKey, acc: AccountInfo<Buffer>) => {
const decoded = Orderbook.LAYOUT.decode(acc.data);
const details = {
pubkey: id,
account: {
...acc,
},
info: decoded,
} as ParsedAccountBase;
return details;
};
const getMidPrice = (marketAddress?: string, mintAddress?: string) => { const getMidPrice = (marketAddress?: string, mintAddress?: string) => {
const SERUM_TOKEN = TOKEN_MINTS.find( const SERUM_TOKEN = TOKEN_MINTS.find(

View File

@ -1,4 +1,3 @@
import { useContext } from "react";
import { TokenAccount } from "../models"; import { TokenAccount } from "../models";
import { useAccountsContext } from './../contexts/accounts'; import { useAccountsContext } from './../contexts/accounts';

View File

@ -11,12 +11,12 @@ export function useUserBalance(mint?: PublicKey) {
return userAccounts return userAccounts
.filter(acc => mint?.equals(acc.info.mint)) .filter(acc => mint?.equals(acc.info.mint))
.sort((a, b) => b.info.amount.sub(a.info.amount).toNumber()); .sort((a, b) => b.info.amount.sub(a.info.amount).toNumber());
}, [userAccounts]); }, [userAccounts, mint]);
const balanceLamports = useMemo(() => { const balanceLamports = useMemo(() => {
return accounts return accounts
.reduce((res, item) => res += item.info.amount.toNumber(), 0); .reduce((res, item) => res += item.info.amount.toNumber(), 0);
},[accounts, mintInfo]); },[accounts]);
return { return {
balance: fromLamports(balanceLamports, mintInfo), balance: fromLamports(balanceLamports, mintInfo),

1
src/models/dex/index.ts Normal file
View File

@ -0,0 +1 @@
export * from "./market";

44
src/models/dex/market.ts Normal file
View File

@ -0,0 +1,44 @@
import { Market, MARKETS, Orderbook } from "@project-serum/serum";
import { AccountInfo, PublicKey } from "@solana/web3.js";
import {
MintParser,
ParsedAccountBase,
cache,
} from "./../../contexts/accounts";
export const OrderBookParser = (id: PublicKey, acc: AccountInfo<Buffer>) => {
const decoded = Orderbook.LAYOUT.decode(acc.data);
const details = {
pubkey: id,
account: {
...acc,
},
info: decoded,
} as ParsedAccountBase;
return details;
};
const DEFAULT_DEX_ID = new PublicKey('EUqojwWA2rd19FZrzeBncJsm38Jm1hEhE3zsmX3bRc2o');
export const DexMarketParser = (pubkey: PublicKey, acc: AccountInfo<Buffer>) => {
const market = MARKETS.find(m => m.address.equals(pubkey));
const decoded = Market.getLayout(market?.programId || DEFAULT_DEX_ID)
.decode(acc.data);
const details = {
pubkey,
account: {
...acc,
},
info: decoded,
} as ParsedAccountBase;
cache.registerParser(details.info.baseMint, MintParser);
cache.registerParser(details.info.quoteMint, MintParser);
cache.registerParser(details.info.bids, OrderBookParser);
cache.registerParser(details.info.asks, OrderBookParser);
return details;
}

View File

@ -48,12 +48,15 @@ export const borrowInstruction = (
dexMarket: PublicKey, dexMarket: PublicKey,
dexOrderBookSide: PublicKey, dexOrderBookSide: PublicKey,
memory: PublicKey,
): TransactionInstruction => { ): TransactionInstruction => {
const dataLayout = BufferLayout.struct([ const dataLayout = BufferLayout.struct([
BufferLayout.u8("instruction"), BufferLayout.u8("instruction"),
Layout.uint64("collateralAmount"), Layout.uint64("collateralAmount"),
]); ]);
debugger;
const data = Buffer.alloc(dataLayout.span); const data = Buffer.alloc(dataLayout.span);
dataLayout.encode( dataLayout.encode(
{ {
@ -77,6 +80,7 @@ export const borrowInstruction = (
{ pubkey: lendingMarketAuthority, isSigner: false, isWritable: true }, { pubkey: lendingMarketAuthority, isSigner: false, isWritable: true },
{ pubkey: dexMarket, isSigner: false, isWritable: true }, { pubkey: dexMarket, isSigner: false, isWritable: true },
{ pubkey: dexOrderBookSide, isSigner: false, isWritable: false }, { pubkey: dexOrderBookSide, isSigner: false, isWritable: false },
{ pubkey: memory, isSigner: false, isWritable: true },
{ pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false }, { pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
{ pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false }, { pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false }, { pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },

View File

@ -16,6 +16,7 @@ export const LendingReserveLayout: typeof BufferLayout.Structure = BufferLayout.
Layout.uint64("lastUpdateSlot"), Layout.uint64("lastUpdateSlot"),
Layout.publicKey("lendingMarket"), Layout.publicKey("lendingMarket"),
Layout.publicKey("liquidityMint"), Layout.publicKey("liquidityMint"),
BufferLayout.u8("liquidityMintDecimals"),
Layout.publicKey("liquiditySupply"), Layout.publicKey("liquiditySupply"),
Layout.publicKey("collateralMint"), Layout.publicKey("collateralMint"),
Layout.publicKey("collateralSupply"), Layout.publicKey("collateralSupply"),
@ -23,9 +24,16 @@ export const LendingReserveLayout: typeof BufferLayout.Structure = BufferLayout.
BufferLayout.u32('dexMarketOption'), BufferLayout.u32('dexMarketOption'),
Layout.publicKey("dexMarket"), Layout.publicKey("dexMarket"),
BufferLayout.u8("maxUtilizationRate"), BufferLayout.struct([
/// Max utilization rate as a percent
BufferLayout.u8("collateralFactor"), BufferLayout.u8("maxUtilizationRate"),
/// The ratio of the loan to the value of the collateral as a percent
BufferLayout.u8("loanToValueRatio"),
/// The percent discount the liquidator gets when buying collateral for an unhealthy obligation
BufferLayout.u8("liquidationBonus"),
/// The percent at which an obligation is considered unhealthy
BufferLayout.u8("liquidationThreshold"),
], "config"),
Layout.uint128("cumulativeBorrowRate"), Layout.uint128("cumulativeBorrowRate"),
Layout.uint128("totalBorrows"), Layout.uint128("totalBorrows"),
@ -36,6 +44,8 @@ export const LendingReserveLayout: typeof BufferLayout.Structure = BufferLayout.
); );
export const isLendingReserve = (info: AccountInfo<Buffer>) => { export const isLendingReserve = (info: AccountInfo<Buffer>) => {
console.log(LendingReserveLayout.span);
console.log(info.data.length);
return info.data.length === LendingReserveLayout.span; return info.data.length === LendingReserveLayout.span;
} }