feat: add borrow instruction
This commit is contained in:
parent
5cd9478317
commit
d63da050ad
|
@ -29,7 +29,7 @@ body {
|
|||
text-align: center;
|
||||
display: flex;
|
||||
align-self: center;
|
||||
align-items: centerconn;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,33 +1,29 @@
|
|||
import {
|
||||
Account,
|
||||
AccountInfo,
|
||||
Connection,
|
||||
PublicKey,
|
||||
sendAndConfirmRawTransaction,
|
||||
SYSVAR_CLOCK_PUBKEY,
|
||||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import BN from "bn.js";
|
||||
import * as BufferLayout from "buffer-layout";
|
||||
import { sendTransaction } from "../contexts/connection";
|
||||
import { notify } from "../utils/notifications";
|
||||
import * as Layout from "./../utils/layout";
|
||||
import { depositInstruction, initReserveInstruction, LendingReserve } from "./../models/lending/reserve";
|
||||
import { AccountLayout, MintInfo, Token } from "@solana/spl-token";
|
||||
import { LendingReserve } from "./../models/lending/reserve";
|
||||
import { AccountLayout, MintInfo, MintLayout, Token } from "@solana/spl-token";
|
||||
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids";
|
||||
import { createUninitializedAccount, ensureSplAccount, findOrCreateAccountByMint } from "./account";
|
||||
import { cache, GenericAccountParser, MintParser, ParsedAccount } from "../contexts/accounts";
|
||||
import { TokenAccount } from "../models";
|
||||
import { isConstructorDeclaration } from "typescript";
|
||||
import { LendingMarketParser } from "../models/lending";
|
||||
import { sign } from "crypto";
|
||||
import { fromLamports, toLamports } from "../utils/utils";
|
||||
import { cache, MintParser, ParsedAccount } from "../contexts/accounts";
|
||||
import { TokenAccount, LendingObligationLayout, borrowInstruction, LendingMarket } from "../models";
|
||||
import { toLamports } from "../utils/utils";
|
||||
|
||||
export const borrow = async (
|
||||
from: TokenAccount,
|
||||
amount: number,
|
||||
reserve: LendingReserve,
|
||||
reserveAddress: PublicKey,
|
||||
|
||||
borrowReserve: LendingReserve,
|
||||
borrowReserveAddress: PublicKey,
|
||||
|
||||
depositReserve: LendingReserve,
|
||||
depositReserveAddress: PublicKey,
|
||||
|
||||
connection: Connection,
|
||||
wallet: any) => {
|
||||
|
||||
|
@ -37,9 +33,6 @@ export const borrow = async (
|
|||
type: "warn",
|
||||
});
|
||||
|
||||
const isInitalized = true; // TODO: finish reserve init
|
||||
|
||||
// user from account
|
||||
const signers: Account[] = [];
|
||||
const instructions: TransactionInstruction[] = [];
|
||||
const cleanupInstructions: TransactionInstruction[] = [];
|
||||
|
@ -49,11 +42,11 @@ export const borrow = async (
|
|||
);
|
||||
|
||||
const [authority] = await PublicKey.findProgramAddress(
|
||||
[reserve.lendingMarket.toBuffer()], // which account should be authority
|
||||
[depositReserve.lendingMarket.toBuffer()], // which account should be authority
|
||||
LENDING_PROGRAM_ID
|
||||
);
|
||||
|
||||
const mint = (await cache.query(connection, reserve.liquidityMint, MintParser)) as ParsedAccount<MintInfo>;
|
||||
const mint = (await cache.query(connection, depositReserve.collateralMint, MintParser)) as ParsedAccount<MintInfo>;
|
||||
const amountLamports = toLamports(amount, mint?.info);
|
||||
|
||||
const fromAccount = ensureSplAccount(
|
||||
|
@ -77,37 +70,77 @@ export const borrow = async (
|
|||
)
|
||||
);
|
||||
|
||||
let toAccount: PublicKey;
|
||||
if (isInitalized) {
|
||||
// get destination account
|
||||
toAccount = await findOrCreateAccountByMint(
|
||||
wallet.publicKey,
|
||||
wallet.publicKey,
|
||||
instructions,
|
||||
cleanupInstructions,
|
||||
accountRentExempt,
|
||||
reserve.collateralMint,
|
||||
signers
|
||||
);
|
||||
} else {
|
||||
toAccount = createUninitializedAccount(
|
||||
instructions,
|
||||
wallet.publicKey,
|
||||
accountRentExempt,
|
||||
signers,
|
||||
);
|
||||
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 dexMarketAddress = market.info.quoteMint.equals(borrowReserve.liquidityMint) ?
|
||||
borrowReserve.dexMarket :
|
||||
depositReserve.dexMarket;
|
||||
|
||||
const dexMarket = cache.get(dexMarketAddress);
|
||||
if(!dexMarket) {
|
||||
throw new Error(`Dex market doesn't exsists.`)
|
||||
}
|
||||
|
||||
const dexOrderBookSide = market.info.quoteMint.equals(borrowReserve.liquidityMint) ?
|
||||
dexMarket.info.bids :
|
||||
dexMarket.info.asks;
|
||||
|
||||
// deposit
|
||||
instructions.push(
|
||||
depositInstruction(
|
||||
borrowInstruction(
|
||||
amountLamports,
|
||||
fromAccount,
|
||||
toAccount,
|
||||
depositReserveAddress,
|
||||
depositReserve.liquiditySupply,
|
||||
borrowReserveAddress,
|
||||
borrowReserve.liquiditySupply,
|
||||
|
||||
obligation,
|
||||
obligationMint,
|
||||
obligationTokenOutput,
|
||||
wallet.publicKey,
|
||||
|
||||
authority,
|
||||
reserveAddress,
|
||||
reserve.liquiditySupply,
|
||||
reserve.collateralMint,
|
||||
|
||||
dexMarketAddress,
|
||||
dexOrderBookSide,
|
||||
)
|
||||
);
|
||||
try {
|
||||
|
|
|
@ -19,27 +19,43 @@ export const BorrowInput = (props: { className?: string, reserve: LendingReserve
|
|||
const { id } = useParams<{ id: string }>();
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
const reserve = props.reserve;
|
||||
const address = props.address;
|
||||
const borrowReserve = props.reserve;
|
||||
const borrowReserveAddress = props.address;
|
||||
|
||||
const name = useTokenName(reserve?.liquidityMint);
|
||||
const { balance: tokenBalance, accounts: fromAccounts } = useUserBalance(reserve?.liquidityMint);
|
||||
const [collateralReserve, setCollateralReserve] = useState<LendingReserve>();
|
||||
|
||||
const collateralReserveAddress = useMemo(() => {
|
||||
return cache.byParser(LendingReserveParser)
|
||||
.find(acc => cache.get(acc) === collateralReserve);
|
||||
}, [collateralReserve])
|
||||
|
||||
const name = useTokenName(borrowReserve?.liquidityMint);
|
||||
const {
|
||||
balance: tokenBalance,
|
||||
accounts: fromAccounts
|
||||
} = useUserBalance(collateralReserve?.liquidityMint);
|
||||
// const collateralBalance = useUserBalance(reserve?.collateralMint);
|
||||
|
||||
const onBorrow = useCallback(() => {
|
||||
if(!collateralReserve || !collateralReserveAddress) {
|
||||
return;
|
||||
}
|
||||
|
||||
borrow(
|
||||
fromAccounts[0],
|
||||
parseFloat(value),
|
||||
reserve,
|
||||
address,
|
||||
borrowReserve,
|
||||
borrowReserveAddress,
|
||||
collateralReserve,
|
||||
new PublicKey(collateralReserveAddress),
|
||||
connection,
|
||||
wallet);
|
||||
}, [value, reserve, fromAccounts, address]);
|
||||
}, [value, borrowReserve, fromAccounts, borrowReserveAddress]);
|
||||
|
||||
const bodyStyle: React.CSSProperties = {
|
||||
display: 'flex',
|
||||
const bodyStyle: React.CSSProperties = {
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
};
|
||||
|
@ -51,7 +67,7 @@ export const BorrowInput = (props: { className?: string, reserve: LendingReserve
|
|||
How much would you like to borrow?
|
||||
</div>
|
||||
<div className="token-input">
|
||||
<TokenIcon mintAddress={reserve?.liquidityMint} />
|
||||
<TokenIcon mintAddress={borrowReserve?.liquidityMint} />
|
||||
<NumericInput value={value}
|
||||
onChange={(val: any) => {
|
||||
setValue(val);
|
||||
|
|
|
@ -55,6 +55,7 @@ export const useLending = () => {
|
|||
.map(acc => [
|
||||
(acc?.info as LendingReserve).collateralMint.toBase58(),
|
||||
(acc?.info as LendingReserve).liquidityMint.toBase58(),
|
||||
(acc?.info as LendingReserve).dexMarket.toBase58(),
|
||||
])
|
||||
].flat().filter((p) => p) as string[];
|
||||
|
||||
|
|
|
@ -15,11 +15,6 @@ import { AccountInfo, Connection, PublicKey } from "@solana/web3.js";
|
|||
import { useMemo } from "react";
|
||||
import { EventEmitter } from "./../utils/eventEmitter";
|
||||
|
||||
interface RecentPoolData {
|
||||
pool_identifier: string;
|
||||
volume24hA: number;
|
||||
}
|
||||
|
||||
export interface MarketsContextState {
|
||||
midPriceInUSD: (mint: string) => number;
|
||||
marketEmitter: EventEmitter;
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
export * from "./account";
|
||||
export * from './lending';
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
import {
|
||||
PublicKey,
|
||||
SYSVAR_CLOCK_PUBKEY,
|
||||
SYSVAR_RENT_PUBKEY,
|
||||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import BN from "bn.js";
|
||||
import * as BufferLayout from "buffer-layout";
|
||||
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids";
|
||||
import * as Layout from "./../../utils/layout";
|
||||
import { LendingInstruction } from './lending';
|
||||
|
||||
/// Borrow tokens from a reserve by depositing collateral tokens. The number of borrowed tokens
|
||||
/// is calculated by market price. The debt obligation is tokenized.
|
||||
///
|
||||
/// 0. `[writable]` Collateral input SPL Token account, $authority can transfer $collateral_amount
|
||||
/// 1. `[writable]` Liquidity output SPL Token account
|
||||
/// 2. `[writable]` Deposit reserve account.
|
||||
/// 3. `[writable]` Deposit reserve collateral supply SPL Token account
|
||||
/// 4. `[writable]` Borrow reserve account.
|
||||
/// 5. `[writable]` Borrow reserve liquidity supply SPL Token account
|
||||
/// 6. `[writable]` Obligation - uninitialized
|
||||
/// 7. `[writable]` Obligation token mint - uninitialized
|
||||
/// 8. `[writable]` Obligation token output - uninitialized
|
||||
/// 9. `[]` Obligation token owner - uninitialized
|
||||
/// 10 `[]` Derived lending market authority ($authority).
|
||||
/// 11 `[]` Dex market
|
||||
/// 12 `[]` Dex order book side // could be bid/ask
|
||||
/// 13 `[]` Temporary memory
|
||||
/// 14 `[]` Clock sysvar
|
||||
/// 15 `[]` Rent sysvar
|
||||
/// 16 '[]` Token program id
|
||||
export const borrowInstruction = (
|
||||
collateralAmount: number | BN,
|
||||
from: PublicKey, // Collateral input SPL Token account. $authority can transfer $collateralAmount
|
||||
to: PublicKey, // Liquidity output SPL Token account,
|
||||
depositReserve: PublicKey,
|
||||
depositReserveCollateralSupply: PublicKey,
|
||||
borrowReserve: PublicKey,
|
||||
borrowReserveLiquiditySupply: PublicKey,
|
||||
|
||||
obligation: PublicKey,
|
||||
obligationMint: PublicKey,
|
||||
obligationTokenOutput: PublicKey,
|
||||
obligationTokenOwner: PublicKey,
|
||||
|
||||
lendingMarketAuthority: PublicKey,
|
||||
|
||||
dexMarket: PublicKey,
|
||||
dexOrderBookSide: PublicKey,
|
||||
): TransactionInstruction => {
|
||||
const dataLayout = BufferLayout.struct([
|
||||
BufferLayout.u8("instruction"),
|
||||
Layout.uint64("collateralAmount"),
|
||||
]);
|
||||
|
||||
const data = Buffer.alloc(dataLayout.span);
|
||||
dataLayout.encode(
|
||||
{
|
||||
instruction: LendingInstruction.BorrowReserveLiquidity,
|
||||
collateralAmount: new BN(collateralAmount),
|
||||
},
|
||||
data
|
||||
);
|
||||
|
||||
const keys = [
|
||||
{ pubkey: from, isSigner: false, isWritable: true },
|
||||
{ pubkey: to, isSigner: false, isWritable: true },
|
||||
{ pubkey: depositReserve, isSigner: false, isWritable: true },
|
||||
{ pubkey: depositReserveCollateralSupply, isSigner: false, isWritable: true },
|
||||
{ pubkey: borrowReserve, isSigner: false, isWritable: true },
|
||||
{ pubkey: borrowReserveLiquiditySupply, isSigner: false, isWritable: false },
|
||||
{ pubkey: obligation, isSigner: false, isWritable: true },
|
||||
{ pubkey: obligationMint, isSigner: false, isWritable: true },
|
||||
{ pubkey: obligationTokenOutput, isSigner: false, isWritable: true },
|
||||
{ pubkey: obligationTokenOwner, isSigner: false, isWritable: false },
|
||||
{ pubkey: lendingMarketAuthority, isSigner: false, isWritable: true },
|
||||
{ pubkey: dexMarket, isSigner: false, isWritable: true },
|
||||
{ pubkey: dexOrderBookSide, isSigner: false, isWritable: false },
|
||||
{ pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
|
||||
{ pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
|
||||
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
||||
];
|
||||
return new TransactionInstruction({
|
||||
keys,
|
||||
programId: LENDING_PROGRAM_ID,
|
||||
data,
|
||||
});
|
||||
};
|
|
@ -1,2 +1,5 @@
|
|||
export * from './market';
|
||||
export * from './reserve';
|
||||
export * from './obligation';
|
||||
export * from './lending';
|
||||
export * from './borrow';
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
export enum LendingInstruction {
|
||||
InitLendingMarket = 0,
|
||||
InitReserve = 1,
|
||||
DepositReserveLiquidity = 2,
|
||||
WithdrawReserveLiquidity = 3,
|
||||
BorrowReserveLiquidity = 4,
|
||||
RepayReserveLiquidity = 5
|
||||
}
|
|
@ -1 +1,44 @@
|
|||
export const x = 1;
|
||||
import {
|
||||
AccountInfo,
|
||||
Connection,
|
||||
PublicKey,
|
||||
sendAndConfirmRawTransaction,
|
||||
SYSVAR_CLOCK_PUBKEY,
|
||||
SYSVAR_RENT_PUBKEY,
|
||||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import BN from "bn.js";
|
||||
import * as BufferLayout from "buffer-layout";
|
||||
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids";
|
||||
import { sendTransaction } from "../../contexts/connection";
|
||||
import * as Layout from "./../../utils/layout";
|
||||
import { LendingInstruction } from './lending';
|
||||
|
||||
export const LendingObligationLayout: typeof BufferLayout.Structure = BufferLayout.struct(
|
||||
[
|
||||
/// Slot when obligation was updated. Used for calculating interest.
|
||||
Layout.uint64("lastUpdateSlot"),
|
||||
/// Amount of collateral tokens deposited for this obligation
|
||||
Layout.uint64("collateralAmount"),
|
||||
/// Reserve which collateral tokens were deposited into
|
||||
Layout.publicKey("collateralSupply"),
|
||||
/// Borrow rate used for calculating interest.
|
||||
Layout.uint128("cumulativeBorrowRate"),
|
||||
/// Amount of tokens borrowed for this obligation plus interest
|
||||
Layout.uint128("borrowAmount"),
|
||||
/// Reserve which tokens were borrowed from
|
||||
Layout.publicKey("borrowReserve"),
|
||||
/// Mint address of the tokens for this obligation
|
||||
Layout.publicKey("tokenMint"),
|
||||
]
|
||||
);
|
||||
|
||||
export interface LendingObligation {
|
||||
lastUpdateSlot: BN;
|
||||
collateralAmount: BN;
|
||||
collateralSupply: PublicKey;
|
||||
cumulativeBorrowRate: BN; // decimals
|
||||
borrowAmount: BN; // decimals
|
||||
borrowReserve: PublicKey;
|
||||
tokenMint: PublicKey;
|
||||
}
|
|
@ -12,6 +12,7 @@ import * as BufferLayout from "buffer-layout";
|
|||
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids";
|
||||
import { sendTransaction } from "../../contexts/connection";
|
||||
import * as Layout from "./../../utils/layout";
|
||||
import { LendingInstruction } from './lending';
|
||||
|
||||
export const LendingReserveLayout: typeof BufferLayout.Structure = BufferLayout.struct(
|
||||
[
|
||||
|
@ -108,7 +109,7 @@ export const initReserveInstruction = (
|
|||
const data = Buffer.alloc(dataLayout.span);
|
||||
dataLayout.encode(
|
||||
{
|
||||
instruction: 1, // Init reserve instruction
|
||||
instruction: LendingInstruction.InitReserve, // Init reserve instruction
|
||||
liquidityAmount: new BN(liquidityAmount),
|
||||
maxUtilizationRate: maxUtilizationRate,
|
||||
},
|
||||
|
@ -169,7 +170,7 @@ export const depositInstruction = (
|
|||
const data = Buffer.alloc(dataLayout.span);
|
||||
dataLayout.encode(
|
||||
{
|
||||
instruction: 2, // Deposit instruction
|
||||
instruction: LendingInstruction.DepositReserveLiquidity, // Deposit instruction
|
||||
liquidityAmount: new BN(liquidityAmount),
|
||||
},
|
||||
data
|
||||
|
|
Loading…
Reference in New Issue