feat: add deposit instruction
This commit is contained in:
parent
682b21c9f9
commit
5c5fdd8d5c
|
@ -29,7 +29,7 @@ body {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
align-items: center;
|
align-items: centerconn;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
import { AccountLayout, Token } from "@solana/spl-token";
|
||||||
|
import { Account, PublicKey, SystemProgram, TransactionInstruction } from "@solana/web3.js";
|
||||||
|
import { TOKEN_PROGRAM_ID, WRAPPED_SOL_MINT } from "../constants/ids";
|
||||||
|
import { TokenAccount } from "../models";
|
||||||
|
import { cache, TokenAccountParser } from './../contexts/accounts';
|
||||||
|
|
||||||
|
export function ensureSplAccount(
|
||||||
|
instructions: TransactionInstruction[],
|
||||||
|
cleanupInstructions: TransactionInstruction[],
|
||||||
|
toCheck: TokenAccount,
|
||||||
|
payer: PublicKey,
|
||||||
|
amount: number,
|
||||||
|
signers: Account[]
|
||||||
|
) {
|
||||||
|
if (!toCheck.info.isNative) {
|
||||||
|
return toCheck.pubkey;
|
||||||
|
}
|
||||||
|
|
||||||
|
const account = createUninitializedAccount(
|
||||||
|
instructions,
|
||||||
|
payer,
|
||||||
|
amount,
|
||||||
|
signers);
|
||||||
|
|
||||||
|
instructions.push(
|
||||||
|
Token.createInitAccountInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
WRAPPED_SOL_MINT,
|
||||||
|
account,
|
||||||
|
payer
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
cleanupInstructions.push(
|
||||||
|
Token.createCloseAccountInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
account,
|
||||||
|
payer,
|
||||||
|
payer,
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createUninitializedAccount(
|
||||||
|
instructions: TransactionInstruction[],
|
||||||
|
payer: PublicKey,
|
||||||
|
amount: number,
|
||||||
|
signers: Account[]) {
|
||||||
|
const account = new Account();
|
||||||
|
instructions.push(
|
||||||
|
SystemProgram.createAccount({
|
||||||
|
fromPubkey: payer,
|
||||||
|
newAccountPubkey: account.publicKey,
|
||||||
|
lamports: amount,
|
||||||
|
space: AccountLayout.span,
|
||||||
|
programId: TOKEN_PROGRAM_ID,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
signers.push(account);
|
||||||
|
|
||||||
|
return account.publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createTokenAccount(
|
||||||
|
instructions: TransactionInstruction[],
|
||||||
|
payer: PublicKey,
|
||||||
|
accountRentExempt: number,
|
||||||
|
mint: PublicKey,
|
||||||
|
owner: PublicKey,
|
||||||
|
signers: Account[],
|
||||||
|
) {
|
||||||
|
const account = createUninitializedAccount(
|
||||||
|
instructions,
|
||||||
|
payer,
|
||||||
|
accountRentExempt,
|
||||||
|
signers);
|
||||||
|
|
||||||
|
instructions.push(
|
||||||
|
Token.createInitAccountInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
mint,
|
||||||
|
account,
|
||||||
|
owner
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check if one of to accounts needs to be native sol ... if yes unwrap it ...
|
||||||
|
export function findOrCreateAccountByMint(
|
||||||
|
payer: PublicKey,
|
||||||
|
owner: PublicKey,
|
||||||
|
instructions: TransactionInstruction[],
|
||||||
|
cleanupInstructions: TransactionInstruction[],
|
||||||
|
accountRentExempt: number,
|
||||||
|
mint: PublicKey, // use to identify same type
|
||||||
|
signers: Account[],
|
||||||
|
excluded?: Set<string>
|
||||||
|
): PublicKey {
|
||||||
|
const accountToFind = mint.toBase58();
|
||||||
|
const account = cache.byParser(TokenAccountParser)
|
||||||
|
.map(id => cache.get(id))
|
||||||
|
.find(
|
||||||
|
(acc) =>
|
||||||
|
acc !== undefined &&
|
||||||
|
acc.info.mint.toBase58() === accountToFind &&
|
||||||
|
acc.info.owner.toBase58() === owner.toBase58() &&
|
||||||
|
(excluded === undefined || !excluded.has(acc.pubkey.toBase58()))
|
||||||
|
);
|
||||||
|
const isWrappedSol = accountToFind === WRAPPED_SOL_MINT.toBase58();
|
||||||
|
|
||||||
|
let toAccount: PublicKey;
|
||||||
|
if (account && !isWrappedSol) {
|
||||||
|
toAccount = account.pubkey;
|
||||||
|
} else {
|
||||||
|
// creating depositor pool account
|
||||||
|
toAccount = createTokenAccount(
|
||||||
|
instructions,
|
||||||
|
payer,
|
||||||
|
accountRentExempt,
|
||||||
|
mint,
|
||||||
|
owner,
|
||||||
|
signers,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isWrappedSol) {
|
||||||
|
cleanupInstructions.push(
|
||||||
|
Token.createCloseAccountInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
toAccount,
|
||||||
|
payer,
|
||||||
|
payer,
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return toAccount;
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
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, Token } from "@solana/spl-token";
|
||||||
|
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids";
|
||||||
|
import { createUninitializedAccount, ensureSplAccount, findOrCreateAccountByMint } from "./account";
|
||||||
|
import { cache, GenericAccountParser } from "../contexts/accounts";
|
||||||
|
import { TokenAccount } from "../models";
|
||||||
|
import { isConstructorDeclaration } from "typescript";
|
||||||
|
import { LendingMarketParser } from "../models/lending";
|
||||||
|
import { sign } from "crypto";
|
||||||
|
|
||||||
|
export const deposit = async (
|
||||||
|
from: TokenAccount,
|
||||||
|
amount: number,
|
||||||
|
reserve: LendingReserve,
|
||||||
|
reserveAddress: PublicKey,
|
||||||
|
connection: Connection,
|
||||||
|
wallet: any) => {
|
||||||
|
|
||||||
|
// TODO: customize ?
|
||||||
|
const MAX_UTILIZATION_RATE = 80;
|
||||||
|
|
||||||
|
notify({
|
||||||
|
message: "Depositing funds...",
|
||||||
|
description: "Please review transactions to approve.",
|
||||||
|
type: "warn",
|
||||||
|
});
|
||||||
|
|
||||||
|
const isInitalized = true; // TODO: finish reserve init
|
||||||
|
|
||||||
|
// user from account
|
||||||
|
const signers: Account[] = [];
|
||||||
|
const instructions: TransactionInstruction[] = [];
|
||||||
|
const cleanupInstructions: TransactionInstruction[] = [];
|
||||||
|
|
||||||
|
const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
|
||||||
|
AccountLayout.span
|
||||||
|
);
|
||||||
|
|
||||||
|
const [authority] = await PublicKey.findProgramAddress(
|
||||||
|
[reserve.lendingMarket.toBuffer()], // which account should be authority
|
||||||
|
LENDING_PROGRAM_ID
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: ...
|
||||||
|
const amountLamports = amount;
|
||||||
|
|
||||||
|
const fromAccount = ensureSplAccount(
|
||||||
|
instructions,
|
||||||
|
cleanupInstructions,
|
||||||
|
from,
|
||||||
|
wallet.publicKey,
|
||||||
|
amountLamports + accountRentExempt,
|
||||||
|
signers
|
||||||
|
);
|
||||||
|
|
||||||
|
// create approval for transfer transactions
|
||||||
|
instructions.push(
|
||||||
|
Token.createApproveInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
fromAccount,
|
||||||
|
authority,
|
||||||
|
wallet.publicKey,
|
||||||
|
[],
|
||||||
|
amountLamports,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
let toAccount: PublicKey;
|
||||||
|
if(isInitalized) {
|
||||||
|
// get destination account
|
||||||
|
toAccount = await findOrCreateAccountByMint(
|
||||||
|
wallet.publicKey,
|
||||||
|
wallet.publicKey,
|
||||||
|
instructions,
|
||||||
|
cleanupInstructions,
|
||||||
|
accountRentExempt,
|
||||||
|
reserve.liquidityMint,
|
||||||
|
signers
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
toAccount = createUninitializedAccount(
|
||||||
|
instructions,
|
||||||
|
wallet.publicKey,
|
||||||
|
accountRentExempt,
|
||||||
|
signers,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInitalized) {
|
||||||
|
// deposit
|
||||||
|
instructions.push(
|
||||||
|
depositInstruction(
|
||||||
|
amountLamports,
|
||||||
|
fromAccount,
|
||||||
|
toAccount,
|
||||||
|
authority,
|
||||||
|
reserveAddress,
|
||||||
|
reserve.liquiditySupply,
|
||||||
|
reserve.collateralMint,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// TODO: finish reserve init
|
||||||
|
|
||||||
|
instructions.push(initReserveInstruction(
|
||||||
|
amountLamports,
|
||||||
|
MAX_UTILIZATION_RATE,
|
||||||
|
fromAccount,
|
||||||
|
toAccount,
|
||||||
|
reserveAddress,
|
||||||
|
reserve.liquidityMint,
|
||||||
|
reserve.liquiditySupply,
|
||||||
|
reserve.collateralMint,
|
||||||
|
reserve.collateralSupply,
|
||||||
|
reserve.lendingMarket,
|
||||||
|
authority,
|
||||||
|
reserve.dexMarket,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let tx = await sendTransaction(
|
||||||
|
connection,
|
||||||
|
wallet,
|
||||||
|
instructions.concat(cleanupInstructions),
|
||||||
|
signers,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
notify({
|
||||||
|
message: "Funds deposited.",
|
||||||
|
type: "success",
|
||||||
|
description: `Transaction - ${tx}`,
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
// TODO:
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ export class NumericInput extends React.Component<any, any> {
|
||||||
onChange = (e: any) => {
|
onChange = (e: any) => {
|
||||||
const { value } = e.target;
|
const { value } = e.target;
|
||||||
const reg = /^-?\d*(\.\d*)?$/;
|
const reg = /^-?\d*(\.\d*)?$/;
|
||||||
if ((!isNaN(value) && reg.test(value)) || value === "" || value === "-") {
|
if (reg.test(value) || value === "" || value === "-") {
|
||||||
this.props.onChange(value);
|
this.props.onChange(value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,9 @@ export class NumericInput extends React.Component<any, any> {
|
||||||
if (value.charAt(value.length - 1) === "." || value === "-") {
|
if (value.charAt(value.length - 1) === "." || value === "-") {
|
||||||
valueTemp = value.slice(0, -1);
|
valueTemp = value.slice(0, -1);
|
||||||
}
|
}
|
||||||
|
if (value.startsWith(".") || value.startsWith("-.")) {
|
||||||
|
valueTemp = valueTemp.replace(".", "0.");
|
||||||
|
}
|
||||||
onChange(valueTemp.replace(/0*(\d+)/, "$1"));
|
onChange(valueTemp.replace(/0*(\d+)/, "$1"));
|
||||||
if (onBlur) {
|
if (onBlur) {
|
||||||
onBlur();
|
onBlur();
|
||||||
|
@ -33,4 +36,4 @@ export class NumericInput extends React.Component<any, any> {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,13 +6,18 @@ import { useUserAccounts } from "./useUserAccounts";
|
||||||
|
|
||||||
export function useUserBalance(mint?: PublicKey) {
|
export function useUserBalance(mint?: PublicKey) {
|
||||||
const { userAccounts } = useUserAccounts();
|
const { userAccounts } = useUserAccounts();
|
||||||
|
|
||||||
const mintInfo = useMint(mint);
|
const mintInfo = useMint(mint);
|
||||||
|
const accounts = useMemo(() => {
|
||||||
return useMemo(() =>
|
return userAccounts
|
||||||
convert(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());
|
||||||
|
}, [userAccounts]);
|
||||||
|
|
||||||
|
const balance = useMemo(() =>
|
||||||
|
convert(accounts
|
||||||
.reduce((res, item) => res += item.info.amount.toNumber(), 0)
|
.reduce((res, item) => res += item.info.amount.toNumber(), 0)
|
||||||
, mintInfo),
|
, mintInfo),
|
||||||
[userAccounts]);
|
[accounts, mintInfo]);
|
||||||
|
|
||||||
|
return { balance, accounts };
|
||||||
}
|
}
|
|
@ -1,28 +1,36 @@
|
||||||
import {
|
import {
|
||||||
AccountInfo,
|
AccountInfo,
|
||||||
|
Connection,
|
||||||
PublicKey,
|
PublicKey,
|
||||||
|
sendAndConfirmRawTransaction,
|
||||||
|
SYSVAR_CLOCK_PUBKEY,
|
||||||
|
SYSVAR_RENT_PUBKEY,
|
||||||
|
TransactionInstruction,
|
||||||
} from "@solana/web3.js";
|
} from "@solana/web3.js";
|
||||||
import BN from "bn.js";
|
import BN from "bn.js";
|
||||||
import * as BufferLayout from "buffer-layout";
|
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 * as Layout from "./../../utils/layout";
|
||||||
|
|
||||||
export const LendingReserveLayout: typeof BufferLayout.Structure = BufferLayout.struct(
|
export const LendingReserveLayout: typeof BufferLayout.Structure = BufferLayout.struct(
|
||||||
[
|
[
|
||||||
BufferLayout.u8("isInitialized"),
|
Layout.uint64("lastUpdateSlot"),
|
||||||
Layout.publicKey("lendingMarket"),
|
Layout.publicKey("lendingMarket"),
|
||||||
Layout.publicKey("liquiditySupply"),
|
|
||||||
Layout.publicKey("liquidityMint"),
|
Layout.publicKey("liquidityMint"),
|
||||||
Layout.publicKey("collateralSupply"),
|
Layout.publicKey("liquiditySupply"),
|
||||||
Layout.publicKey("collateralMint"),
|
Layout.publicKey("collateralMint"),
|
||||||
|
Layout.publicKey("collateralSupply"),
|
||||||
// TODO: replace u32 option with generic quivalent
|
// TODO: replace u32 option with generic quivalent
|
||||||
BufferLayout.u32('dexMarketOption'),
|
BufferLayout.u32('dexMarketOption'),
|
||||||
Layout.publicKey("dexMarket"),
|
Layout.publicKey("dexMarket"),
|
||||||
Layout.uint64("dexMarketPrice"),
|
BufferLayout.u8("maxUtilizationRate"),
|
||||||
Layout.uint64("dexMarketPriceUpdatedSlot"),
|
|
||||||
|
|
||||||
Layout.uint128("cumulative_borrow_rate"),
|
Layout.uint128("cumulative_borrow_rate"),
|
||||||
Layout.uint128("total_borrows"),
|
Layout.uint128("total_borrows"),
|
||||||
Layout.uint64("borrow_state_update_slot"),
|
|
||||||
|
Layout.uint64("totalLiquidity"),
|
||||||
|
Layout.uint64("collateralMintSupply"),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -31,7 +39,8 @@ export const isLendingReserve = (info: AccountInfo<Buffer>) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LendingReserve {
|
export interface LendingReserve {
|
||||||
isInitialized: boolean,
|
lastUpdateSlot: BN;
|
||||||
|
|
||||||
lendingMarket: PublicKey;
|
lendingMarket: PublicKey;
|
||||||
liquiditySupply: PublicKey;
|
liquiditySupply: PublicKey;
|
||||||
liquidityMint: PublicKey;
|
liquidityMint: PublicKey;
|
||||||
|
@ -42,11 +51,11 @@ export interface LendingReserve {
|
||||||
dexMarket: PublicKey;
|
dexMarket: PublicKey;
|
||||||
dexMarketPrice: BN; // what is precision on the price?
|
dexMarketPrice: BN; // what is precision on the price?
|
||||||
|
|
||||||
|
maxUtilizationRate: number;
|
||||||
dexMarketPriceUpdatedSlot: BN;
|
dexMarketPriceUpdatedSlot: BN;
|
||||||
|
|
||||||
// Layout.uint128("cumulative_borrow_rate"),
|
// Layout.uint128("cumulative_borrow_rate"),
|
||||||
// Layout.uint128("total_borrows"),
|
// Layout.uint128("total_borrows"),
|
||||||
borrow_state_update_slot: BN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LendingReserveParser = (pubKey: PublicKey, info: AccountInfo<Buffer>) => {
|
export const LendingReserveParser = (pubKey: PublicKey, info: AccountInfo<Buffer>) => {
|
||||||
|
@ -64,5 +73,110 @@ export const LendingReserveParser = (pubKey: PublicKey, info: AccountInfo<Buffer
|
||||||
return details;
|
return details;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO:
|
export const initReserveInstruction = (
|
||||||
// create instructions for init, deposit and withdraw
|
liquidityAmount: number | BN,
|
||||||
|
maxUtilizationRate: number,
|
||||||
|
|
||||||
|
from: PublicKey, // Liquidity input SPL Token account. $authority can transfer $liquidity_amount
|
||||||
|
to: PublicKey, // Collateral output SPL Token account,
|
||||||
|
|
||||||
|
reserveAccount: PublicKey,
|
||||||
|
liquidityMint: PublicKey,
|
||||||
|
liquiditySupply: PublicKey,
|
||||||
|
collateralMint: PublicKey,
|
||||||
|
collateralSupply: PublicKey,
|
||||||
|
lendingMarket: PublicKey,
|
||||||
|
lendingMarketAuthority: PublicKey,
|
||||||
|
|
||||||
|
dexMarket: PublicKey, // TODO: optional
|
||||||
|
): TransactionInstruction => {
|
||||||
|
const dataLayout = BufferLayout.struct([
|
||||||
|
BufferLayout.u8("instruction"),
|
||||||
|
Layout.uint64("liquidityAmount"),
|
||||||
|
BufferLayout.u8("maxUtilizationRate")
|
||||||
|
]);
|
||||||
|
|
||||||
|
const data = Buffer.alloc(dataLayout.span);
|
||||||
|
dataLayout.encode(
|
||||||
|
{
|
||||||
|
instruction: 1, // Init reserve instruction
|
||||||
|
liquidityAmount: new BN(liquidityAmount),
|
||||||
|
maxUtilizationRate: maxUtilizationRate,
|
||||||
|
},
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
const keys = [
|
||||||
|
{ pubkey: from, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: to, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: reserveAccount, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: liquidityMint, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: liquiditySupply, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: collateralMint, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: collateralSupply, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: lendingMarket, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: lendingMarketAuthority, 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 },
|
||||||
|
|
||||||
|
// optionals
|
||||||
|
{ pubkey: dexMarket, isSigner: false, isWritable: false },
|
||||||
|
];
|
||||||
|
return new TransactionInstruction({
|
||||||
|
keys,
|
||||||
|
programId: LENDING_PROGRAM_ID,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Deposit liquidity into a reserve. The output is a collateral token representing ownership
|
||||||
|
/// of the reserve liquidity pool.
|
||||||
|
///
|
||||||
|
/// 0. `[writable]` Liquidity input SPL Token account. $authority can transfer $liquidity_amount
|
||||||
|
/// 1. `[writable]` Collateral output SPL Token account,
|
||||||
|
/// 2. `[writable]` Reserve account.
|
||||||
|
/// 3. `[writable]` Reserve liquidity supply SPL Token account.
|
||||||
|
/// 4. `[writable]` Reserve collateral SPL Token mint.
|
||||||
|
/// 5. `[]` Derived lending market authority ($authority).
|
||||||
|
/// 6. `[]` Clock sysvar
|
||||||
|
/// 7. '[]` Token program id
|
||||||
|
export const depositInstruction = (
|
||||||
|
liquidityAmount: number | BN,
|
||||||
|
from: PublicKey, // Liquidity input SPL Token account. $authority can transfer $liquidity_amount
|
||||||
|
to: PublicKey, // Collateral output SPL Token account,
|
||||||
|
reserveAuthority: PublicKey,
|
||||||
|
reserveAccount: PublicKey,
|
||||||
|
reserveSupply: PublicKey,
|
||||||
|
collateralMint: PublicKey,
|
||||||
|
): TransactionInstruction => {
|
||||||
|
const dataLayout = BufferLayout.struct([
|
||||||
|
BufferLayout.u8("instruction"),
|
||||||
|
Layout.uint64("liquidityAmount"),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const data = Buffer.alloc(dataLayout.span);
|
||||||
|
dataLayout.encode(
|
||||||
|
{
|
||||||
|
instruction: 2, // Deposit instruction
|
||||||
|
liquidityAmount: new BN(liquidityAmount),
|
||||||
|
},
|
||||||
|
data
|
||||||
|
);
|
||||||
|
|
||||||
|
const keys = [
|
||||||
|
{ pubkey: from, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: to, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: reserveAccount, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: reserveSupply, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: collateralMint, isSigner: false, isWritable: true },
|
||||||
|
{ pubkey: reserveAuthority, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
|
||||||
|
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
||||||
|
];
|
||||||
|
return new TransactionInstruction({
|
||||||
|
keys,
|
||||||
|
programId: LENDING_PROGRAM_ID,
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
@ -46,10 +46,11 @@ export const uint64 = (property = "uint64"): unknown => {
|
||||||
|
|
||||||
layout.encode = (num: BN, buffer: Buffer, offset: number) => {
|
layout.encode = (num: BN, buffer: Buffer, offset: number) => {
|
||||||
const a = num.toArray().reverse();
|
const a = num.toArray().reverse();
|
||||||
const b = Buffer.from(a);
|
let b = Buffer.from(a);
|
||||||
if (b.length !== 8) {
|
if (b.length !== 8) {
|
||||||
const zeroPad = Buffer.alloc(8);
|
const zeroPad = Buffer.alloc(8);
|
||||||
b.copy(zeroPad);
|
b.copy(zeroPad);
|
||||||
|
b = zeroPad;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _encode(b, buffer, offset);
|
return _encode(b, buffer, offset);
|
||||||
|
|
|
@ -1,28 +1,79 @@
|
||||||
import React, { useMemo } from "react";
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { useLendingReserve, useTokenName, useUserAccounts, useUserBalance } from './../../../hooks';
|
import { useLendingReserve, useTokenName, useUserAccounts, useUserBalance } from './../../../hooks';
|
||||||
import { LendingReserve, LendingReserveParser } from "../../../models/lending";
|
import { LendingReserve, LendingReserveParser } from "../../../models/lending";
|
||||||
import { TokenIcon } from "../../../components/TokenIcon";
|
import { TokenIcon } from "../../../components/TokenIcon";
|
||||||
import { formatNumber } from "../../../utils/utils";
|
import { formatNumber } from "../../../utils/utils";
|
||||||
import { Button } from "antd";
|
import { Button } from "antd";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { useAccount } from "../../../contexts/accounts";
|
import { cache, useAccount } from "../../../contexts/accounts";
|
||||||
|
import { NumericInput } from "../../../components/Input/numeric";
|
||||||
|
import { useConnection } from "../../../contexts/connection";
|
||||||
|
import { useWallet } from "../../../contexts/wallet";
|
||||||
|
import { deposit } from './../../../actions/deposit';
|
||||||
|
|
||||||
export const DepositAddView = () => {
|
export const DepositAddView = () => {
|
||||||
|
const connection = useConnection();
|
||||||
|
const { wallet } = useWallet();
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
|
const [value, setValue] = useState('');
|
||||||
const lendingReserve = useLendingReserve(id);
|
const lendingReserve = useLendingReserve(id);
|
||||||
const reserve = lendingReserve?.info;
|
const reserve = lendingReserve?.info;
|
||||||
|
|
||||||
const name = useTokenName(reserve?.liquidityMint);
|
|
||||||
const tokenBalance = useUserBalance(reserve?.liquidityMint);
|
|
||||||
const collateralBalance = useUserBalance(reserve?.collateralMint);
|
|
||||||
|
|
||||||
return <div style={{ display: 'flex', justifyContent: 'space-around' }}>
|
const name = useTokenName(reserve?.liquidityMint);
|
||||||
|
const { balance: tokenBalance, accounts: fromAccounts } = useUserBalance(reserve?.liquidityMint);
|
||||||
|
// const collateralBalance = useUserBalance(reserve?.collateralMint);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const reserve = lendingReserve?.info;
|
||||||
|
if(!reserve) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`utlization: ${reserve.maxUtilizationRate}`)
|
||||||
|
console.log(`lendingMarket: ${reserve.lendingMarket.toBase58()}`);
|
||||||
|
|
||||||
|
const lendingMarket = await cache.get(reserve.lendingMarket);
|
||||||
|
console.log(`lendingMarket quote: ${lendingMarket?.info.quoteMint.toBase58()}`);
|
||||||
|
|
||||||
|
console.log(`liquiditySupply: ${reserve.liquiditySupply.toBase58()}`);
|
||||||
|
console.log(`liquidityMint: ${reserve.liquidityMint.toBase58()}`);
|
||||||
|
console.log(`collateralSupply: ${reserve.collateralSupply.toBase58()}`);
|
||||||
|
console.log(`collateralMint: ${reserve.collateralMint.toBase58()}`);
|
||||||
|
})();
|
||||||
|
}, [lendingReserve])
|
||||||
|
|
||||||
|
const onDeposit = useCallback(() => {
|
||||||
|
if(!lendingReserve || !reserve) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
deposit(
|
||||||
|
fromAccounts[0],
|
||||||
|
parseFloat(value),
|
||||||
|
reserve,
|
||||||
|
lendingReserve.pubkey,
|
||||||
|
connection,
|
||||||
|
wallet);
|
||||||
|
}, [value, reserve, fromAccounts]);
|
||||||
|
|
||||||
|
return <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around' }}>
|
||||||
<TokenIcon mintAddress={reserve?.liquidityMint} />
|
<TokenIcon mintAddress={reserve?.liquidityMint} />
|
||||||
{name}
|
<NumericInput value={value}
|
||||||
<div>{formatNumber.format(tokenBalance)} {name}</div>
|
onChange={(val: any) => {
|
||||||
<div>{formatNumber.format(collateralBalance)} {name}</div>
|
setValue(val);
|
||||||
<div>--</div>
|
}}
|
||||||
<Button>Deposit</Button>
|
style={{
|
||||||
|
fontSize: 20,
|
||||||
|
boxShadow: "none",
|
||||||
|
borderColor: "transparent",
|
||||||
|
outline: "transpaernt",
|
||||||
|
}}
|
||||||
|
placeholder="0.00"
|
||||||
|
/>
|
||||||
|
<div>{name}</div>
|
||||||
|
|
||||||
|
<Button onClick={onDeposit} disabled={fromAccounts.length === 0}>Deposit</Button>
|
||||||
|
|
||||||
ADD: {id}
|
ADD: {id}
|
||||||
</div>;
|
</div>;
|
||||||
|
|
|
@ -9,8 +9,8 @@ import { PublicKey } from "@solana/web3.js";
|
||||||
|
|
||||||
export const ReserveItem = (props: { reserve: LendingReserve, address: PublicKey }) => {
|
export const ReserveItem = (props: { reserve: LendingReserve, address: PublicKey }) => {
|
||||||
const name = useTokenName(props.reserve.liquidityMint);
|
const name = useTokenName(props.reserve.liquidityMint);
|
||||||
const tokenBalance = useUserBalance(props.reserve.liquidityMint);
|
const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
|
||||||
const collateralBalance = useUserBalance(props.reserve.collateralMint);
|
const { balance: collateralBalance } = useUserBalance(props.reserve.collateralMint);
|
||||||
|
|
||||||
return <div style={{ display: 'flex', justifyContent: 'space-around' }}>
|
return <div style={{ display: 'flex', justifyContent: 'space-around' }}>
|
||||||
<span style={{ display: 'flex' }}><TokenIcon mintAddress={props.reserve.liquidityMint} />{name}</span>
|
<span style={{ display: 'flex' }}><TokenIcon mintAddress={props.reserve.liquidityMint} />{name}</span>
|
||||||
|
|
|
@ -10,8 +10,8 @@ import { useAccount, useMint } from "../../contexts/accounts";
|
||||||
|
|
||||||
export const LendingReserveItem = (props: { reserve: LendingReserve, address: PublicKey }) => {
|
export const LendingReserveItem = (props: { reserve: LendingReserve, address: PublicKey }) => {
|
||||||
const name = useTokenName(props.reserve.liquidityMint);
|
const name = useTokenName(props.reserve.liquidityMint);
|
||||||
const tokenBalance = useUserBalance(props.reserve.liquidityMint);
|
const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
|
||||||
const collateralBalance = useUserBalance(props.reserve.collateralMint);
|
const { balance: collateralBalance } = useUserBalance(props.reserve.collateralMint);
|
||||||
|
|
||||||
const collateralSupply = useAccount(props.reserve.collateralSupply);
|
const collateralSupply = useAccount(props.reserve.collateralSupply);
|
||||||
const liquiditySupply = useAccount(props.reserve.liquiditySupply);
|
const liquiditySupply = useAccount(props.reserve.liquiditySupply);
|
||||||
|
|
|
@ -14,8 +14,8 @@ export const ReserveView = () => {
|
||||||
const reserve = lendingReserve?.info;
|
const reserve = lendingReserve?.info;
|
||||||
|
|
||||||
const name = useTokenName(reserve?.liquidityMint);
|
const name = useTokenName(reserve?.liquidityMint);
|
||||||
const tokenBalance = useUserBalance(reserve?.liquidityMint);
|
const { balance: tokenBalance } = useUserBalance(reserve?.liquidityMint);
|
||||||
const collateralBalance = useUserBalance(reserve?.collateralMint);
|
const { balance: collateralBalance } = useUserBalance(reserve?.collateralMint);
|
||||||
|
|
||||||
return <div style={{ display: 'flex', justifyContent: 'space-around' }}>
|
return <div style={{ display: 'flex', justifyContent: 'space-around' }}>
|
||||||
<TokenIcon mintAddress={reserve?.liquidityMint} />
|
<TokenIcon mintAddress={reserve?.liquidityMint} />
|
||||||
|
|
Loading…
Reference in New Issue