mirror of https://github.com/certusone/oyster.git
chore: prettier
This commit is contained in:
parent
547ab00d7b
commit
5ea0434a68
|
@ -2,11 +2,8 @@ import React from "react";
|
|||
import "./App.less";
|
||||
import { Routes } from "./routes";
|
||||
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<Routes />
|
||||
);
|
||||
return <Routes />;
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
import { AccountLayout, Token } from "@solana/spl-token";
|
||||
import { Account, PublicKey, SystemProgram, TransactionInstruction } from "@solana/web3.js";
|
||||
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';
|
||||
import { cache, TokenAccountParser } from "./../contexts/accounts";
|
||||
|
||||
export function ensureSplAccount(
|
||||
instructions: TransactionInstruction[],
|
||||
|
@ -20,7 +25,8 @@ export function ensureSplAccount(
|
|||
instructions,
|
||||
payer,
|
||||
amount,
|
||||
signers);
|
||||
signers
|
||||
);
|
||||
|
||||
instructions.push(
|
||||
Token.createInitAccountInstruction(
|
||||
|
@ -50,7 +56,8 @@ export function createTempMemoryAccount(
|
|||
instructions: TransactionInstruction[],
|
||||
payer: PublicKey,
|
||||
signers: Account[],
|
||||
space = DEFAULT_TEMP_MEM_SPACE) {
|
||||
space = DEFAULT_TEMP_MEM_SPACE
|
||||
) {
|
||||
const account = new Account();
|
||||
instructions.push(
|
||||
SystemProgram.createAccount({
|
||||
|
@ -68,12 +75,12 @@ export function createTempMemoryAccount(
|
|||
return account.publicKey;
|
||||
}
|
||||
|
||||
|
||||
export function createUninitializedAccount(
|
||||
instructions: TransactionInstruction[],
|
||||
payer: PublicKey,
|
||||
amount: number,
|
||||
signers: Account[]) {
|
||||
signers: Account[]
|
||||
) {
|
||||
const account = new Account();
|
||||
instructions.push(
|
||||
SystemProgram.createAccount({
|
||||
|
@ -96,21 +103,17 @@ export function createTokenAccount(
|
|||
accountRentExempt: number,
|
||||
mint: PublicKey,
|
||||
owner: PublicKey,
|
||||
signers: Account[],
|
||||
signers: Account[]
|
||||
) {
|
||||
const account = createUninitializedAccount(
|
||||
instructions,
|
||||
payer,
|
||||
accountRentExempt,
|
||||
signers);
|
||||
signers
|
||||
);
|
||||
|
||||
instructions.push(
|
||||
Token.createInitAccountInstruction(
|
||||
TOKEN_PROGRAM_ID,
|
||||
mint,
|
||||
account,
|
||||
owner
|
||||
)
|
||||
Token.createInitAccountInstruction(TOKEN_PROGRAM_ID, mint, account, owner)
|
||||
);
|
||||
|
||||
return account;
|
||||
|
@ -128,8 +131,9 @@ export function findOrCreateAccountByMint(
|
|||
excluded?: Set<string>
|
||||
): PublicKey {
|
||||
const accountToFind = mint.toBase58();
|
||||
const account = cache.byParser(TokenAccountParser)
|
||||
.map(id => cache.get(id))
|
||||
const account = cache
|
||||
.byParser(TokenAccountParser)
|
||||
.map((id) => cache.get(id))
|
||||
.find(
|
||||
(acc) =>
|
||||
acc !== undefined &&
|
||||
|
@ -150,7 +154,7 @@ export function findOrCreateAccountByMint(
|
|||
accountRentExempt,
|
||||
mint,
|
||||
owner,
|
||||
signers,
|
||||
signers
|
||||
);
|
||||
|
||||
if (isWrappedSol) {
|
||||
|
|
|
@ -9,11 +9,20 @@ import { notify } from "../utils/notifications";
|
|||
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 { createTempMemoryAccount, createUninitializedAccount, ensureSplAccount, findOrCreateAccountByMint } from "./account";
|
||||
import {
|
||||
createTempMemoryAccount,
|
||||
createUninitializedAccount,
|
||||
ensureSplAccount,
|
||||
findOrCreateAccountByMint,
|
||||
} from "./account";
|
||||
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 { DexMarketParser } from "../models/dex";
|
||||
|
||||
export const borrow = async (
|
||||
from: TokenAccount,
|
||||
|
@ -26,8 +35,8 @@ export const borrow = async (
|
|||
depositReserveAddress: PublicKey,
|
||||
|
||||
connection: Connection,
|
||||
wallet: any) => {
|
||||
|
||||
wallet: any
|
||||
) => {
|
||||
notify({
|
||||
message: "Borrowing funds...",
|
||||
description: "Please review transactions to approve.",
|
||||
|
@ -48,23 +57,21 @@ export const borrow = async (
|
|||
await connection.getMinimumBalanceForRentExemption(
|
||||
LendingObligationLayout.span
|
||||
),
|
||||
signers,
|
||||
signers
|
||||
);
|
||||
|
||||
const obligationMint = createUninitializedAccount(
|
||||
instructions,
|
||||
wallet.publicKey,
|
||||
await connection.getMinimumBalanceForRentExemption(
|
||||
MintLayout.span
|
||||
),
|
||||
signers,
|
||||
await connection.getMinimumBalanceForRentExemption(MintLayout.span),
|
||||
signers
|
||||
);
|
||||
|
||||
const obligationTokenOutput = createUninitializedAccount(
|
||||
instructions,
|
||||
wallet.publicKey,
|
||||
accountRentExempt,
|
||||
signers,
|
||||
signers
|
||||
);
|
||||
|
||||
let toAccount = await findOrCreateAccountByMint(
|
||||
|
@ -78,7 +85,9 @@ export const borrow = async (
|
|||
);
|
||||
|
||||
// create all accounts in one transaction
|
||||
let tx = await sendTransaction(connection, wallet, instructions, [...signers]);
|
||||
let tx = await sendTransaction(connection, wallet, instructions, [
|
||||
...signers,
|
||||
]);
|
||||
|
||||
notify({
|
||||
message: "Obligation accounts created",
|
||||
|
@ -101,7 +110,11 @@ export const borrow = async (
|
|||
LENDING_PROGRAM_ID
|
||||
);
|
||||
|
||||
const mint = (await cache.query(connection, depositReserve.collateralMint, 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(
|
||||
|
@ -121,27 +134,33 @@ export const borrow = async (
|
|||
authority,
|
||||
wallet.publicKey,
|
||||
[],
|
||||
amountLamports,
|
||||
amountLamports
|
||||
)
|
||||
);
|
||||
|
||||
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 = borrowReserve.dexMarketOption
|
||||
? borrowReserve.dexMarket
|
||||
: depositReserve.dexMarket;
|
||||
const dexMarket = cache.get(dexMarketAddress);
|
||||
|
||||
if (!dexMarket) {
|
||||
throw new Error(`Dex market doesn't exsists.`)
|
||||
throw new Error(`Dex market doesn't exsists.`);
|
||||
}
|
||||
|
||||
const dexOrderBookSide = market.info.quoteMint.equals(depositReserve.liquidityMint) ?
|
||||
dexMarket?.info.bids :
|
||||
dexMarket?.info.asks
|
||||
const dexOrderBookSide = market.info.quoteMint.equals(
|
||||
depositReserve.liquidityMint
|
||||
)
|
||||
? dexMarket?.info.bids
|
||||
: dexMarket?.info.asks;
|
||||
|
||||
const memory = createTempMemoryAccount(
|
||||
instructions,
|
||||
wallet.publicKey,
|
||||
signers,
|
||||
signers
|
||||
);
|
||||
|
||||
// deposit
|
||||
|
@ -165,7 +184,7 @@ export const borrow = async (
|
|||
dexMarketAddress,
|
||||
dexOrderBookSide,
|
||||
|
||||
memory,
|
||||
memory
|
||||
)
|
||||
);
|
||||
try {
|
||||
|
@ -185,4 +204,4 @@ export const borrow = async (
|
|||
} catch {
|
||||
// TODO:
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -6,10 +6,18 @@ import {
|
|||
} from "@solana/web3.js";
|
||||
import { sendTransaction } from "../contexts/connection";
|
||||
import { notify } from "../utils/notifications";
|
||||
import { depositInstruction, initReserveInstruction, LendingReserve } from "./../models/lending/reserve";
|
||||
import {
|
||||
depositInstruction,
|
||||
initReserveInstruction,
|
||||
LendingReserve,
|
||||
} from "./../models/lending/reserve";
|
||||
import { AccountLayout, MintInfo, Token } from "@solana/spl-token";
|
||||
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids";
|
||||
import { createUninitializedAccount, ensureSplAccount, findOrCreateAccountByMint } from "./account";
|
||||
import {
|
||||
createUninitializedAccount,
|
||||
ensureSplAccount,
|
||||
findOrCreateAccountByMint,
|
||||
} from "./account";
|
||||
import { cache, MintParser, ParsedAccount } from "../contexts/accounts";
|
||||
import { TokenAccount } from "../models";
|
||||
import { toLamports } from "../utils/utils";
|
||||
|
@ -20,8 +28,8 @@ export const deposit = async (
|
|||
reserve: LendingReserve,
|
||||
reserveAddress: PublicKey,
|
||||
connection: Connection,
|
||||
wallet: any) => {
|
||||
|
||||
wallet: any
|
||||
) => {
|
||||
// TODO: customize ?
|
||||
const MAX_UTILIZATION_RATE = 80;
|
||||
|
||||
|
@ -47,7 +55,11 @@ export const deposit = async (
|
|||
LENDING_PROGRAM_ID
|
||||
);
|
||||
|
||||
const mint = (await cache.query(connection, reserve.liquidityMint, MintParser)) as ParsedAccount<MintInfo>;
|
||||
const mint = (await cache.query(
|
||||
connection,
|
||||
reserve.liquidityMint,
|
||||
MintParser
|
||||
)) as ParsedAccount<MintInfo>;
|
||||
const amountLamports = toLamports(amount, mint?.info);
|
||||
|
||||
const fromAccount = ensureSplAccount(
|
||||
|
@ -67,7 +79,7 @@ export const deposit = async (
|
|||
authority,
|
||||
wallet.publicKey,
|
||||
[],
|
||||
amountLamports,
|
||||
amountLamports
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -88,7 +100,7 @@ export const deposit = async (
|
|||
instructions,
|
||||
wallet.publicKey,
|
||||
accountRentExempt,
|
||||
signers,
|
||||
signers
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -102,12 +114,13 @@ export const deposit = async (
|
|||
authority,
|
||||
reserveAddress,
|
||||
reserve.liquiditySupply,
|
||||
reserve.collateralMint,
|
||||
reserve.collateralMint
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// TODO: finish reserve init
|
||||
instructions.push(initReserveInstruction(
|
||||
instructions.push(
|
||||
initReserveInstruction(
|
||||
amountLamports,
|
||||
MAX_UTILIZATION_RATE,
|
||||
fromAccount,
|
||||
|
@ -119,8 +132,9 @@ export const deposit = async (
|
|||
reserve.collateralSupply,
|
||||
reserve.lendingMarket,
|
||||
authority,
|
||||
reserve.dexMarket,
|
||||
));
|
||||
reserve.dexMarket
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -140,4 +154,4 @@ export const deposit = async (
|
|||
} catch {
|
||||
// TODO:
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export { borrow } from './borrow';
|
||||
export { deposit } from './deposit';
|
||||
export { withdraw } from './withdraw';
|
||||
export * from './account';
|
||||
export { borrow } from "./borrow";
|
||||
export { deposit } from "./deposit";
|
||||
export { withdraw } from "./withdraw";
|
||||
export * from "./account";
|
||||
|
|
|
@ -6,7 +6,10 @@ import {
|
|||
} from "@solana/web3.js";
|
||||
import { sendTransaction } from "../contexts/connection";
|
||||
import { notify } from "../utils/notifications";
|
||||
import { LendingReserve, withdrawInstruction } from "./../models/lending/reserve";
|
||||
import {
|
||||
LendingReserve,
|
||||
withdrawInstruction,
|
||||
} from "./../models/lending/reserve";
|
||||
import { AccountLayout, Token } from "@solana/spl-token";
|
||||
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids";
|
||||
import { findOrCreateAccountByMint } from "./account";
|
||||
|
@ -18,8 +21,8 @@ export const withdraw = async (
|
|||
reserve: LendingReserve,
|
||||
reserveAddress: PublicKey,
|
||||
connection: Connection,
|
||||
wallet: any) => {
|
||||
|
||||
wallet: any
|
||||
) => {
|
||||
notify({
|
||||
message: "Withdrawing funds...",
|
||||
description: "Please review transactions to approve.",
|
||||
|
@ -50,7 +53,7 @@ export const withdraw = async (
|
|||
authority,
|
||||
wallet.publicKey,
|
||||
[],
|
||||
amountLamports,
|
||||
amountLamports
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -73,7 +76,7 @@ export const withdraw = async (
|
|||
reserveAddress,
|
||||
reserve.collateralMint,
|
||||
reserve.liquiditySupply,
|
||||
authority,
|
||||
authority
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -94,4 +97,4 @@ export const withdraw = async (
|
|||
} catch {
|
||||
// TODO:
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import { useLendingReserves, useTokenName, useUserBalance } from '../../hooks';
|
||||
import { useLendingReserves, useTokenName, useUserBalance } from "../../hooks";
|
||||
import { LendingReserve, LendingReserveParser } from "../../models";
|
||||
import { TokenIcon } from "../TokenIcon";
|
||||
import { getTokenName } from "../../utils/utils";
|
||||
|
@ -8,9 +8,9 @@ import { cache, ParsedAccount } from "../../contexts/accounts";
|
|||
import { NumericInput } from "../Input/numeric";
|
||||
import { useConnection, useConnectionConfig } from "../../contexts/connection";
|
||||
import { useWallet } from "../../contexts/wallet";
|
||||
import { borrow } from '../../actions';
|
||||
import { borrow } from "../../actions";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import './style.less';
|
||||
import "./style.less";
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
|
@ -22,7 +22,8 @@ const CollateralSelector = (props: {
|
|||
const { reserveAccounts } = useLendingReserves();
|
||||
const { tokenMap } = useConnectionConfig();
|
||||
|
||||
return <Select
|
||||
return (
|
||||
<Select
|
||||
size="large"
|
||||
showSearch
|
||||
style={{ minWidth: 120 }}
|
||||
|
@ -38,44 +39,54 @@ const CollateralSelector = (props: {
|
|||
}
|
||||
>
|
||||
{reserveAccounts
|
||||
.filter(reserve => reserve.info !== props.reserve)
|
||||
.map(reserve => {
|
||||
.filter((reserve) => reserve.info !== props.reserve)
|
||||
.map((reserve) => {
|
||||
const mint = reserve.info.liquidityMint.toBase58();
|
||||
const address = reserve.pubkey.toBase58();
|
||||
const name = getTokenName(tokenMap, mint);
|
||||
return <Option key={address} value={address} name={name} title={address}>
|
||||
<div key={address} style={{ display: "flex", alignItems: "center" }}>
|
||||
return (
|
||||
<Option key={address} value={address} name={name} title={address}>
|
||||
<div
|
||||
key={address}
|
||||
style={{ display: "flex", alignItems: "center" }}
|
||||
>
|
||||
<TokenIcon mintAddress={mint} />
|
||||
{name}
|
||||
</div>
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>;
|
||||
}
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
|
||||
export const BorrowInput = (props: { className?: string, reserve: LendingReserve, address: PublicKey }) => {
|
||||
export const BorrowInput = (props: {
|
||||
className?: string;
|
||||
reserve: LendingReserve;
|
||||
address: PublicKey;
|
||||
}) => {
|
||||
const connection = useConnection();
|
||||
const { wallet } = useWallet();
|
||||
const [value, setValue] = useState('');
|
||||
const [value, setValue] = useState("");
|
||||
|
||||
const borrowReserve = props.reserve;
|
||||
const borrowReserveAddress = props.address;
|
||||
|
||||
const [collateralReserveMint, setCollateralReserveMint] = useState<string>();
|
||||
|
||||
|
||||
const collateralReserve = useMemo(() => {
|
||||
const id: string = cache.byParser(LendingReserveParser)
|
||||
.find(acc => acc === collateralReserveMint) || '';
|
||||
const id: string =
|
||||
cache
|
||||
.byParser(LendingReserveParser)
|
||||
.find((acc) => acc === collateralReserveMint) || "";
|
||||
|
||||
return cache.get(id) as ParsedAccount<LendingReserve>;
|
||||
}, [collateralReserveMint])
|
||||
|
||||
}, [collateralReserveMint]);
|
||||
|
||||
const name = useTokenName(borrowReserve?.liquidityMint);
|
||||
const {
|
||||
accounts: fromAccounts
|
||||
} = useUserBalance(collateralReserve?.info.collateralMint);
|
||||
const { accounts: fromAccounts } = useUserBalance(
|
||||
collateralReserve?.info.collateralMint
|
||||
);
|
||||
// const collateralBalance = useUserBalance(reserve?.collateralMint);
|
||||
|
||||
const onBorrow = useCallback(() => {
|
||||
|
@ -91,26 +102,42 @@ export const BorrowInput = (props: { className?: string, reserve: LendingReserve
|
|||
collateralReserve.info,
|
||||
collateralReserve.pubkey,
|
||||
connection,
|
||||
wallet);
|
||||
}, [value, borrowReserve, fromAccounts, borrowReserveAddress]);
|
||||
wallet
|
||||
);
|
||||
}, [
|
||||
connection,
|
||||
wallet,
|
||||
value,
|
||||
collateralReserve,
|
||||
borrowReserve,
|
||||
fromAccounts,
|
||||
borrowReserveAddress,
|
||||
]);
|
||||
|
||||
const bodyStyle: React.CSSProperties = {
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: "100%",
|
||||
};
|
||||
|
||||
return <Card className={props.className} bodyStyle={bodyStyle}>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around' }}>
|
||||
return (
|
||||
<Card className={props.className} bodyStyle={bodyStyle}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
<div className="borrow-input-title">
|
||||
How much would you like to borrow?
|
||||
</div>
|
||||
<div className="token-input">
|
||||
<TokenIcon mintAddress={borrowReserve?.liquidityMint} />
|
||||
<NumericInput value={value}
|
||||
<NumericInput
|
||||
value={value}
|
||||
onChange={(val: any) => {
|
||||
setValue(val);
|
||||
}}
|
||||
|
@ -125,16 +152,21 @@ export const BorrowInput = (props: { className?: string, reserve: LendingReserve
|
|||
/>
|
||||
<div>{name}</div>
|
||||
</div>
|
||||
<div className="borrow-input-title">
|
||||
Select collateral account?
|
||||
</div>
|
||||
<div className="borrow-input-title">Select collateral account?</div>
|
||||
<CollateralSelector
|
||||
reserve={borrowReserve}
|
||||
mint={collateralReserveMint}
|
||||
onMintChange={setCollateralReserveMint}
|
||||
/>
|
||||
|
||||
<Button type="primary" onClick={onBorrow} disabled={fromAccounts.length === 0}>Borrow</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={onBorrow}
|
||||
disabled={fromAccounts.length === 0}
|
||||
>
|
||||
Borrow
|
||||
</Button>
|
||||
</div>
|
||||
</Card >;
|
||||
}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -18,13 +18,13 @@ export const CurrentUserBadge = (props: {}) => {
|
|||
return (
|
||||
<div className="wallet-wrapper">
|
||||
<span>
|
||||
{formatNumber.format(((account?.lamports || 0) / LAMPORTS_PER_SOL))} SOL
|
||||
{formatNumber.format((account?.lamports || 0) / LAMPORTS_PER_SOL)} SOL
|
||||
</span>
|
||||
<div className="wallet-key">
|
||||
{shortenAddress(`${wallet.publicKey}`)}
|
||||
<Identicon
|
||||
address={wallet.publicKey.toBase58()}
|
||||
style={{ marginLeft: "0.5rem", display: 'flex' }}
|
||||
address={wallet.publicKey?.toBase58()}
|
||||
style={{ marginLeft: "0.5rem", display: "flex" }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,31 +1,45 @@
|
|||
import React, { } from "react";
|
||||
import { useTokenName, useUserBalance, useCollateralBalance } from './../../hooks';
|
||||
import React from "react";
|
||||
import {
|
||||
useTokenName,
|
||||
useUserBalance,
|
||||
useCollateralBalance,
|
||||
} from "./../../hooks";
|
||||
import { LendingReserve } from "../../models/lending";
|
||||
import { formatNumber } from "../../utils/utils";
|
||||
import { Card } from "antd";
|
||||
import './style.less';
|
||||
import "./style.less";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export const DepositInfoLine = (props: {
|
||||
className?: string,
|
||||
reserve: LendingReserve,
|
||||
address: PublicKey }) => {
|
||||
className?: string;
|
||||
reserve: LendingReserve;
|
||||
address: PublicKey;
|
||||
}) => {
|
||||
const name = useTokenName(props.reserve.liquidityMint);
|
||||
const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
|
||||
const { balance: collateralBalance } = useCollateralBalance(props.reserve);
|
||||
|
||||
return <Card className={props.className} bodyStyle={{ display: 'flex', justifyContent: 'space-around', }} >
|
||||
return (
|
||||
<Card
|
||||
className={props.className}
|
||||
bodyStyle={{ display: "flex", justifyContent: "space-around" }}
|
||||
>
|
||||
<div className="deposit-info-line-item ">
|
||||
<div>Your balance in Oyster</div>
|
||||
<div>{formatNumber.format(collateralBalance)} {name}</div>
|
||||
<div>
|
||||
{formatNumber.format(collateralBalance)} {name}
|
||||
</div>
|
||||
</div>
|
||||
<div className="deposit-info-line-item ">
|
||||
<div>Your wallet balance</div>
|
||||
<div>{formatNumber.format(tokenBalance)} {name}</div>
|
||||
<div>
|
||||
{formatNumber.format(tokenBalance)} {name}
|
||||
</div>
|
||||
</div>
|
||||
<div className="deposit-info-line-item ">
|
||||
<div>Health factor</div>
|
||||
<div>--</div>
|
||||
</div>
|
||||
</Card>
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,19 +1,23 @@
|
|||
import React, { useCallback, useState } from "react";
|
||||
import { useTokenName, useUserBalance } from '../../hooks';
|
||||
import { useTokenName, useUserBalance } from "../../hooks";
|
||||
import { LendingReserve } from "../../models/lending";
|
||||
import { TokenIcon } from "../TokenIcon";
|
||||
import { Button, Card } from "antd";
|
||||
import { NumericInput } from "../Input/numeric";
|
||||
import { useConnection } from "../../contexts/connection";
|
||||
import { useWallet } from "../../contexts/wallet";
|
||||
import { deposit } from '../../actions/deposit';
|
||||
import { deposit } from "../../actions/deposit";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import './style.less';
|
||||
import "./style.less";
|
||||
|
||||
export const DepositInput = (props: { className?: string, reserve: LendingReserve, address: PublicKey }) => {
|
||||
export const DepositInput = (props: {
|
||||
className?: string;
|
||||
reserve: LendingReserve;
|
||||
address: PublicKey;
|
||||
}) => {
|
||||
const connection = useConnection();
|
||||
const { wallet } = useWallet();
|
||||
const [value, setValue] = useState('');
|
||||
const [value, setValue] = useState("");
|
||||
|
||||
const reserve = props.reserve;
|
||||
const address = props.address;
|
||||
|
@ -29,26 +33,34 @@ export const DepositInput = (props: { className?: string, reserve: LendingReserv
|
|||
reserve,
|
||||
address,
|
||||
connection,
|
||||
wallet);
|
||||
}, [value, reserve, fromAccounts, address]);
|
||||
wallet
|
||||
);
|
||||
}, [connection, wallet, value, reserve, fromAccounts, address]);
|
||||
|
||||
const bodyStyle: React.CSSProperties = {
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: "100%",
|
||||
};
|
||||
|
||||
return <Card className={props.className} bodyStyle={bodyStyle}>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around' }}>
|
||||
return (
|
||||
<Card className={props.className} bodyStyle={bodyStyle}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
<div className="deposit-input-title">
|
||||
How much would you like to deposit?
|
||||
</div>
|
||||
<div className="token-input">
|
||||
<TokenIcon mintAddress={reserve?.liquidityMint} />
|
||||
<NumericInput value={value}
|
||||
<NumericInput
|
||||
value={value}
|
||||
onChange={(val: any) => {
|
||||
setValue(val);
|
||||
}}
|
||||
|
@ -64,7 +76,14 @@ export const DepositInput = (props: { className?: string, reserve: LendingReserv
|
|||
<div>{name}</div>
|
||||
</div>
|
||||
|
||||
<Button type="primary" onClick={onDeposit} disabled={fromAccounts.length === 0}>Deposit</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={onDeposit}
|
||||
disabled={fromAccounts.length === 0}
|
||||
>
|
||||
Deposit
|
||||
</Button>
|
||||
</div>
|
||||
</Card >;
|
||||
}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -11,13 +11,16 @@ export const Identicon = (props: {
|
|||
className?: string;
|
||||
}) => {
|
||||
const { style, className } = props;
|
||||
const address = typeof props.address === 'string' ? props.address : props.address?.toBase58();
|
||||
const address =
|
||||
typeof props.address === "string"
|
||||
? props.address
|
||||
: props.address?.toBase58();
|
||||
const ref = useRef<HTMLDivElement>();
|
||||
|
||||
useEffect(() => {
|
||||
if (address && ref.current) {
|
||||
ref.current.innerHTML = "";
|
||||
ref.current.className = props.className || "";
|
||||
ref.current.className = className || "";
|
||||
ref.current.appendChild(
|
||||
Jazzicon(
|
||||
style?.width || 16,
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import React from "react";
|
||||
import "./../../App.less";
|
||||
import { Menu } from 'antd';
|
||||
import { Menu } from "antd";
|
||||
import {
|
||||
PieChartOutlined,
|
||||
GithubOutlined,
|
||||
BankOutlined,
|
||||
LogoutOutlined,
|
||||
HomeOutlined,
|
||||
RocketOutlined
|
||||
} from '@ant-design/icons';
|
||||
RocketOutlined,
|
||||
} from "@ant-design/icons";
|
||||
|
||||
import BasicLayout, { } from '@ant-design/pro-layout';
|
||||
import BasicLayout from "@ant-design/pro-layout";
|
||||
import { AppBar } from "./../AppBar";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import { useConnectionConfig } from "../../contexts/connection";
|
||||
|
@ -19,16 +19,17 @@ export const AppLayout = (props: any) => {
|
|||
const { env } = useConnectionConfig();
|
||||
const location = useLocation();
|
||||
|
||||
console.log(location.pathname)
|
||||
console.log(location.pathname);
|
||||
const paths: { [key: string]: string } = {
|
||||
'/dashboard': '2',
|
||||
'/deposit': '3',
|
||||
'/borrow': '4',
|
||||
'/faucet': '4',
|
||||
|
||||
"/dashboard": "2",
|
||||
"/deposit": "3",
|
||||
"/borrow": "4",
|
||||
"/faucet": "4",
|
||||
};
|
||||
|
||||
const current = [...Object.keys(paths)].find(key => location.pathname.startsWith(key)) || '';
|
||||
const current =
|
||||
[...Object.keys(paths)].find((key) => location.pathname.startsWith(key)) ||
|
||||
"";
|
||||
const defaultKey = paths[current] || "1";
|
||||
|
||||
return (
|
||||
|
@ -38,7 +39,8 @@ export const AppLayout = (props: any) => {
|
|||
Oyster Lending is unaudited software. Use at your own risk.
|
||||
</div>
|
||||
</div>
|
||||
<BasicLayout title="Oyster Lending"
|
||||
<BasicLayout
|
||||
title="Oyster Lending"
|
||||
navTheme="realDark"
|
||||
headerTheme="dark"
|
||||
theme="dark"
|
||||
|
@ -47,10 +49,13 @@ export const AppLayout = (props: any) => {
|
|||
logo={<div className="App-logo" />}
|
||||
rightContentRender={() => <AppBar />}
|
||||
links={[
|
||||
<div title="Fork"><GithubOutlined /></div>
|
||||
<div title="Fork">
|
||||
<GithubOutlined />
|
||||
</div>,
|
||||
]}
|
||||
menuContentRender={() => {
|
||||
return <Menu theme="dark" defaultSelectedKeys={[defaultKey]} mode="inline">
|
||||
return (
|
||||
<Menu theme="dark" defaultSelectedKeys={[defaultKey]} mode="inline">
|
||||
<Menu.Item key="1" icon={<HomeOutlined />}>
|
||||
<Link
|
||||
to={{
|
||||
|
@ -87,7 +92,8 @@ export const AppLayout = (props: any) => {
|
|||
Borrow
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
{env !== "mainnet-beta" && <Menu.Item key="5" icon={<RocketOutlined />}>
|
||||
{env !== "mainnet-beta" && (
|
||||
<Menu.Item key="5" icon={<RocketOutlined />}>
|
||||
<Link
|
||||
to={{
|
||||
pathname: "/faucet",
|
||||
|
@ -95,13 +101,14 @@ export const AppLayout = (props: any) => {
|
|||
>
|
||||
Faucet
|
||||
</Link>
|
||||
</Menu.Item>}
|
||||
</Menu.Item>
|
||||
)}
|
||||
</Menu>
|
||||
);
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</BasicLayout>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -1,26 +1,35 @@
|
|||
import React, { useState } from "react";
|
||||
import React from "react";
|
||||
import { LendingReserve } from "../../models/lending";
|
||||
import { Card } from "antd";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import './style.less';
|
||||
|
||||
export const ReserveStatus = (props: { className?: string, reserve: LendingReserve, address: PublicKey }) => {
|
||||
const [] = useState('');
|
||||
import "./style.less";
|
||||
|
||||
export const ReserveStatus = (props: {
|
||||
className?: string;
|
||||
reserve: LendingReserve;
|
||||
address: PublicKey;
|
||||
}) => {
|
||||
const bodyStyle: React.CSSProperties = {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
};
|
||||
|
||||
return <Card className={props.className}
|
||||
title={
|
||||
<>Reserve Status & Configuration</>
|
||||
}
|
||||
bodyStyle={bodyStyle}>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around' }}>
|
||||
return (
|
||||
<Card
|
||||
className={props.className}
|
||||
title={<>Reserve Status & Configuration</>}
|
||||
bodyStyle={bodyStyle}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
TODO: Reserve Status - add chart
|
||||
</div>
|
||||
</Card >;
|
||||
}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import { useTokenName } from './../../hooks';
|
||||
import { useTokenName } from "./../../hooks";
|
||||
import { LendingReserve } from "../../models/lending";
|
||||
import { TokenIcon } from "../../components/TokenIcon";
|
||||
import { formatNumber, formatPct, fromLamports } from "../../utils/utils";
|
||||
|
@ -10,22 +10,25 @@ import { PublicKey } from "@solana/web3.js";
|
|||
const { Text } = Typography;
|
||||
|
||||
export enum SideReserveOverviewMode {
|
||||
Deposit = 'deposit',
|
||||
Borrow = 'borrow'
|
||||
Deposit = "deposit",
|
||||
Borrow = "borrow",
|
||||
}
|
||||
|
||||
export const SideReserveOverview = (props: {
|
||||
className?: string;
|
||||
reserve: LendingReserve,
|
||||
address: PublicKey,
|
||||
mode: SideReserveOverviewMode
|
||||
reserve: LendingReserve;
|
||||
address: PublicKey;
|
||||
mode: SideReserveOverviewMode;
|
||||
}) => {
|
||||
const reserve = props.reserve;
|
||||
const mode = props.mode;
|
||||
const name = useTokenName(reserve?.liquidityMint);
|
||||
const liquidityMint = useMint(props.reserve.liquidityMint);
|
||||
|
||||
const totalLiquidity = fromLamports(props.reserve.totalLiquidity.toNumber(), liquidityMint);
|
||||
const totalLiquidity = fromLamports(
|
||||
props.reserve.totalLiquidity.toNumber(),
|
||||
liquidityMint
|
||||
);
|
||||
|
||||
// TODO: calculate
|
||||
const depositApy = 0.048;
|
||||
|
@ -38,24 +41,20 @@ export const SideReserveOverview = (props: {
|
|||
|
||||
let extraInfo: JSX.Element | null = null;
|
||||
if (mode === SideReserveOverviewMode.Deposit) {
|
||||
|
||||
extraInfo = <>
|
||||
extraInfo = (
|
||||
<>
|
||||
<div className="card-row">
|
||||
<Text type="secondary" className="card-cell ">
|
||||
Deposit APY:
|
||||
</Text>
|
||||
<div className="card-cell ">
|
||||
{formatPct.format(depositApy)}
|
||||
</div>
|
||||
<div className="card-cell ">{formatPct.format(depositApy)}</div>
|
||||
</div>
|
||||
|
||||
<div className="card-row">
|
||||
<Text type="secondary" className="card-cell ">
|
||||
Maxiumum LTV:
|
||||
</Text>
|
||||
<div className="card-cell ">
|
||||
{formatPct.format(maxLTV)}
|
||||
</div>
|
||||
<div className="card-cell ">{formatPct.format(maxLTV)}</div>
|
||||
</div>
|
||||
|
||||
<div className="card-row">
|
||||
|
@ -75,32 +74,46 @@ export const SideReserveOverview = (props: {
|
|||
{formatPct.format(liquidiationPenalty)}
|
||||
</div>
|
||||
</div>
|
||||
</>;
|
||||
</>
|
||||
);
|
||||
} else if (mode === SideReserveOverviewMode.Borrow) {
|
||||
extraInfo = <>
|
||||
extraInfo = (
|
||||
<>
|
||||
<div className="card-row">
|
||||
<Text type="secondary" className="card-cell ">
|
||||
Borrow APY:
|
||||
</Text>
|
||||
<div className="card-cell ">
|
||||
{formatPct.format(borrowApy)}
|
||||
<div className="card-cell ">{formatPct.format(borrowApy)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</>;
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return <Card className={props.className} title={
|
||||
<div style={{ display: 'flex', alignItems: 'center', fontSize: '1.2rem', justifyContent: 'center' }}>
|
||||
<TokenIcon mintAddress={reserve?.liquidityMint} style={{ width: 30, height: 30 }} /> {name} Reserve Overview
|
||||
return (
|
||||
<Card
|
||||
className={props.className}
|
||||
title={
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
fontSize: "1.2rem",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<TokenIcon
|
||||
mintAddress={reserve?.liquidityMint}
|
||||
style={{ width: 30, height: 30 }}
|
||||
/>{" "}
|
||||
{name} Reserve Overview
|
||||
</div>
|
||||
}>
|
||||
}
|
||||
>
|
||||
<div className="card-row">
|
||||
<Text type="secondary" className="card-cell ">
|
||||
Utilization rate:
|
||||
</Text>
|
||||
<div className="card-cell ">
|
||||
{formatPct.format(utilizationRate)}
|
||||
</div>
|
||||
<div className="card-cell ">{formatPct.format(utilizationRate)}</div>
|
||||
</div>
|
||||
|
||||
<div className="card-row">
|
||||
|
@ -113,5 +126,6 @@ export const SideReserveOverview = (props: {
|
|||
</div>
|
||||
|
||||
{extraInfo}
|
||||
</Card>;
|
||||
}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -18,8 +18,8 @@ export const TokenIcon = (props: {
|
|||
alt="Token icon"
|
||||
className={props.className}
|
||||
key={icon}
|
||||
width={props.style?.width || '20'}
|
||||
height={props.style?.height || '20'}
|
||||
width={props.style?.width || "20"}
|
||||
height={props.style?.height || "20"}
|
||||
src={icon}
|
||||
style={{
|
||||
marginRight: "0.5rem",
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
import React from "react";
|
||||
import { useCollateralBalance, useTokenName, useUserBalance } from './../../hooks';
|
||||
import {
|
||||
useCollateralBalance,
|
||||
useTokenName,
|
||||
useUserBalance,
|
||||
} from "./../../hooks";
|
||||
import { LendingReserve } from "../../models/lending";
|
||||
import { formatNumber } from "../../utils/utils";
|
||||
import { Button, Card, Typography } from "antd";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useMint } from "../../contexts/accounts";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
export const UserLendingCard = (props: {
|
||||
className?: string;
|
||||
reserve: LendingReserve,
|
||||
address: PublicKey,
|
||||
reserve: LendingReserve;
|
||||
address: PublicKey;
|
||||
}) => {
|
||||
const reserve = props.reserve;
|
||||
const address = props.address;
|
||||
|
@ -22,19 +25,28 @@ export const UserLendingCard = (props: {
|
|||
const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
|
||||
const { balance: collateralBalance } = useCollateralBalance(props.reserve);
|
||||
|
||||
|
||||
// TODO: calculate
|
||||
const borrowed = 0;
|
||||
const healthFactor = '--';
|
||||
const healthFactor = "--";
|
||||
const ltv = 0;
|
||||
const available = 0;
|
||||
|
||||
|
||||
return <Card className={props.className} title={
|
||||
<div style={{ display: 'flex', alignItems: 'center', fontSize: '1.2rem', justifyContent: 'center' }}>
|
||||
return (
|
||||
<Card
|
||||
className={props.className}
|
||||
title={
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
fontSize: "1.2rem",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
Your Information
|
||||
</div>
|
||||
}>
|
||||
}
|
||||
>
|
||||
<h3>Borrows</h3>
|
||||
|
||||
<div className="card-row">
|
||||
|
@ -50,18 +62,14 @@ export const UserLendingCard = (props: {
|
|||
<Text type="secondary" className="card-cell ">
|
||||
Health factor:
|
||||
</Text>
|
||||
<div className="card-cell ">
|
||||
{healthFactor}
|
||||
</div>
|
||||
<div className="card-cell ">{healthFactor}</div>
|
||||
</div>
|
||||
|
||||
<div className="card-row">
|
||||
<Text type="secondary" className="card-cell ">
|
||||
Loan to value:
|
||||
</Text>
|
||||
<div className="card-cell ">
|
||||
{formatNumber.format(ltv)}
|
||||
</div>
|
||||
<div className="card-cell ">{formatNumber.format(ltv)}</div>
|
||||
</div>
|
||||
|
||||
<div className="card-row">
|
||||
|
@ -93,7 +101,10 @@ export const UserLendingCard = (props: {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card-row" style={{ marginTop: 20, justifyContent: 'space-evenly' }}>
|
||||
<div
|
||||
className="card-row"
|
||||
style={{ marginTop: 20, justifyContent: "space-evenly" }}
|
||||
>
|
||||
<Link to={`/deposit/${address}`}>
|
||||
<Button>Deposit</Button>
|
||||
</Link>
|
||||
|
@ -107,7 +118,6 @@ export const UserLendingCard = (props: {
|
|||
<Button disabled={true}>Repay</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
|
||||
</Card>;
|
||||
}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,30 +1,39 @@
|
|||
import React, { useCallback, useState } from "react";
|
||||
import { useCollateralBalance, useTokenName, useUserBalance } from '../../hooks';
|
||||
import {
|
||||
useCollateralBalance,
|
||||
useTokenName,
|
||||
useUserBalance,
|
||||
} from "../../hooks";
|
||||
import { LendingReserve } from "../../models/lending";
|
||||
import { TokenIcon } from "../TokenIcon";
|
||||
import { Button, Card } from "antd";
|
||||
import { NumericInput } from "../Input/numeric";
|
||||
import { useConnection } from "../../contexts/connection";
|
||||
import { useWallet } from "../../contexts/wallet";
|
||||
import { withdraw } from '../../actions';
|
||||
import { withdraw } from "../../actions";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import './style.less';
|
||||
import { useMint } from "../../contexts/accounts";
|
||||
import "./style.less";
|
||||
|
||||
export const WithdrawInput = (props: { className?: string, reserve: LendingReserve, address: PublicKey }) => {
|
||||
export const WithdrawInput = (props: {
|
||||
className?: string;
|
||||
reserve: LendingReserve;
|
||||
address: PublicKey;
|
||||
}) => {
|
||||
const connection = useConnection();
|
||||
const { wallet } = useWallet();
|
||||
const [value, setValue] = useState('');
|
||||
const [value, setValue] = useState("");
|
||||
|
||||
const reserve = props.reserve;
|
||||
const address = props.address;
|
||||
|
||||
const liquidityMint = useMint(reserve?.liquidityMint);
|
||||
const name = useTokenName(reserve?.liquidityMint);
|
||||
const { balanceLamports: collateralBalanceLamports, accounts: fromAccounts } = useUserBalance(reserve?.collateralMint);
|
||||
const {
|
||||
balance: collateralBalanceInLiquidity
|
||||
} = useCollateralBalance(reserve);
|
||||
balanceLamports: collateralBalanceLamports,
|
||||
accounts: fromAccounts,
|
||||
} = useUserBalance(reserve?.collateralMint);
|
||||
const { balance: collateralBalanceInLiquidity } = useCollateralBalance(
|
||||
reserve
|
||||
);
|
||||
|
||||
const onWithdraw = useCallback(() => {
|
||||
withdraw(
|
||||
|
@ -36,26 +45,43 @@ export const WithdrawInput = (props: { className?: string, reserve: LendingReser
|
|||
reserve,
|
||||
address,
|
||||
connection,
|
||||
wallet);
|
||||
}, [value, reserve, fromAccounts, address, liquidityMint]);
|
||||
wallet
|
||||
);
|
||||
}, [
|
||||
connection,
|
||||
wallet,
|
||||
collateralBalanceLamports,
|
||||
collateralBalanceInLiquidity,
|
||||
value,
|
||||
reserve,
|
||||
fromAccounts,
|
||||
address,
|
||||
]);
|
||||
|
||||
const bodyStyle: React.CSSProperties = {
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: "100%",
|
||||
};
|
||||
|
||||
return <Card className={props.className} bodyStyle={bodyStyle}>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around' }}>
|
||||
return (
|
||||
<Card className={props.className} bodyStyle={bodyStyle}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
}}
|
||||
>
|
||||
<div className="withdraw-input-title">
|
||||
How much would you like to withdraw?
|
||||
</div>
|
||||
<div className="token-input">
|
||||
<TokenIcon mintAddress={reserve?.liquidityMint} />
|
||||
<NumericInput value={value}
|
||||
<NumericInput
|
||||
value={value}
|
||||
onChange={(val: any) => {
|
||||
setValue(val);
|
||||
}}
|
||||
|
@ -71,7 +97,14 @@ export const WithdrawInput = (props: { className?: string, reserve: LendingReser
|
|||
<div>{name}</div>
|
||||
</div>
|
||||
|
||||
<Button type="primary" onClick={onWithdraw} disabled={fromAccounts.length === 0}>Withdraw</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={onWithdraw}
|
||||
disabled={fromAccounts.length === 0}
|
||||
>
|
||||
Withdraw
|
||||
</Button>
|
||||
</div>
|
||||
</Card >;
|
||||
}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useConnection } from "./connection";
|
|||
import { useWallet } from "./wallet";
|
||||
import { AccountInfo, Connection, PublicKey } from "@solana/web3.js";
|
||||
import { programIds, WRAPPED_SOL_MINT } from "./../constants/ids";
|
||||
import { AccountLayout, u64, MintInfo, MintLayout, Token } from "@solana/spl-token";
|
||||
import { AccountLayout, u64, MintInfo, MintLayout } from "@solana/spl-token";
|
||||
import { TokenAccount } from "./../models";
|
||||
import { chunks } from "./../utils/utils";
|
||||
import { EventEmitter } from "./../utils/eventEmitter";
|
||||
|
@ -46,7 +46,10 @@ export const MintParser = (pubKey: PublicKey, info: AccountInfo<Buffer>) => {
|
|||
return details;
|
||||
};
|
||||
|
||||
export const TokenAccountParser = (pubKey: PublicKey, info: AccountInfo<Buffer>) => {
|
||||
export const TokenAccountParser = (
|
||||
pubKey: PublicKey,
|
||||
info: AccountInfo<Buffer>
|
||||
) => {
|
||||
const buffer = Buffer.from(info.data);
|
||||
const data = deserializeAccount(buffer);
|
||||
|
||||
|
@ -118,8 +121,12 @@ export const cache = {
|
|||
|
||||
return query;
|
||||
},
|
||||
add: (id: PublicKey | string, obj: AccountInfo<Buffer>, parser?: AccountParser) => {
|
||||
const address = typeof id === 'string' ? id : id?.toBase58();
|
||||
add: (
|
||||
id: PublicKey | string,
|
||||
obj: AccountInfo<Buffer>,
|
||||
parser?: AccountParser
|
||||
) => {
|
||||
const address = typeof id === "string" ? id : id?.toBase58();
|
||||
const deserialize = parser ? parser : keyToAccountParser.get(address);
|
||||
if (!deserialize) {
|
||||
throw new Error(
|
||||
|
@ -160,7 +167,7 @@ export const cache = {
|
|||
},
|
||||
registerParser: (pubkey: PublicKey | string, parser: AccountParser) => {
|
||||
if (pubkey) {
|
||||
const address = typeof pubkey === 'string' ? pubkey : pubkey?.toBase58();
|
||||
const address = typeof pubkey === "string" ? pubkey : pubkey?.toBase58();
|
||||
keyToAccountParser.set(address, parser);
|
||||
}
|
||||
|
||||
|
@ -172,7 +179,7 @@ export const useAccountsContext = () => {
|
|||
const context = useContext(AccountsContext);
|
||||
|
||||
return context;
|
||||
}
|
||||
};
|
||||
|
||||
function wrapNativeAccount(
|
||||
pubkey: PublicKey,
|
||||
|
@ -206,13 +213,16 @@ const UseNativeAccount = () => {
|
|||
|
||||
const [nativeAccount, setNativeAccount] = useState<AccountInfo<Buffer>>();
|
||||
|
||||
const updateCache = useCallback((account) => {
|
||||
const updateCache = useCallback(
|
||||
(account) => {
|
||||
const wrapped = wrapNativeAccount(wallet.publicKey, account);
|
||||
if (wrapped !== undefined && wallet) {
|
||||
cache.registerParser(wallet.publicKey.toBase58(), TokenAccountParser);
|
||||
genericCache.set(wallet.publicKey.toBase58(), wrapped as TokenAccount);
|
||||
cache.registerParser(wallet.publicKey?.toBase58(), TokenAccountParser);
|
||||
genericCache.set(wallet.publicKey?.toBase58(), wrapped as TokenAccount);
|
||||
}
|
||||
}, [wallet]);
|
||||
},
|
||||
[wallet]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!connection || !wallet?.publicKey) {
|
||||
|
@ -231,7 +241,7 @@ const UseNativeAccount = () => {
|
|||
setNativeAccount(acc);
|
||||
}
|
||||
});
|
||||
}, [setNativeAccount, wallet, wallet.publicKey, connection]);
|
||||
}, [setNativeAccount, wallet, wallet.publicKey, connection, updateCache]);
|
||||
|
||||
return { nativeAccount };
|
||||
};
|
||||
|
@ -252,8 +262,7 @@ const precacheUserTokenAccounts = async (
|
|||
const accounts = await connection.getTokenAccountsByOwner(owner, {
|
||||
programId: programIds().token,
|
||||
});
|
||||
accounts.value
|
||||
.forEach((info) => {
|
||||
accounts.value.forEach((info) => {
|
||||
cache.add(info.pubkey.toBase58(), info.account, TokenAccountParser);
|
||||
});
|
||||
};
|
||||
|
@ -266,15 +275,21 @@ export function AccountsProvider({ children = null as any }) {
|
|||
const { nativeAccount } = UseNativeAccount();
|
||||
|
||||
const selectUserAccounts = useCallback(() => {
|
||||
return cache.byParser(TokenAccountParser).map(id => cache.get(id)).filter(
|
||||
(a) => a && a.info.owner.toBase58() === wallet.publicKey.toBase58()
|
||||
).map(a => a as TokenAccount);
|
||||
return cache
|
||||
.byParser(TokenAccountParser)
|
||||
.map((id) => cache.get(id))
|
||||
.filter(
|
||||
(a) => a && a.info.owner.toBase58() === wallet.publicKey?.toBase58()
|
||||
)
|
||||
.map((a) => a as TokenAccount);
|
||||
}, [wallet]);
|
||||
|
||||
useEffect(() => {
|
||||
const accounts = selectUserAccounts().filter((a) => a !== undefined) as TokenAccount[];
|
||||
const accounts = selectUserAccounts().filter(
|
||||
(a) => a !== undefined
|
||||
) as TokenAccount[];
|
||||
setUserAccounts(accounts);
|
||||
}, [nativeAccount, wallet, tokenAccounts]);
|
||||
}, [nativeAccount, wallet, tokenAccounts, selectUserAccounts]);
|
||||
|
||||
const publicKey = wallet?.publicKey;
|
||||
useEffect(() => {
|
||||
|
@ -296,10 +311,7 @@ export function AccountsProvider({ children = null as any }) {
|
|||
if (info.accountInfo.data.length === AccountLayout.span) {
|
||||
const data = deserializeAccount(info.accountInfo.data);
|
||||
|
||||
if (
|
||||
PRECACHED_OWNERS.has(data.owner.toBase58()) ||
|
||||
!cache.get(id)
|
||||
) {
|
||||
if (PRECACHED_OWNERS.has(data.owner.toBase58()) || !cache.get(id)) {
|
||||
cache.add(id, info.accountInfo, TokenAccountParser);
|
||||
setTokenAccounts(selectUserAccounts());
|
||||
accountEmitter.raiseAccountUpdated(id);
|
||||
|
@ -359,7 +371,8 @@ export const getMultipleAccounts = async (
|
|||
const array = result
|
||||
.map(
|
||||
(a) =>
|
||||
a.array.map((acc) => {
|
||||
a.array
|
||||
.map((acc) => {
|
||||
if (!acc) {
|
||||
return;
|
||||
}
|
||||
|
@ -370,7 +383,8 @@ export const getMultipleAccounts = async (
|
|||
data: Buffer.from(data[0], "base64"),
|
||||
} as AccountInfo<Buffer>;
|
||||
return obj;
|
||||
}).filter(_ => _) as AccountInfo<Buffer>[]
|
||||
})
|
||||
.filter((_) => _) as AccountInfo<Buffer>[]
|
||||
)
|
||||
.flat();
|
||||
return { keys, array };
|
||||
|
@ -412,15 +426,15 @@ export function useMint(key?: string | PublicKey) {
|
|||
|
||||
cache
|
||||
.query(connection, id, MintParser)
|
||||
.then(acc => setMint(acc.info as any))
|
||||
.catch((err) =>
|
||||
console.log(err)
|
||||
);
|
||||
.then((acc) => setMint(acc.info as any))
|
||||
.catch((err) => console.log(err));
|
||||
|
||||
const dispose = cache.emitter.onCache((e) => {
|
||||
const event = e;
|
||||
if (event.id === id) {
|
||||
cache.query(connection, id, MintParser).then(mint => setMint(mint.info as any));
|
||||
cache
|
||||
.query(connection, id, MintParser)
|
||||
.then((mint) => setMint(mint.info as any));
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
|
@ -443,9 +457,9 @@ export function useAccount(pubKey?: PublicKey) {
|
|||
return;
|
||||
}
|
||||
|
||||
const acc = await cache.query(connection, key, TokenAccountParser).catch((err) =>
|
||||
console.log(err)
|
||||
);
|
||||
const acc = await cache
|
||||
.query(connection, key, TokenAccountParser)
|
||||
.catch((err) => console.log(err));
|
||||
if (acc) {
|
||||
setAccount(acc);
|
||||
}
|
||||
|
|
|
@ -10,13 +10,18 @@ import React, { useContext, useEffect, useMemo, useState } from "react";
|
|||
import { setProgramIds } from "./../constants/ids";
|
||||
import { notify } from "./../utils/notifications";
|
||||
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" | "lending";
|
||||
export type ENV =
|
||||
| "mainnet-beta"
|
||||
| "testnet"
|
||||
| "devnet"
|
||||
| "localnet"
|
||||
| "lending";
|
||||
|
||||
export const ENDPOINTS = [
|
||||
{
|
||||
name: 'lending' as ENV,
|
||||
name: "lending" as ENV,
|
||||
endpoint: "https://tln.solana.com",
|
||||
},
|
||||
{
|
||||
|
@ -88,7 +93,7 @@ export function ConnectionProvider({ children = undefined as any }) {
|
|||
.then((res) => {
|
||||
return res.json();
|
||||
})
|
||||
.catch(err => [])
|
||||
.catch((err) => [])
|
||||
.then((list: KnownToken[]) => {
|
||||
const knownMints = [...LocalTokens, ...list].reduce((map, item) => {
|
||||
map.set(item.mintAddress, item);
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { useConnection } from "./connection";
|
||||
import { LENDING_PROGRAM_ID } from "./../constants/ids";
|
||||
import { LendingMarketParser, isLendingReserve, isLendingMarket, LendingReserveParser, LendingReserve } from "./../models/lending";
|
||||
import { cache, getMultipleAccounts, MintParser, ParsedAccount } from "./accounts";
|
||||
import {
|
||||
LendingMarketParser,
|
||||
isLendingReserve,
|
||||
isLendingMarket,
|
||||
LendingReserveParser,
|
||||
LendingReserve,
|
||||
} from "./../models/lending";
|
||||
import {
|
||||
cache,
|
||||
getMultipleAccounts,
|
||||
MintParser,
|
||||
ParsedAccount,
|
||||
} from "./accounts";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { DexMarketParser } from "../models/dex";
|
||||
|
||||
export interface LendingContextState {
|
||||
|
||||
}
|
||||
export interface LendingContextState {}
|
||||
|
||||
const LendingContext = React.createContext<LendingContextState | null>(null);
|
||||
|
||||
|
@ -17,14 +26,13 @@ export function LendingProvider({ children = null as any }) {
|
|||
return (
|
||||
<LendingContext.Provider
|
||||
value={{
|
||||
accounts
|
||||
accounts,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</LendingContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export const useLending = () => {
|
||||
const connection = useConnection();
|
||||
|
@ -32,9 +40,17 @@ export const useLending = () => {
|
|||
|
||||
const processAccount = useCallback((item) => {
|
||||
if (isLendingReserve(item.account)) {
|
||||
return cache.add(item.pubkey.toBase58(), item.account, LendingReserveParser);
|
||||
return cache.add(
|
||||
item.pubkey.toBase58(),
|
||||
item.account,
|
||||
LendingReserveParser
|
||||
);
|
||||
} else if (isLendingMarket(item.account)) {
|
||||
return cache.add(item.pubkey.toBase58(), item.account, LendingMarketParser);
|
||||
return cache.add(
|
||||
item.pubkey.toBase58(),
|
||||
item.account,
|
||||
LendingMarketParser
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
@ -45,20 +61,32 @@ export const useLending = () => {
|
|||
const queryLendingAccounts = async () => {
|
||||
const accounts = (await connection.getProgramAccounts(LENDING_PROGRAM_ID))
|
||||
.map(processAccount)
|
||||
.filter(item => item !== undefined);
|
||||
.filter((item) => item !== undefined);
|
||||
|
||||
const toQuery = [
|
||||
...accounts.filter(acc => (acc?.info as LendingReserve).lendingMarket !== undefined)
|
||||
.map(acc => acc as ParsedAccount<LendingReserve>)
|
||||
.map(acc => {
|
||||
...accounts
|
||||
.filter(
|
||||
(acc) => (acc?.info as LendingReserve).lendingMarket !== undefined
|
||||
)
|
||||
.map((acc) => acc as ParsedAccount<LendingReserve>)
|
||||
.map((acc) => {
|
||||
const result = [
|
||||
cache.registerParser(acc?.info.collateralMint.toBase58(), MintParser),
|
||||
cache.registerParser(acc?.info.liquidityMint.toBase58(), MintParser),
|
||||
cache.registerParser(
|
||||
acc?.info.collateralMint.toBase58(),
|
||||
MintParser
|
||||
),
|
||||
cache.registerParser(
|
||||
acc?.info.liquidityMint.toBase58(),
|
||||
MintParser
|
||||
),
|
||||
// ignore dex if its not set
|
||||
cache.registerParser(acc?.info.dexMarketOption ? acc?.info.dexMarket.toBase58() : '', DexMarketParser),
|
||||
].filter(_ => _);
|
||||
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
|
||||
|
@ -76,12 +104,10 @@ export const useLending = () => {
|
|||
return accounts;
|
||||
};
|
||||
|
||||
Promise.all([
|
||||
queryLendingAccounts(),
|
||||
]).then((all) => {
|
||||
Promise.all([queryLendingAccounts()]).then((all) => {
|
||||
setLendingAccounts(all.flat());
|
||||
});
|
||||
}, [connection]);
|
||||
}, [connection, processAccount]);
|
||||
|
||||
useEffect(() => {
|
||||
const subID = connection.onProgramAccountChange(
|
||||
|
@ -100,7 +126,7 @@ export const useLending = () => {
|
|||
return () => {
|
||||
connection.removeProgramAccountChangeListener(subID);
|
||||
};
|
||||
}, [connection, lendingAccounts]);
|
||||
}, [connection, lendingAccounts, processAccount]);
|
||||
|
||||
return { accounts: lendingAccounts };
|
||||
};
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
import React, { useCallback, useContext, useEffect, useState } from "react";
|
||||
import { MINT_TO_MARKET } from "./../models/marketOverrides";
|
||||
import {
|
||||
STABLE_COINS,
|
||||
} from "./../utils/utils";
|
||||
import { STABLE_COINS } from "./../utils/utils";
|
||||
import { useConnectionConfig } from "./connection";
|
||||
import {
|
||||
cache,
|
||||
getMultipleAccounts,
|
||||
} from "./accounts";
|
||||
import { cache, getMultipleAccounts } from "./accounts";
|
||||
import { Market, MARKETS, Orderbook, TOKEN_MINTS } from "@project-serum/serum";
|
||||
import { AccountInfo, Connection, PublicKey } from "@solana/web3.js";
|
||||
import { useMemo } from "react";
|
||||
|
@ -39,14 +34,10 @@ export function MarketProvider({ children = null as any }) {
|
|||
]);
|
||||
|
||||
// TODO: identify which markets to query ...
|
||||
const mints = useMemo(() => [
|
||||
|
||||
] as PublicKey[], []);
|
||||
const mints = useMemo(() => [] as PublicKey[], []);
|
||||
|
||||
const marketByMint = useMemo(() => {
|
||||
return [
|
||||
...new Set(mints).values(),
|
||||
].reduce((acc, key) => {
|
||||
return [...new Set(mints).values()].reduce((acc, key) => {
|
||||
const mintAddress = key.toBase58();
|
||||
|
||||
const SERUM_TOKEN = TOKEN_MINTS.find(
|
||||
|
@ -240,8 +231,6 @@ export const useMidPriceInUSD = (mint: string) => {
|
|||
return { price, isBase: price === 1.0 };
|
||||
};
|
||||
|
||||
|
||||
|
||||
const getMidPrice = (marketAddress?: string, mintAddress?: string) => {
|
||||
const SERUM_TOKEN = TOKEN_MINTS.find(
|
||||
(a) => a.address.toBase58() === mintAddress
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
export * from './useUserAccounts';
|
||||
export * from './useAccountByMint';
|
||||
export * from './useLendingReserves';
|
||||
export * from './useTokenName';
|
||||
export * from './useUserBalance';
|
||||
export * from './useCollateralBalance';
|
||||
export * from "./useUserAccounts";
|
||||
export * from "./useAccountByMint";
|
||||
export * from "./useLendingReserves";
|
||||
export * from "./useTokenName";
|
||||
export * from "./useUserBalance";
|
||||
export * from "./useCollateralBalance";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useUserAccounts } from './useUserAccounts';
|
||||
import { useUserAccounts } from "./useUserAccounts";
|
||||
|
||||
export const useAccountByMint = (mint: string) => {
|
||||
const { userAccounts } = useUserAccounts();
|
||||
|
|
|
@ -14,6 +14,6 @@ export function useCollateralBalance(reserve?: LendingReserve) {
|
|||
return {
|
||||
balance: fromLamports(collateralRatioLamports, mint),
|
||||
balanceLamports: collateralRatioLamports,
|
||||
accounts
|
||||
accounts,
|
||||
};
|
||||
}
|
|
@ -1,14 +1,19 @@
|
|||
import { PublicKey } from "@solana/web3.js";
|
||||
import { useEffect, useState } from "react";
|
||||
import { LendingReserve, LendingReserveParser } from "../models/lending";
|
||||
import { cache, ParsedAccount } from './../contexts/accounts';
|
||||
import { cache, ParsedAccount } from "./../contexts/accounts";
|
||||
|
||||
const getLendingReserves = () => {
|
||||
return cache.byParser(LendingReserveParser).map(id => cache.get(id)).filter(acc => acc !== undefined) as any[];
|
||||
return cache
|
||||
.byParser(LendingReserveParser)
|
||||
.map((id) => cache.get(id))
|
||||
.filter((acc) => acc !== undefined) as any[];
|
||||
};
|
||||
|
||||
export function useLendingReserves() {
|
||||
const [reserveAccounts, setReserveAccounts] = useState<ParsedAccount<LendingReserve>[]>([]);
|
||||
const [reserveAccounts, setReserveAccounts] = useState<
|
||||
ParsedAccount<LendingReserve>[]
|
||||
>([]);
|
||||
|
||||
useEffect(() => {
|
||||
setReserveAccounts(getLendingReserves());
|
||||
|
@ -22,7 +27,7 @@ export function useLendingReserves() {
|
|||
return () => {
|
||||
dispose();
|
||||
};
|
||||
}, [setReserveAccounts])
|
||||
}, [setReserveAccounts]);
|
||||
|
||||
return {
|
||||
reserveAccounts,
|
||||
|
@ -30,8 +35,10 @@ export function useLendingReserves() {
|
|||
}
|
||||
|
||||
export function useLendingReserve(address: string | PublicKey) {
|
||||
const id = typeof address === 'string' ? address : address?.toBase58();
|
||||
const [reserveAccount, setReserveAccount] = useState<ParsedAccount<LendingReserve>>();
|
||||
const id = typeof address === "string" ? address : address?.toBase58();
|
||||
const [reserveAccount, setReserveAccount] = useState<
|
||||
ParsedAccount<LendingReserve>
|
||||
>();
|
||||
|
||||
useEffect(() => {
|
||||
setReserveAccount(cache.get(id));
|
||||
|
@ -45,7 +52,7 @@ export function useLendingReserve(address: string | PublicKey) {
|
|||
return () => {
|
||||
dispose();
|
||||
};
|
||||
}, [setReserveAccount])
|
||||
}, [id, setReserveAccount]);
|
||||
|
||||
return reserveAccount;
|
||||
}
|
|
@ -4,6 +4,7 @@ import { getTokenName } from "../utils/utils";
|
|||
|
||||
export function useTokenName(mintAddress?: string | PublicKey) {
|
||||
const { tokenMap } = useConnectionConfig();
|
||||
const address = typeof mintAddress === 'string' ? mintAddress : mintAddress?.toBase58();
|
||||
const address =
|
||||
typeof mintAddress === "string" ? mintAddress : mintAddress?.toBase58();
|
||||
return getTokenName(tokenMap, address);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { TokenAccount } from "../models";
|
||||
import { useAccountsContext } from './../contexts/accounts';
|
||||
import { useAccountsContext } from "./../contexts/accounts";
|
||||
|
||||
export function useUserAccounts() {
|
||||
const context = useAccountsContext();
|
||||
|
|
|
@ -9,13 +9,15 @@ export function useUserBalance(mint?: PublicKey) {
|
|||
const mintInfo = useMint(mint);
|
||||
const accounts = useMemo(() => {
|
||||
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());
|
||||
}, [userAccounts, mint]);
|
||||
|
||||
const balanceLamports = useMemo(() => {
|
||||
return accounts
|
||||
.reduce((res, item) => res += item.info.amount.toNumber(), 0);
|
||||
return accounts.reduce(
|
||||
(res, item) => (res += item.info.amount.toNumber()),
|
||||
0
|
||||
);
|
||||
}, [accounts]);
|
||||
|
||||
return {
|
||||
|
|
|
@ -20,12 +20,18 @@ export const OrderBookParser = (id: PublicKey, acc: AccountInfo<Buffer>) => {
|
|||
return details;
|
||||
};
|
||||
|
||||
const DEFAULT_DEX_ID = new PublicKey('EUqojwWA2rd19FZrzeBncJsm38Jm1hEhE3zsmX3bRc2o');
|
||||
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);
|
||||
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,
|
||||
|
@ -41,4 +47,4 @@ export const DexMarketParser = (pubkey: PublicKey, acc: AccountInfo<Buffer>) =>
|
|||
cache.registerParser(details.info.asks, OrderBookParser);
|
||||
|
||||
return details;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
export * from "./account";
|
||||
export * from './lending';
|
||||
export * from "./lending";
|
||||
|
|
|
@ -8,7 +8,7 @@ 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';
|
||||
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.
|
||||
|
@ -49,7 +49,7 @@ export const borrowInstruction = (
|
|||
dexMarket: PublicKey,
|
||||
dexOrderBookSide: PublicKey,
|
||||
|
||||
memory: PublicKey,
|
||||
memory: PublicKey
|
||||
): TransactionInstruction => {
|
||||
const dataLayout = BufferLayout.struct([
|
||||
BufferLayout.u8("instruction"),
|
||||
|
@ -69,9 +69,17 @@ export const borrowInstruction = (
|
|||
{ 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: depositReserveCollateralSupply,
|
||||
isSigner: false,
|
||||
isWritable: true,
|
||||
},
|
||||
{ pubkey: borrowReserve, isSigner: false, isWritable: true },
|
||||
{ pubkey: borrowReserveLiquiditySupply, isSigner: false, isWritable: false },
|
||||
{
|
||||
pubkey: borrowReserveLiquiditySupply,
|
||||
isSigner: false,
|
||||
isWritable: false,
|
||||
},
|
||||
{ pubkey: obligation, isSigner: false, isWritable: true },
|
||||
{ pubkey: obligationMint, isSigner: false, isWritable: true },
|
||||
{ pubkey: obligationTokenOutput, isSigner: false, isWritable: true },
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export * from './market';
|
||||
export * from './reserve';
|
||||
export * from './obligation';
|
||||
export * from './lending';
|
||||
export * from './borrow';
|
||||
export * from "./market";
|
||||
export * from "./reserve";
|
||||
export * from "./obligation";
|
||||
export * from "./lending";
|
||||
export * from "./borrow";
|
||||
|
|
|
@ -4,5 +4,5 @@ export enum LendingInstruction {
|
|||
DepositReserveLiquidity = 2,
|
||||
WithdrawReserveLiquidity = 3,
|
||||
BorrowReserveLiquidity = 4,
|
||||
RepayReserveLiquidity = 5
|
||||
RepayReserveLiquidity = 5,
|
||||
}
|
|
@ -1,15 +1,9 @@
|
|||
import {
|
||||
AccountInfo,
|
||||
PublicKey,
|
||||
} from "@solana/web3.js";
|
||||
import { AccountInfo, PublicKey } from "@solana/web3.js";
|
||||
import * as BufferLayout from "buffer-layout";
|
||||
import * as Layout from "./../../utils/layout";
|
||||
|
||||
export const LendingMarketLayout: typeof BufferLayout.Structure = BufferLayout.struct(
|
||||
[
|
||||
BufferLayout.u8("isInitialized"),
|
||||
Layout.publicKey("quoteMint"),
|
||||
]
|
||||
[BufferLayout.u8("isInitialized"), Layout.publicKey("quoteMint")]
|
||||
);
|
||||
|
||||
export interface LendingMarket {
|
||||
|
@ -19,9 +13,12 @@ export interface LendingMarket {
|
|||
|
||||
export const isLendingMarket = (info: AccountInfo<Buffer>) => {
|
||||
return info.data.length === LendingMarketLayout.span;
|
||||
}
|
||||
};
|
||||
|
||||
export const LendingMarketParser = (pubKey: PublicKey, info: AccountInfo<Buffer>) => {
|
||||
export const LendingMarketParser = (
|
||||
pubKey: PublicKey,
|
||||
info: AccountInfo<Buffer>
|
||||
) => {
|
||||
const buffer = Buffer.from(info.data);
|
||||
const data = LendingMarketLayout.decode(buffer);
|
||||
|
||||
|
@ -36,6 +33,5 @@ export const LendingMarketParser = (pubKey: PublicKey, info: AccountInfo<Buffer>
|
|||
return details;
|
||||
};
|
||||
|
||||
|
||||
// TODO:
|
||||
// create instructions for init
|
|
@ -1,6 +1,4 @@
|
|||
import {
|
||||
PublicKey,
|
||||
} from "@solana/web3.js";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import BN from "bn.js";
|
||||
import * as BufferLayout from "buffer-layout";
|
||||
import * as Layout from "./../../utils/layout";
|
||||
|
|
|
@ -9,7 +9,7 @@ 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';
|
||||
import { LendingInstruction } from "./lending";
|
||||
|
||||
export const LendingReserveLayout: typeof BufferLayout.Structure = BufferLayout.struct(
|
||||
[
|
||||
|
@ -21,10 +21,11 @@ export const LendingReserveLayout: typeof BufferLayout.Structure = BufferLayout.
|
|||
Layout.publicKey("collateralMint"),
|
||||
Layout.publicKey("collateralSupply"),
|
||||
// TODO: replace u32 option with generic quivalent
|
||||
BufferLayout.u32('dexMarketOption'),
|
||||
BufferLayout.u32("dexMarketOption"),
|
||||
Layout.publicKey("dexMarket"),
|
||||
|
||||
BufferLayout.struct([
|
||||
BufferLayout.struct(
|
||||
[
|
||||
/// Max utilization rate as a percent
|
||||
BufferLayout.u8("maxUtilizationRate"),
|
||||
/// The ratio of the loan to the value of the collateral as a percent
|
||||
|
@ -33,7 +34,9 @@ export const LendingReserveLayout: typeof BufferLayout.Structure = BufferLayout.
|
|||
BufferLayout.u8("liquidationBonus"),
|
||||
/// The percent at which an obligation is considered unhealthy
|
||||
BufferLayout.u8("liquidationThreshold"),
|
||||
], "config"),
|
||||
],
|
||||
"config"
|
||||
),
|
||||
|
||||
Layout.uint128("cumulativeBorrowRate"),
|
||||
Layout.uint128("totalBorrows"),
|
||||
|
@ -47,7 +50,7 @@ export const isLendingReserve = (info: AccountInfo<Buffer>) => {
|
|||
console.log(LendingReserveLayout.span);
|
||||
console.log(info.data.length);
|
||||
return info.data.length === LendingReserveLayout.span;
|
||||
}
|
||||
};
|
||||
|
||||
export interface LendingReserve {
|
||||
lastUpdateSlot: BN;
|
||||
|
@ -63,11 +66,11 @@ export interface LendingReserve {
|
|||
dexMarketPrice: BN; // what is precision on the price?
|
||||
|
||||
config: {
|
||||
maxUtilizationRate: number,
|
||||
loanToValueRatio: number,
|
||||
liquidationBonus: number,
|
||||
liquidationThreshold: number,
|
||||
}
|
||||
maxUtilizationRate: number;
|
||||
loanToValueRatio: number;
|
||||
liquidationBonus: number;
|
||||
liquidationThreshold: number;
|
||||
};
|
||||
// collateralFactor: number;
|
||||
|
||||
cumulativeBorrowRate: BN;
|
||||
|
@ -80,7 +83,10 @@ export interface LendingReserve {
|
|||
// Layout.uint128("total_borrows"),
|
||||
}
|
||||
|
||||
export const LendingReserveParser = (pubKey: PublicKey, info: AccountInfo<Buffer>) => {
|
||||
export const LendingReserveParser = (
|
||||
pubKey: PublicKey,
|
||||
info: AccountInfo<Buffer>
|
||||
) => {
|
||||
const buffer = Buffer.from(info.data);
|
||||
const data = LendingReserveLayout.decode(buffer);
|
||||
|
||||
|
@ -110,12 +116,12 @@ export const initReserveInstruction = (
|
|||
lendingMarket: PublicKey,
|
||||
lendingMarketAuthority: PublicKey,
|
||||
|
||||
dexMarket: PublicKey, // TODO: optional
|
||||
dexMarket: PublicKey // TODO: optional
|
||||
): TransactionInstruction => {
|
||||
const dataLayout = BufferLayout.struct([
|
||||
BufferLayout.u8("instruction"),
|
||||
Layout.uint64("liquidityAmount"),
|
||||
BufferLayout.u8("maxUtilizationRate")
|
||||
BufferLayout.u8("maxUtilizationRate"),
|
||||
]);
|
||||
|
||||
const data = Buffer.alloc(dataLayout.span);
|
||||
|
@ -172,7 +178,7 @@ export const depositInstruction = (
|
|||
reserveAuthority: PublicKey,
|
||||
reserveAccount: PublicKey,
|
||||
reserveSupply: PublicKey,
|
||||
collateralMint: PublicKey,
|
||||
collateralMint: PublicKey
|
||||
): TransactionInstruction => {
|
||||
const dataLayout = BufferLayout.struct([
|
||||
BufferLayout.u8("instruction"),
|
||||
|
@ -212,7 +218,7 @@ export const withdrawInstruction = (
|
|||
reserveAccount: PublicKey,
|
||||
collateralMint: PublicKey,
|
||||
reserveSupply: PublicKey,
|
||||
authority: PublicKey,
|
||||
authority: PublicKey
|
||||
): TransactionInstruction => {
|
||||
const dataLayout = BufferLayout.struct([
|
||||
BufferLayout.u8("instruction"),
|
||||
|
@ -244,4 +250,3 @@ export const withdrawInstruction = (
|
|||
data,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
BorrowReserveView,
|
||||
WithdrawView,
|
||||
FaucetView,
|
||||
} from './views';
|
||||
} from "./views";
|
||||
|
||||
export function Routes() {
|
||||
return (
|
||||
|
@ -31,13 +31,27 @@ export function Routes() {
|
|||
<AppLayout>
|
||||
<Switch>
|
||||
<Route exact path="/" component={() => <HomeView />} />
|
||||
<Route exact path="/dashboard" children={<DashboardView />} />
|
||||
<Route
|
||||
exact
|
||||
path="/dashboard"
|
||||
children={<DashboardView />}
|
||||
/>
|
||||
<Route path="/reserve/:id" children={<ReserveView />} />
|
||||
<Route exact path="/deposit" component={() => <DepositView />} />
|
||||
<Route path="/deposit/:id" children={<DepositReserveView />} />
|
||||
<Route
|
||||
exact
|
||||
path="/deposit"
|
||||
component={() => <DepositView />}
|
||||
/>
|
||||
<Route
|
||||
path="/deposit/:id"
|
||||
children={<DepositReserveView />}
|
||||
/>
|
||||
<Route path="/withdraw/:id" children={<WithdrawView />} />
|
||||
<Route exact path="/borrow" children={<BorrowView />} />
|
||||
<Route path="/borrow/:id" children={<BorrowReserveView />} />
|
||||
<Route
|
||||
path="/borrow/:id"
|
||||
children={<BorrowReserveView />}
|
||||
/>
|
||||
<Route exact path="/faucet" children={<FaucetView />} />
|
||||
</Switch>
|
||||
</AppLayout>
|
||||
|
|
|
@ -32,15 +32,14 @@ export const uint64 = (property = "uint64"): unknown => {
|
|||
const _decode = layout.decode.bind(layout);
|
||||
const _encode = layout.encode.bind(layout);
|
||||
|
||||
|
||||
layout.decode = (buffer: Buffer, offset: number) => {
|
||||
const data = _decode(buffer, offset);
|
||||
return new BN(
|
||||
[...data]
|
||||
.reverse()
|
||||
.map(i => `00${i.toString(16)}`.slice(-2))
|
||||
.join(''),
|
||||
16,
|
||||
.map((i) => `00${i.toString(16)}`.slice(-2))
|
||||
.join(""),
|
||||
16
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -65,15 +64,14 @@ export const uint128 = (property = "uint128"): unknown => {
|
|||
const _decode = layout.decode.bind(layout);
|
||||
const _encode = layout.encode.bind(layout);
|
||||
|
||||
|
||||
layout.decode = (buffer: Buffer, offset: number) => {
|
||||
const data = _decode(buffer, offset);
|
||||
return new BN(
|
||||
[...data]
|
||||
.reverse()
|
||||
.map(i => `00${i.toString(16)}`.slice(-2))
|
||||
.join(''),
|
||||
16,
|
||||
.map((i) => `00${i.toString(16)}`.slice(-2))
|
||||
.join(""),
|
||||
16
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -89,7 +87,6 @@ export const uint128 = (property = "uint128"): unknown => {
|
|||
return _encode(b, buffer, offset);
|
||||
};
|
||||
|
||||
|
||||
return layout;
|
||||
};
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ export function getTokenName(
|
|||
shorten = true
|
||||
): string {
|
||||
if (!mintAddress) {
|
||||
return 'N/A';
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
const knownSymbol = map.get(mintAddress)?.tokenSymbol;
|
||||
|
@ -66,9 +66,10 @@ export function getTokenName(
|
|||
|
||||
export function getTokenIcon(
|
||||
map: KnownTokenMap,
|
||||
mintAddress?: string | PublicKey,
|
||||
mintAddress?: string | PublicKey
|
||||
): string | undefined {
|
||||
const address = typeof mintAddress === 'string' ? mintAddress : mintAddress?.toBase58();
|
||||
const address =
|
||||
typeof mintAddress === "string" ? mintAddress : mintAddress?.toBase58();
|
||||
if (!address) {
|
||||
return;
|
||||
}
|
||||
|
@ -101,7 +102,7 @@ export function toLamports(
|
|||
typeof account === "number" ? account : account.info.amount?.toNumber();
|
||||
|
||||
const precision = Math.pow(10, mint?.decimals || 0);
|
||||
return (amount * precision);
|
||||
return amount * precision;
|
||||
}
|
||||
|
||||
export function fromLamports(
|
||||
|
@ -171,12 +172,12 @@ const numberFormater = new Intl.NumberFormat("en-US", {
|
|||
export const formatNumber = {
|
||||
format: (val?: number) => {
|
||||
if (!val) {
|
||||
return '--';
|
||||
return "--";
|
||||
}
|
||||
|
||||
return numberFormater.format(val);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export const formatPct = new Intl.NumberFormat("en-US", {
|
||||
style: "percent",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import { useLendingReserves } from '../../hooks';
|
||||
import { BorrowItem } from './item';
|
||||
import './itemStyle.less';
|
||||
import { useLendingReserves } from "../../hooks";
|
||||
import { BorrowItem } from "./item";
|
||||
import "./itemStyle.less";
|
||||
|
||||
export const BorrowView = () => {
|
||||
const { reserveAccounts } = useLendingReserves();
|
||||
|
@ -13,7 +13,9 @@ export const BorrowView = () => {
|
|||
<div>APY</div>
|
||||
<div>Action</div>
|
||||
</div>
|
||||
{reserveAccounts.map(account => <BorrowItem reserve={account.info} address={account.pubkey} />)}
|
||||
{reserveAccounts.map((account) => (
|
||||
<BorrowItem reserve={account.info} address={account.pubkey} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import { useCollateralBalance, useTokenName } from '../../hooks';
|
||||
import { useCollateralBalance, useTokenName } from "../../hooks";
|
||||
import { LendingReserve } from "../../models/lending";
|
||||
import { TokenIcon } from "../../components/TokenIcon";
|
||||
import { formatNumber } from "../../utils/utils";
|
||||
|
@ -7,17 +7,26 @@ import { Button, Card } from "antd";
|
|||
import { Link } from "react-router-dom";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export const BorrowItem = (props: { reserve: LendingReserve, address: PublicKey }) => {
|
||||
export const BorrowItem = (props: {
|
||||
reserve: LendingReserve;
|
||||
address: PublicKey;
|
||||
}) => {
|
||||
const name = useTokenName(props.reserve.liquidityMint);
|
||||
|
||||
// TODO: calculate avilable amount... based on total owned collateral across all the reserves
|
||||
const { balance: collateralBalance } = useCollateralBalance(props.reserve);
|
||||
|
||||
return <Link to={`/borrow/${props.address.toBase58()}`}>
|
||||
return (
|
||||
<Link to={`/borrow/${props.address.toBase58()}`}>
|
||||
<Card>
|
||||
<div className="borrow-item">
|
||||
<span style={{ display: 'flex' }}><TokenIcon mintAddress={props.reserve.liquidityMint} />{name}</span>
|
||||
<div>{formatNumber.format(collateralBalance)} {name}</div>
|
||||
<span style={{ display: "flex" }}>
|
||||
<TokenIcon mintAddress={props.reserve.liquidityMint} />
|
||||
{name}
|
||||
</span>
|
||||
<div>
|
||||
{formatNumber.format(collateralBalance)} {name}
|
||||
</div>
|
||||
<div>--</div>
|
||||
<div>
|
||||
<Button>
|
||||
|
@ -26,5 +35,6 @@ export const BorrowItem = (props: { reserve: LendingReserve, address: PublicKey
|
|||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>;
|
||||
}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import React, { } from "react";
|
||||
import { useLendingReserve } from '../../hooks';
|
||||
import React from "react";
|
||||
import { useLendingReserve } from "../../hooks";
|
||||
import { useParams } from "react-router-dom";
|
||||
import './style.less';
|
||||
import "./style.less";
|
||||
|
||||
import { BorrowInput } from '../../components/BorrowInput';
|
||||
import { SideReserveOverview, SideReserveOverviewMode } from '../../components/SideReserveOverview';
|
||||
import { BorrowInput } from "../../components/BorrowInput";
|
||||
import {
|
||||
SideReserveOverview,
|
||||
SideReserveOverviewMode,
|
||||
} from "../../components/SideReserveOverview";
|
||||
|
||||
export const BorrowReserveView = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
|
@ -15,17 +18,21 @@ export const BorrowReserveView = () => {
|
|||
return null;
|
||||
}
|
||||
|
||||
return <div className="borrow-reserve">
|
||||
return (
|
||||
<div className="borrow-reserve">
|
||||
<div className="borrow-reserve-container">
|
||||
<BorrowInput
|
||||
className="borrow-reserve-item borrow-reserve-item-left"
|
||||
reserve={reserve}
|
||||
address={lendingReserve.pubkey} />
|
||||
address={lendingReserve.pubkey}
|
||||
/>
|
||||
<SideReserveOverview
|
||||
className="borrow-reserve-item borrow-reserve-item-right"
|
||||
reserve={reserve}
|
||||
address={lendingReserve.pubkey}
|
||||
mode={SideReserveOverviewMode.Borrow} />
|
||||
mode={SideReserveOverviewMode.Borrow}
|
||||
/>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import React from "react";
|
||||
|
||||
export const DashboardView = () => {
|
||||
|
||||
return <div className="flexColumn">
|
||||
DASHBOARD:
|
||||
TODO:
|
||||
1. Add deposits
|
||||
2. Add obligations
|
||||
</div>;
|
||||
}
|
||||
return (
|
||||
<div className="flexColumn">
|
||||
DASHBOARD: TODO: 1. Add deposits 2. Add obligations
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1 +1 @@
|
|||
export * from './view';
|
||||
export * from "./view";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import { useLendingReserves } from '../../../hooks';
|
||||
import { ReserveItem } from './item';
|
||||
import './itemStyle.less';
|
||||
import { useLendingReserves } from "../../../hooks";
|
||||
import { ReserveItem } from "./item";
|
||||
import "./itemStyle.less";
|
||||
|
||||
export const DepositView = () => {
|
||||
const { reserveAccounts } = useLendingReserves();
|
||||
|
@ -14,7 +14,9 @@ export const DepositView = () => {
|
|||
<div>APY</div>
|
||||
<div>Action</div>
|
||||
</div>
|
||||
{reserveAccounts.map(account => <ReserveItem reserve={account.info} address={account.pubkey} />)}
|
||||
{reserveAccounts.map((account) => (
|
||||
<ReserveItem reserve={account.info} address={account.pubkey} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,5 +1,9 @@
|
|||
import React from "react";
|
||||
import { useCollateralBalance, useTokenName, useUserBalance } from '../../../hooks';
|
||||
import {
|
||||
useCollateralBalance,
|
||||
useTokenName,
|
||||
useUserBalance,
|
||||
} from "../../../hooks";
|
||||
import { LendingReserve } from "../../../models/lending";
|
||||
import { TokenIcon } from "../../../components/TokenIcon";
|
||||
import { formatNumber } from "../../../utils/utils";
|
||||
|
@ -7,17 +11,28 @@ import { Button, Card } from "antd";
|
|||
import { Link } from "react-router-dom";
|
||||
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 { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
|
||||
const { balance: collateralBalance } = useCollateralBalance(props.reserve);
|
||||
|
||||
return <Link to={`/deposit/${props.address.toBase58()}`}>
|
||||
return (
|
||||
<Link to={`/deposit/${props.address.toBase58()}`}>
|
||||
<Card>
|
||||
<div className="deposit-item">
|
||||
<span style={{ display: 'flex' }}><TokenIcon mintAddress={props.reserve.liquidityMint} />{name}</span>
|
||||
<div>{formatNumber.format(tokenBalance)} {name}</div>
|
||||
<div>{formatNumber.format(collateralBalance)} {name}</div>
|
||||
<span style={{ display: "flex" }}>
|
||||
<TokenIcon mintAddress={props.reserve.liquidityMint} />
|
||||
{name}
|
||||
</span>
|
||||
<div>
|
||||
{formatNumber.format(tokenBalance)} {name}
|
||||
</div>
|
||||
<div>
|
||||
{formatNumber.format(collateralBalance)} {name}
|
||||
</div>
|
||||
<div>--</div>
|
||||
<div>
|
||||
<Button>
|
||||
|
@ -26,5 +41,6 @@ export const ReserveItem = (props: { reserve: LendingReserve, address: PublicKey
|
|||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Link>;
|
||||
}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import React, { } from "react";
|
||||
import { useLendingReserve } from '../../hooks';
|
||||
import React from "react";
|
||||
import { useLendingReserve } from "../../hooks";
|
||||
import { useParams } from "react-router-dom";
|
||||
import './style.less';
|
||||
import "./style.less";
|
||||
|
||||
import { DepositInput } from '../../components/DepositInput';
|
||||
import { DepositInfoLine } from '../../components/DepositInfoLine';
|
||||
import { SideReserveOverview, SideReserveOverviewMode } from '../../components/SideReserveOverview';
|
||||
import { DepositInput } from "../../components/DepositInput";
|
||||
import { DepositInfoLine } from "../../components/DepositInfoLine";
|
||||
import {
|
||||
SideReserveOverview,
|
||||
SideReserveOverviewMode,
|
||||
} from "../../components/SideReserveOverview";
|
||||
|
||||
export const DepositReserveView = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
|
@ -16,21 +19,26 @@ export const DepositReserveView = () => {
|
|||
return null;
|
||||
}
|
||||
|
||||
return <div className="deposit-reserve">
|
||||
return (
|
||||
<div className="deposit-reserve">
|
||||
<DepositInfoLine
|
||||
className="deposit-reserve-item"
|
||||
reserve={reserve}
|
||||
address={lendingReserve.pubkey} />
|
||||
address={lendingReserve.pubkey}
|
||||
/>
|
||||
<div className="deposit-reserve-container">
|
||||
<DepositInput
|
||||
className="deposit-reserve-item deposit-reserve-item-left"
|
||||
reserve={reserve}
|
||||
address={lendingReserve.pubkey} />
|
||||
address={lendingReserve.pubkey}
|
||||
/>
|
||||
<SideReserveOverview
|
||||
className="deposit-reserve-item deposit-reserve-item-right"
|
||||
reserve={reserve}
|
||||
address={lendingReserve.pubkey}
|
||||
mode={SideReserveOverviewMode.Deposit} />
|
||||
mode={SideReserveOverviewMode.Deposit}
|
||||
/>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -10,27 +10,36 @@ export const FaucetView = () => {
|
|||
|
||||
const airdrop = useCallback(() => {
|
||||
connection.requestAirdrop(wallet.publicKey, 1 * LAMPORTS_PER_SOL);
|
||||
}, [wallet, connection])
|
||||
}, [wallet, connection]);
|
||||
|
||||
const bodyStyle: React.CSSProperties = {
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: "100%",
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="flexColumn" style={{ flex: 1 }}>
|
||||
<Card title={'Faucet'} bodyStyle={bodyStyle} style={{ flex: 1 }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around', alignItems: 'center' }}>
|
||||
<Card title={"Faucet"} bodyStyle={bodyStyle} style={{ flex: 1 }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-around",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div className="deposit-input-title" style={{ margin: 10 }}>
|
||||
This Faucet will help you fund your accounts outside of Solana main network.
|
||||
This Faucet will help you fund your accounts outside of Solana main
|
||||
network.
|
||||
</div>
|
||||
<Button type="primary" onClick={airdrop} >Give me SOL</Button>
|
||||
<Button type="primary" onClick={airdrop}>
|
||||
Give me SOL
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import React from "react";
|
||||
import { useLendingReserves } from '../../hooks';
|
||||
import { useLendingReserves } from "../../hooks";
|
||||
import { LendingReserveItem } from "./item";
|
||||
import './itemStyle.less';
|
||||
import "./itemStyle.less";
|
||||
|
||||
export const HomeView = () => {
|
||||
const { reserveAccounts } = useLendingReserves();
|
||||
|
||||
// TODO: add total Liquidity amount ...
|
||||
|
||||
return <div className="flexColumn">
|
||||
return (
|
||||
<div className="flexColumn">
|
||||
<div className="home-item home-header">
|
||||
<div>Asset</div>
|
||||
<div>Market Size</div>
|
||||
|
@ -16,6 +17,9 @@ export const HomeView = () => {
|
|||
<div>Deposit APY</div>
|
||||
<div>Borrow APY</div>
|
||||
</div>
|
||||
{reserveAccounts.map(account => <LendingReserveItem reserve={account.info} address={account.pubkey} />)}
|
||||
</div>;
|
||||
}
|
||||
{reserveAccounts.map((account) => (
|
||||
<LendingReserveItem reserve={account.info} address={account.pubkey} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import { useTokenName } from '../../hooks';
|
||||
import { useTokenName } from "../../hooks";
|
||||
import { LendingReserve } from "../../models/lending";
|
||||
import { TokenIcon } from "../../components/TokenIcon";
|
||||
import { formatNumber, fromLamports } from "../../utils/utils";
|
||||
|
@ -8,26 +8,40 @@ import { Link } from "react-router-dom";
|
|||
import { PublicKey } from "@solana/web3.js";
|
||||
import { 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 liquidityMint = useMint(props.reserve.liquidityMint);
|
||||
|
||||
const totalLiquidity = fromLamports(props.reserve.totalLiquidity.toNumber(), liquidityMint);
|
||||
const totalLiquidity = fromLamports(
|
||||
props.reserve.totalLiquidity.toNumber(),
|
||||
liquidityMint
|
||||
);
|
||||
const totalBorrows = props.reserve.totalBorrows.toString();
|
||||
|
||||
console.log(liquidityMint);
|
||||
|
||||
return <Link to={`/reserve/${props.address.toBase58()}`}>
|
||||
return (
|
||||
<Link to={`/reserve/${props.address.toBase58()}`}>
|
||||
<Card>
|
||||
<div className="home-item">
|
||||
<span style={{ display: 'flex' }}><TokenIcon mintAddress={props.reserve.liquidityMint} />{name}</span>
|
||||
<div>{formatNumber.format(totalLiquidity)} {name}</div>
|
||||
<div>{totalBorrows} {name}</div>
|
||||
<span style={{ display: "flex" }}>
|
||||
<TokenIcon mintAddress={props.reserve.liquidityMint} />
|
||||
{name}
|
||||
</span>
|
||||
<div>
|
||||
{formatNumber.format(totalLiquidity)} {name}
|
||||
</div>
|
||||
<div>
|
||||
{totalBorrows} {name}
|
||||
</div>
|
||||
<div>--</div>
|
||||
<div>--</div>
|
||||
</div>
|
||||
|
||||
</Card>
|
||||
</Link>;
|
||||
}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
export { HomeView } from './home';
|
||||
export { BorrowView } from './borrow';
|
||||
export { BorrowReserveView } from './borrowReserve';
|
||||
export { DashboardView } from './dashboard';
|
||||
export { DepositView } from './deposit';
|
||||
export { DepositReserveView } from './depositReserve';
|
||||
export { ReserveView } from './reserve';
|
||||
export { WithdrawView } from './withdraw';
|
||||
export { FaucetView } from './faucet';
|
||||
export { HomeView } from "./home";
|
||||
export { BorrowView } from "./borrow";
|
||||
export { BorrowReserveView } from "./borrowReserve";
|
||||
export { DashboardView } from "./dashboard";
|
||||
export { DepositView } from "./deposit";
|
||||
export { DepositReserveView } from "./depositReserve";
|
||||
export { ReserveView } from "./reserve";
|
||||
export { WithdrawView } from "./withdraw";
|
||||
export { FaucetView } from "./faucet";
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React, { } from "react";
|
||||
import { useLendingReserve } from './../../hooks';
|
||||
import React from "react";
|
||||
import { useLendingReserve } from "./../../hooks";
|
||||
import { useParams } from "react-router-dom";
|
||||
import './style.less';
|
||||
import "./style.less";
|
||||
|
||||
import { UserLendingCard } from './../../components/UserLendingCard';
|
||||
import { ReserveStatus } from './../../components/ReserveStatus';
|
||||
import { UserLendingCard } from "./../../components/UserLendingCard";
|
||||
import { ReserveStatus } from "./../../components/ReserveStatus";
|
||||
|
||||
export const ReserveView = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
|
@ -15,16 +15,20 @@ export const ReserveView = () => {
|
|||
return null;
|
||||
}
|
||||
|
||||
return <div className="reserve-overview">
|
||||
return (
|
||||
<div className="reserve-overview">
|
||||
<div className="reserve-overview-container">
|
||||
<ReserveStatus
|
||||
className="reserve-overview-item reserve-overview-item-left"
|
||||
reserve={reserve}
|
||||
address={lendingReserve.pubkey} />
|
||||
address={lendingReserve.pubkey}
|
||||
/>
|
||||
<UserLendingCard
|
||||
className="reserve-overview-item reserve-overview-item-right"
|
||||
reserve={reserve}
|
||||
address={lendingReserve.pubkey} />
|
||||
address={lendingReserve.pubkey}
|
||||
/>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import React, { } from "react";
|
||||
import { useLendingReserve } from '../../hooks';
|
||||
import React from "react";
|
||||
import { useLendingReserve } from "../../hooks";
|
||||
import { useParams } from "react-router-dom";
|
||||
import './style.less';
|
||||
import "./style.less";
|
||||
|
||||
import { WithdrawInput } from '../../components/WithdrawInput';
|
||||
import { DepositInfoLine } from '../../components/DepositInfoLine';
|
||||
import { SideReserveOverview, SideReserveOverviewMode } from '../../components/SideReserveOverview';
|
||||
import { WithdrawInput } from "../../components/WithdrawInput";
|
||||
import { DepositInfoLine } from "../../components/DepositInfoLine";
|
||||
import {
|
||||
SideReserveOverview,
|
||||
SideReserveOverviewMode,
|
||||
} from "../../components/SideReserveOverview";
|
||||
|
||||
export const WithdrawView = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
|
@ -16,21 +19,26 @@ export const WithdrawView = () => {
|
|||
return null;
|
||||
}
|
||||
|
||||
return <div className="deposit-reserve">
|
||||
return (
|
||||
<div className="deposit-reserve">
|
||||
<DepositInfoLine
|
||||
className="deposit-reserve-item"
|
||||
reserve={reserve}
|
||||
address={lendingReserve.pubkey} />
|
||||
address={lendingReserve.pubkey}
|
||||
/>
|
||||
<div className="deposit-reserve-container">
|
||||
<WithdrawInput
|
||||
className="deposit-reserve-item deposit-reserve-item-left"
|
||||
reserve={reserve}
|
||||
address={lendingReserve.pubkey} />
|
||||
address={lendingReserve.pubkey}
|
||||
/>
|
||||
<SideReserveOverview
|
||||
className="deposit-reserve-item deposit-reserve-item-right"
|
||||
reserve={reserve}
|
||||
address={lendingReserve.pubkey}
|
||||
mode={SideReserveOverviewMode.Deposit} />
|
||||
mode={SideReserveOverviewMode.Deposit}
|
||||
/>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue