chore: prettier

This commit is contained in:
bartosz-lipinski 2020-11-20 23:54:50 -06:00
parent 547ab00d7b
commit 5ea0434a68
59 changed files with 1274 additions and 945 deletions

View File

@ -2,11 +2,8 @@ import React from "react";
import "./App.less"; import "./App.less";
import { Routes } from "./routes"; import { Routes } from "./routes";
function App() { function App() {
return ( return <Routes />;
<Routes />
);
} }
export default App; export default App;

View File

@ -1,8 +1,13 @@
import { AccountLayout, Token } from "@solana/spl-token"; 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 { TOKEN_PROGRAM_ID, WRAPPED_SOL_MINT } from "../constants/ids";
import { TokenAccount } from "../models"; import { TokenAccount } from "../models";
import { cache, TokenAccountParser } from './../contexts/accounts'; import { cache, TokenAccountParser } from "./../contexts/accounts";
export function ensureSplAccount( export function ensureSplAccount(
instructions: TransactionInstruction[], instructions: TransactionInstruction[],
@ -20,7 +25,8 @@ export function ensureSplAccount(
instructions, instructions,
payer, payer,
amount, amount,
signers); signers
);
instructions.push( instructions.push(
Token.createInitAccountInstruction( Token.createInitAccountInstruction(
@ -50,7 +56,8 @@ export function createTempMemoryAccount(
instructions: TransactionInstruction[], instructions: TransactionInstruction[],
payer: PublicKey, payer: PublicKey,
signers: Account[], signers: Account[],
space = DEFAULT_TEMP_MEM_SPACE) { space = DEFAULT_TEMP_MEM_SPACE
) {
const account = new Account(); const account = new Account();
instructions.push( instructions.push(
SystemProgram.createAccount({ SystemProgram.createAccount({
@ -68,12 +75,12 @@ export function createTempMemoryAccount(
return account.publicKey; return account.publicKey;
} }
export function createUninitializedAccount( export function createUninitializedAccount(
instructions: TransactionInstruction[], instructions: TransactionInstruction[],
payer: PublicKey, payer: PublicKey,
amount: number, amount: number,
signers: Account[]) { signers: Account[]
) {
const account = new Account(); const account = new Account();
instructions.push( instructions.push(
SystemProgram.createAccount({ SystemProgram.createAccount({
@ -96,21 +103,17 @@ export function createTokenAccount(
accountRentExempt: number, accountRentExempt: number,
mint: PublicKey, mint: PublicKey,
owner: PublicKey, owner: PublicKey,
signers: Account[], signers: Account[]
) { ) {
const account = createUninitializedAccount( const account = createUninitializedAccount(
instructions, instructions,
payer, payer,
accountRentExempt, accountRentExempt,
signers); signers
);
instructions.push( instructions.push(
Token.createInitAccountInstruction( Token.createInitAccountInstruction(TOKEN_PROGRAM_ID, mint, account, owner)
TOKEN_PROGRAM_ID,
mint,
account,
owner
)
); );
return account; return account;
@ -128,8 +131,9 @@ export function findOrCreateAccountByMint(
excluded?: Set<string> excluded?: Set<string>
): PublicKey { ): PublicKey {
const accountToFind = mint.toBase58(); const accountToFind = mint.toBase58();
const account = cache.byParser(TokenAccountParser) const account = cache
.map(id => cache.get(id)) .byParser(TokenAccountParser)
.map((id) => cache.get(id))
.find( .find(
(acc) => (acc) =>
acc !== undefined && acc !== undefined &&
@ -150,7 +154,7 @@ export function findOrCreateAccountByMint(
accountRentExempt, accountRentExempt,
mint, mint,
owner, owner,
signers, signers
); );
if (isWrappedSol) { if (isWrappedSol) {

View File

@ -9,11 +9,20 @@ import { notify } from "../utils/notifications";
import { LendingReserve } from "./../models/lending/reserve"; import { LendingReserve } from "./../models/lending/reserve";
import { AccountLayout, MintInfo, MintLayout, Token } from "@solana/spl-token"; import { AccountLayout, MintInfo, MintLayout, Token } from "@solana/spl-token";
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids"; import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids";
import { createTempMemoryAccount, createUninitializedAccount, ensureSplAccount, findOrCreateAccountByMint } from "./account"; import {
createTempMemoryAccount,
createUninitializedAccount,
ensureSplAccount,
findOrCreateAccountByMint,
} from "./account";
import { cache, MintParser, ParsedAccount } from "../contexts/accounts"; import { cache, MintParser, ParsedAccount } from "../contexts/accounts";
import { TokenAccount, LendingObligationLayout, borrowInstruction, LendingMarket } from "../models"; import {
TokenAccount,
LendingObligationLayout,
borrowInstruction,
LendingMarket,
} from "../models";
import { toLamports } from "../utils/utils"; import { toLamports } from "../utils/utils";
import { DexMarketParser } from "../models/dex";
export const borrow = async ( export const borrow = async (
from: TokenAccount, from: TokenAccount,
@ -26,8 +35,8 @@ export const borrow = async (
depositReserveAddress: PublicKey, depositReserveAddress: PublicKey,
connection: Connection, connection: Connection,
wallet: any) => { wallet: any
) => {
notify({ notify({
message: "Borrowing funds...", message: "Borrowing funds...",
description: "Please review transactions to approve.", description: "Please review transactions to approve.",
@ -48,23 +57,21 @@ export const borrow = async (
await connection.getMinimumBalanceForRentExemption( await connection.getMinimumBalanceForRentExemption(
LendingObligationLayout.span LendingObligationLayout.span
), ),
signers, signers
); );
const obligationMint = createUninitializedAccount( const obligationMint = createUninitializedAccount(
instructions, instructions,
wallet.publicKey, wallet.publicKey,
await connection.getMinimumBalanceForRentExemption( await connection.getMinimumBalanceForRentExemption(MintLayout.span),
MintLayout.span signers
),
signers,
); );
const obligationTokenOutput = createUninitializedAccount( const obligationTokenOutput = createUninitializedAccount(
instructions, instructions,
wallet.publicKey, wallet.publicKey,
accountRentExempt, accountRentExempt,
signers, signers
); );
let toAccount = await findOrCreateAccountByMint( let toAccount = await findOrCreateAccountByMint(
@ -78,7 +85,9 @@ export const borrow = async (
); );
// create all accounts in one transaction // create all accounts in one transaction
let tx = await sendTransaction(connection, wallet, instructions, [...signers]); let tx = await sendTransaction(connection, wallet, instructions, [
...signers,
]);
notify({ notify({
message: "Obligation accounts created", message: "Obligation accounts created",
@ -101,7 +110,11 @@ export const borrow = async (
LENDING_PROGRAM_ID 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 amountLamports = toLamports(amount, mint?.info);
const fromAccount = ensureSplAccount( const fromAccount = ensureSplAccount(
@ -121,27 +134,33 @@ export const borrow = async (
authority, authority,
wallet.publicKey, 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); const dexMarket = cache.get(dexMarketAddress);
if(!dexMarket) { if (!dexMarket) {
throw new Error(`Dex market doesn't exsists.`) throw new Error(`Dex market doesn't exsists.`);
} }
const dexOrderBookSide = market.info.quoteMint.equals(depositReserve.liquidityMint) ? const dexOrderBookSide = market.info.quoteMint.equals(
dexMarket?.info.bids : depositReserve.liquidityMint
dexMarket?.info.asks )
? dexMarket?.info.bids
: dexMarket?.info.asks;
const memory = createTempMemoryAccount( const memory = createTempMemoryAccount(
instructions, instructions,
wallet.publicKey, wallet.publicKey,
signers, signers
); );
// deposit // deposit
@ -165,7 +184,7 @@ export const borrow = async (
dexMarketAddress, dexMarketAddress,
dexOrderBookSide, dexOrderBookSide,
memory, memory
) )
); );
try { try {
@ -185,4 +204,4 @@ export const borrow = async (
} catch { } catch {
// TODO: // TODO:
} }
} };

View File

@ -6,10 +6,18 @@ import {
} from "@solana/web3.js"; } from "@solana/web3.js";
import { sendTransaction } from "../contexts/connection"; import { sendTransaction } from "../contexts/connection";
import { notify } from "../utils/notifications"; 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 { AccountLayout, MintInfo, Token } from "@solana/spl-token";
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids"; import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids";
import { createUninitializedAccount, ensureSplAccount, findOrCreateAccountByMint } from "./account"; import {
createUninitializedAccount,
ensureSplAccount,
findOrCreateAccountByMint,
} from "./account";
import { cache, MintParser, ParsedAccount } from "../contexts/accounts"; import { cache, MintParser, ParsedAccount } from "../contexts/accounts";
import { TokenAccount } from "../models"; import { TokenAccount } from "../models";
import { toLamports } from "../utils/utils"; import { toLamports } from "../utils/utils";
@ -20,8 +28,8 @@ export const deposit = async (
reserve: LendingReserve, reserve: LendingReserve,
reserveAddress: PublicKey, reserveAddress: PublicKey,
connection: Connection, connection: Connection,
wallet: any) => { wallet: any
) => {
// TODO: customize ? // TODO: customize ?
const MAX_UTILIZATION_RATE = 80; const MAX_UTILIZATION_RATE = 80;
@ -47,7 +55,11 @@ export const deposit = async (
LENDING_PROGRAM_ID 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 amountLamports = toLamports(amount, mint?.info);
const fromAccount = ensureSplAccount( const fromAccount = ensureSplAccount(
@ -67,7 +79,7 @@ export const deposit = async (
authority, authority,
wallet.publicKey, wallet.publicKey,
[], [],
amountLamports, amountLamports
) )
); );
@ -88,7 +100,7 @@ export const deposit = async (
instructions, instructions,
wallet.publicKey, wallet.publicKey,
accountRentExempt, accountRentExempt,
signers, signers
); );
} }
@ -102,12 +114,13 @@ export const deposit = async (
authority, authority,
reserveAddress, reserveAddress,
reserve.liquiditySupply, reserve.liquiditySupply,
reserve.collateralMint, reserve.collateralMint
) )
); );
} else { } else {
// TODO: finish reserve init // TODO: finish reserve init
instructions.push(initReserveInstruction( instructions.push(
initReserveInstruction(
amountLamports, amountLamports,
MAX_UTILIZATION_RATE, MAX_UTILIZATION_RATE,
fromAccount, fromAccount,
@ -119,8 +132,9 @@ export const deposit = async (
reserve.collateralSupply, reserve.collateralSupply,
reserve.lendingMarket, reserve.lendingMarket,
authority, authority,
reserve.dexMarket, reserve.dexMarket
)); )
);
} }
try { try {
@ -140,4 +154,4 @@ export const deposit = async (
} catch { } catch {
// TODO: // TODO:
} }
} };

View File

@ -1,4 +1,4 @@
export { borrow } from './borrow'; export { borrow } from "./borrow";
export { deposit } from './deposit'; export { deposit } from "./deposit";
export { withdraw } from './withdraw'; export { withdraw } from "./withdraw";
export * from './account'; export * from "./account";

View File

@ -6,7 +6,10 @@ import {
} from "@solana/web3.js"; } from "@solana/web3.js";
import { sendTransaction } from "../contexts/connection"; import { sendTransaction } from "../contexts/connection";
import { notify } from "../utils/notifications"; 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 { AccountLayout, Token } from "@solana/spl-token";
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids"; import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids";
import { findOrCreateAccountByMint } from "./account"; import { findOrCreateAccountByMint } from "./account";
@ -18,8 +21,8 @@ export const withdraw = async (
reserve: LendingReserve, reserve: LendingReserve,
reserveAddress: PublicKey, reserveAddress: PublicKey,
connection: Connection, connection: Connection,
wallet: any) => { wallet: any
) => {
notify({ notify({
message: "Withdrawing funds...", message: "Withdrawing funds...",
description: "Please review transactions to approve.", description: "Please review transactions to approve.",
@ -50,7 +53,7 @@ export const withdraw = async (
authority, authority,
wallet.publicKey, wallet.publicKey,
[], [],
amountLamports, amountLamports
) )
); );
@ -73,7 +76,7 @@ export const withdraw = async (
reserveAddress, reserveAddress,
reserve.collateralMint, reserve.collateralMint,
reserve.liquiditySupply, reserve.liquiditySupply,
authority, authority
) )
); );
@ -94,4 +97,4 @@ export const withdraw = async (
} catch { } catch {
// TODO: // TODO:
} }
} };

View File

@ -1,5 +1,5 @@
import React, { useCallback, useMemo, useState } from "react"; 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 { LendingReserve, LendingReserveParser } from "../../models";
import { TokenIcon } from "../TokenIcon"; import { TokenIcon } from "../TokenIcon";
import { getTokenName } from "../../utils/utils"; import { getTokenName } from "../../utils/utils";
@ -8,9 +8,9 @@ import { cache, ParsedAccount } from "../../contexts/accounts";
import { NumericInput } from "../Input/numeric"; import { NumericInput } from "../Input/numeric";
import { useConnection, useConnectionConfig } from "../../contexts/connection"; import { useConnection, useConnectionConfig } from "../../contexts/connection";
import { useWallet } from "../../contexts/wallet"; import { useWallet } from "../../contexts/wallet";
import { borrow } from '../../actions'; import { borrow } from "../../actions";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import './style.less'; import "./style.less";
const { Option } = Select; const { Option } = Select;
@ -22,7 +22,8 @@ const CollateralSelector = (props: {
const { reserveAccounts } = useLendingReserves(); const { reserveAccounts } = useLendingReserves();
const { tokenMap } = useConnectionConfig(); const { tokenMap } = useConnectionConfig();
return <Select return (
<Select
size="large" size="large"
showSearch showSearch
style={{ minWidth: 120 }} style={{ minWidth: 120 }}
@ -38,44 +39,54 @@ const CollateralSelector = (props: {
} }
> >
{reserveAccounts {reserveAccounts
.filter(reserve => reserve.info !== props.reserve) .filter((reserve) => reserve.info !== props.reserve)
.map(reserve => { .map((reserve) => {
const mint = reserve.info.liquidityMint.toBase58(); const mint = reserve.info.liquidityMint.toBase58();
const address = reserve.pubkey.toBase58(); const address = reserve.pubkey.toBase58();
const name = getTokenName(tokenMap, mint); const name = getTokenName(tokenMap, mint);
return <Option key={address} value={address} name={name} title={address}> return (
<div key={address} style={{ display: "flex", alignItems: "center" }}> <Option key={address} value={address} name={name} title={address}>
<div
key={address}
style={{ display: "flex", alignItems: "center" }}
>
<TokenIcon mintAddress={mint} /> <TokenIcon mintAddress={mint} />
{name} {name}
</div> </div>
</Option> </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 connection = useConnection();
const { wallet } = useWallet(); const { wallet } = useWallet();
const [value, setValue] = useState(''); const [value, setValue] = useState("");
const borrowReserve = props.reserve; const borrowReserve = props.reserve;
const borrowReserveAddress = props.address; const borrowReserveAddress = props.address;
const [collateralReserveMint, setCollateralReserveMint] = useState<string>(); const [collateralReserveMint, setCollateralReserveMint] = useState<string>();
const collateralReserve = useMemo(() => { const collateralReserve = useMemo(() => {
const id: string = cache.byParser(LendingReserveParser) const id: string =
.find(acc => acc === collateralReserveMint) || ''; cache
.byParser(LendingReserveParser)
.find((acc) => acc === collateralReserveMint) || "";
return cache.get(id) as ParsedAccount<LendingReserve>; return cache.get(id) as ParsedAccount<LendingReserve>;
}, [collateralReserveMint]) }, [collateralReserveMint]);
const name = useTokenName(borrowReserve?.liquidityMint); const name = useTokenName(borrowReserve?.liquidityMint);
const { const { accounts: fromAccounts } = useUserBalance(
accounts: fromAccounts collateralReserve?.info.collateralMint
} = useUserBalance(collateralReserve?.info.collateralMint); );
// const collateralBalance = useUserBalance(reserve?.collateralMint); // const collateralBalance = useUserBalance(reserve?.collateralMint);
const onBorrow = useCallback(() => { const onBorrow = useCallback(() => {
@ -91,26 +102,42 @@ export const BorrowInput = (props: { className?: string, reserve: LendingReserve
collateralReserve.info, collateralReserve.info,
collateralReserve.pubkey, collateralReserve.pubkey,
connection, connection,
wallet); wallet
}, [value, borrowReserve, fromAccounts, borrowReserveAddress]); );
}, [
connection,
wallet,
value,
collateralReserve,
borrowReserve,
fromAccounts,
borrowReserveAddress,
]);
const bodyStyle: React.CSSProperties = { const bodyStyle: React.CSSProperties = {
display: 'flex', display: "flex",
flex: 1, flex: 1,
justifyContent: 'center', justifyContent: "center",
alignItems: 'center', alignItems: "center",
height: '100%', height: "100%",
}; };
return <Card className={props.className} bodyStyle={bodyStyle}> return (
<Card className={props.className} bodyStyle={bodyStyle}>
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around' }}> <div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "space-around",
}}
>
<div className="borrow-input-title"> <div className="borrow-input-title">
How much would you like to borrow? How much would you like to borrow?
</div> </div>
<div className="token-input"> <div className="token-input">
<TokenIcon mintAddress={borrowReserve?.liquidityMint} /> <TokenIcon mintAddress={borrowReserve?.liquidityMint} />
<NumericInput value={value} <NumericInput
value={value}
onChange={(val: any) => { onChange={(val: any) => {
setValue(val); setValue(val);
}} }}
@ -125,16 +152,21 @@ export const BorrowInput = (props: { className?: string, reserve: LendingReserve
/> />
<div>{name}</div> <div>{name}</div>
</div> </div>
<div className="borrow-input-title"> <div className="borrow-input-title">Select collateral account?</div>
Select collateral account?
</div>
<CollateralSelector <CollateralSelector
reserve={borrowReserve} reserve={borrowReserve}
mint={collateralReserveMint} mint={collateralReserveMint}
onMintChange={setCollateralReserveMint} 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> </div>
</Card >; </Card>
} );
};

View File

@ -18,13 +18,13 @@ export const CurrentUserBadge = (props: {}) => {
return ( return (
<div className="wallet-wrapper"> <div className="wallet-wrapper">
<span> <span>
{formatNumber.format(((account?.lamports || 0) / LAMPORTS_PER_SOL))} SOL {formatNumber.format((account?.lamports || 0) / LAMPORTS_PER_SOL)} SOL
</span> </span>
<div className="wallet-key"> <div className="wallet-key">
{shortenAddress(`${wallet.publicKey}`)} {shortenAddress(`${wallet.publicKey}`)}
<Identicon <Identicon
address={wallet.publicKey.toBase58()} address={wallet.publicKey?.toBase58()}
style={{ marginLeft: "0.5rem", display: 'flex' }} style={{ marginLeft: "0.5rem", display: "flex" }}
/> />
</div> </div>
</div> </div>

View File

@ -1,31 +1,45 @@
import React, { } from "react"; import React from "react";
import { useTokenName, useUserBalance, useCollateralBalance } from './../../hooks'; import {
useTokenName,
useUserBalance,
useCollateralBalance,
} from "./../../hooks";
import { LendingReserve } from "../../models/lending"; import { LendingReserve } from "../../models/lending";
import { formatNumber } from "../../utils/utils"; import { formatNumber } from "../../utils/utils";
import { Card } from "antd"; import { Card } from "antd";
import './style.less'; import "./style.less";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
export const DepositInfoLine = (props: { export const DepositInfoLine = (props: {
className?: string, className?: string;
reserve: LendingReserve, reserve: LendingReserve;
address: PublicKey }) => { address: PublicKey;
}) => {
const name = useTokenName(props.reserve.liquidityMint); const name = useTokenName(props.reserve.liquidityMint);
const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint); const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
const { balance: collateralBalance } = useCollateralBalance(props.reserve); 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 className="deposit-info-line-item ">
<div>Your balance in Oyster</div> <div>Your balance in Oyster</div>
<div>{formatNumber.format(collateralBalance)} {name}</div> <div>
{formatNumber.format(collateralBalance)} {name}
</div>
</div> </div>
<div className="deposit-info-line-item "> <div className="deposit-info-line-item ">
<div>Your wallet balance</div> <div>Your wallet balance</div>
<div>{formatNumber.format(tokenBalance)} {name}</div> <div>
{formatNumber.format(tokenBalance)} {name}
</div>
</div> </div>
<div className="deposit-info-line-item "> <div className="deposit-info-line-item ">
<div>Health factor</div> <div>Health factor</div>
<div>--</div> <div>--</div>
</div> </div>
</Card> </Card>
} );
};

View File

@ -1,19 +1,23 @@
import React, { useCallback, useState } from "react"; import React, { useCallback, useState } from "react";
import { useTokenName, useUserBalance } from '../../hooks'; import { useTokenName, useUserBalance } from "../../hooks";
import { LendingReserve } from "../../models/lending"; import { LendingReserve } from "../../models/lending";
import { TokenIcon } from "../TokenIcon"; import { TokenIcon } from "../TokenIcon";
import { Button, Card } from "antd"; import { Button, Card } from "antd";
import { NumericInput } from "../Input/numeric"; import { NumericInput } from "../Input/numeric";
import { useConnection } from "../../contexts/connection"; import { useConnection } from "../../contexts/connection";
import { useWallet } from "../../contexts/wallet"; import { useWallet } from "../../contexts/wallet";
import { deposit } from '../../actions/deposit'; import { deposit } from "../../actions/deposit";
import { PublicKey } from "@solana/web3.js"; 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 connection = useConnection();
const { wallet } = useWallet(); const { wallet } = useWallet();
const [value, setValue] = useState(''); const [value, setValue] = useState("");
const reserve = props.reserve; const reserve = props.reserve;
const address = props.address; const address = props.address;
@ -29,26 +33,34 @@ export const DepositInput = (props: { className?: string, reserve: LendingReserv
reserve, reserve,
address, address,
connection, connection,
wallet); wallet
}, [value, reserve, fromAccounts, address]); );
}, [connection, wallet, value, reserve, fromAccounts, address]);
const bodyStyle: React.CSSProperties = { const bodyStyle: React.CSSProperties = {
display: 'flex', display: "flex",
flex: 1, flex: 1,
justifyContent: 'center', justifyContent: "center",
alignItems: 'center', alignItems: "center",
height: '100%', height: "100%",
}; };
return <Card className={props.className} bodyStyle={bodyStyle}> return (
<Card className={props.className} bodyStyle={bodyStyle}>
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around' }}> <div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "space-around",
}}
>
<div className="deposit-input-title"> <div className="deposit-input-title">
How much would you like to deposit? How much would you like to deposit?
</div> </div>
<div className="token-input"> <div className="token-input">
<TokenIcon mintAddress={reserve?.liquidityMint} /> <TokenIcon mintAddress={reserve?.liquidityMint} />
<NumericInput value={value} <NumericInput
value={value}
onChange={(val: any) => { onChange={(val: any) => {
setValue(val); setValue(val);
}} }}
@ -64,7 +76,14 @@ export const DepositInput = (props: { className?: string, reserve: LendingReserv
<div>{name}</div> <div>{name}</div>
</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> </div>
</Card >; </Card>
} );
};

View File

@ -11,13 +11,16 @@ export const Identicon = (props: {
className?: string; className?: string;
}) => { }) => {
const { style, className } = props; 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>(); const ref = useRef<HTMLDivElement>();
useEffect(() => { useEffect(() => {
if (address && ref.current) { if (address && ref.current) {
ref.current.innerHTML = ""; ref.current.innerHTML = "";
ref.current.className = props.className || ""; ref.current.className = className || "";
ref.current.appendChild( ref.current.appendChild(
Jazzicon( Jazzicon(
style?.width || 16, style?.width || 16,

View File

@ -1,16 +1,16 @@
import React from "react"; import React from "react";
import "./../../App.less"; import "./../../App.less";
import { Menu } from 'antd'; import { Menu } from "antd";
import { import {
PieChartOutlined, PieChartOutlined,
GithubOutlined, GithubOutlined,
BankOutlined, BankOutlined,
LogoutOutlined, LogoutOutlined,
HomeOutlined, HomeOutlined,
RocketOutlined RocketOutlined,
} from '@ant-design/icons'; } from "@ant-design/icons";
import BasicLayout, { } from '@ant-design/pro-layout'; import BasicLayout from "@ant-design/pro-layout";
import { AppBar } from "./../AppBar"; import { AppBar } from "./../AppBar";
import { Link, useLocation } from "react-router-dom"; import { Link, useLocation } from "react-router-dom";
import { useConnectionConfig } from "../../contexts/connection"; import { useConnectionConfig } from "../../contexts/connection";
@ -19,16 +19,17 @@ export const AppLayout = (props: any) => {
const { env } = useConnectionConfig(); const { env } = useConnectionConfig();
const location = useLocation(); const location = useLocation();
console.log(location.pathname) console.log(location.pathname);
const paths: { [key: string]: string } = { const paths: { [key: string]: string } = {
'/dashboard': '2', "/dashboard": "2",
'/deposit': '3', "/deposit": "3",
'/borrow': '4', "/borrow": "4",
'/faucet': '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"; const defaultKey = paths[current] || "1";
return ( return (
@ -38,7 +39,8 @@ export const AppLayout = (props: any) => {
Oyster Lending is unaudited software. Use at your own risk. Oyster Lending is unaudited software. Use at your own risk.
</div> </div>
</div> </div>
<BasicLayout title="Oyster Lending" <BasicLayout
title="Oyster Lending"
navTheme="realDark" navTheme="realDark"
headerTheme="dark" headerTheme="dark"
theme="dark" theme="dark"
@ -47,10 +49,13 @@ export const AppLayout = (props: any) => {
logo={<div className="App-logo" />} logo={<div className="App-logo" />}
rightContentRender={() => <AppBar />} rightContentRender={() => <AppBar />}
links={[ links={[
<div title="Fork"><GithubOutlined /></div> <div title="Fork">
<GithubOutlined />
</div>,
]} ]}
menuContentRender={() => { menuContentRender={() => {
return <Menu theme="dark" defaultSelectedKeys={[defaultKey]} mode="inline"> return (
<Menu theme="dark" defaultSelectedKeys={[defaultKey]} mode="inline">
<Menu.Item key="1" icon={<HomeOutlined />}> <Menu.Item key="1" icon={<HomeOutlined />}>
<Link <Link
to={{ to={{
@ -60,7 +65,7 @@ export const AppLayout = (props: any) => {
Home Home
</Link> </Link>
</Menu.Item> </Menu.Item>
<Menu.Item key="2" icon={<PieChartOutlined />} > <Menu.Item key="2" icon={<PieChartOutlined />}>
<Link <Link
to={{ to={{
pathname: "/dashboard", pathname: "/dashboard",
@ -87,7 +92,8 @@ export const AppLayout = (props: any) => {
Borrow Borrow
</Link> </Link>
</Menu.Item> </Menu.Item>
{env !== "mainnet-beta" && <Menu.Item key="5" icon={<RocketOutlined />}> {env !== "mainnet-beta" && (
<Menu.Item key="5" icon={<RocketOutlined />}>
<Link <Link
to={{ to={{
pathname: "/faucet", pathname: "/faucet",
@ -95,13 +101,14 @@ export const AppLayout = (props: any) => {
> >
Faucet Faucet
</Link> </Link>
</Menu.Item>} </Menu.Item>
)}
</Menu> </Menu>
);
}} }}
> >
{props.children} {props.children}
</BasicLayout> </BasicLayout>
</div> </div>
); );
} };

View File

@ -1,26 +1,35 @@
import React, { useState } from "react"; import React from "react";
import { LendingReserve } from "../../models/lending"; import { LendingReserve } from "../../models/lending";
import { Card } from "antd"; import { Card } from "antd";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import './style.less'; import "./style.less";
export const ReserveStatus = (props: { className?: string, reserve: LendingReserve, address: PublicKey }) => {
const [] = useState('');
export const ReserveStatus = (props: {
className?: string;
reserve: LendingReserve;
address: PublicKey;
}) => {
const bodyStyle: React.CSSProperties = { const bodyStyle: React.CSSProperties = {
display: 'flex', display: "flex",
justifyContent: 'center', justifyContent: "center",
alignItems: 'center' alignItems: "center",
}; };
return <Card className={props.className} return (
title={ <Card
<>Reserve Status &amp; Configuration</> className={props.className}
} title={<>Reserve Status &amp; Configuration</>}
bodyStyle={bodyStyle}> bodyStyle={bodyStyle}
>
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around' }}> <div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "space-around",
}}
>
TODO: Reserve Status - add chart TODO: Reserve Status - add chart
</div> </div>
</Card >; </Card>
} );
};

View File

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { useTokenName } from './../../hooks'; import { useTokenName } from "./../../hooks";
import { LendingReserve } from "../../models/lending"; import { LendingReserve } from "../../models/lending";
import { TokenIcon } from "../../components/TokenIcon"; import { TokenIcon } from "../../components/TokenIcon";
import { formatNumber, formatPct, fromLamports } from "../../utils/utils"; import { formatNumber, formatPct, fromLamports } from "../../utils/utils";
@ -10,22 +10,25 @@ import { PublicKey } from "@solana/web3.js";
const { Text } = Typography; const { Text } = Typography;
export enum SideReserveOverviewMode { export enum SideReserveOverviewMode {
Deposit = 'deposit', Deposit = "deposit",
Borrow = 'borrow' Borrow = "borrow",
} }
export const SideReserveOverview = (props: { export const SideReserveOverview = (props: {
className?: string; className?: string;
reserve: LendingReserve, reserve: LendingReserve;
address: PublicKey, address: PublicKey;
mode: SideReserveOverviewMode mode: SideReserveOverviewMode;
}) => { }) => {
const reserve = props.reserve; const reserve = props.reserve;
const mode = props.mode; const mode = props.mode;
const name = useTokenName(reserve?.liquidityMint); const name = useTokenName(reserve?.liquidityMint);
const liquidityMint = useMint(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
);
// TODO: calculate // TODO: calculate
const depositApy = 0.048; const depositApy = 0.048;
@ -38,24 +41,20 @@ export const SideReserveOverview = (props: {
let extraInfo: JSX.Element | null = null; let extraInfo: JSX.Element | null = null;
if (mode === SideReserveOverviewMode.Deposit) { if (mode === SideReserveOverviewMode.Deposit) {
extraInfo = (
extraInfo = <> <>
<div className="card-row"> <div className="card-row">
<Text type="secondary" className="card-cell "> <Text type="secondary" className="card-cell ">
Deposit APY: Deposit APY:
</Text> </Text>
<div className="card-cell "> <div className="card-cell ">{formatPct.format(depositApy)}</div>
{formatPct.format(depositApy)}
</div>
</div> </div>
<div className="card-row"> <div className="card-row">
<Text type="secondary" className="card-cell "> <Text type="secondary" className="card-cell ">
Maxiumum LTV: Maxiumum LTV:
</Text> </Text>
<div className="card-cell "> <div className="card-cell ">{formatPct.format(maxLTV)}</div>
{formatPct.format(maxLTV)}
</div>
</div> </div>
<div className="card-row"> <div className="card-row">
@ -75,32 +74,46 @@ export const SideReserveOverview = (props: {
{formatPct.format(liquidiationPenalty)} {formatPct.format(liquidiationPenalty)}
</div> </div>
</div> </div>
</>; </>
);
} else if (mode === SideReserveOverviewMode.Borrow) { } else if (mode === SideReserveOverviewMode.Borrow) {
extraInfo = <> extraInfo = (
<>
<div className="card-row"> <div className="card-row">
<Text type="secondary" className="card-cell "> <Text type="secondary" className="card-cell ">
Borrow APY: Borrow APY:
</Text> </Text>
<div className="card-cell "> <div className="card-cell ">{formatPct.format(borrowApy)}</div>
{formatPct.format(borrowApy)}
</div> </div>
</div> </>
</>; );
} }
return <Card className={props.className} title={ return (
<div style={{ display: 'flex', alignItems: 'center', fontSize: '1.2rem', justifyContent: 'center' }}> <Card
<TokenIcon mintAddress={reserve?.liquidityMint} style={{ width: 30, height: 30 }} /> {name} Reserve Overview 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>
}> }
>
<div className="card-row"> <div className="card-row">
<Text type="secondary" className="card-cell "> <Text type="secondary" className="card-cell ">
Utilization rate: Utilization rate:
</Text> </Text>
<div className="card-cell "> <div className="card-cell ">{formatPct.format(utilizationRate)}</div>
{formatPct.format(utilizationRate)}
</div>
</div> </div>
<div className="card-row"> <div className="card-row">
@ -113,5 +126,6 @@ export const SideReserveOverview = (props: {
</div> </div>
{extraInfo} {extraInfo}
</Card>; </Card>
} );
};

View File

@ -18,8 +18,8 @@ export const TokenIcon = (props: {
alt="Token icon" alt="Token icon"
className={props.className} className={props.className}
key={icon} key={icon}
width={props.style?.width || '20'} width={props.style?.width || "20"}
height={props.style?.height || '20'} height={props.style?.height || "20"}
src={icon} src={icon}
style={{ style={{
marginRight: "0.5rem", marginRight: "0.5rem",

View File

@ -1,18 +1,21 @@
import React from "react"; import React from "react";
import { useCollateralBalance, useTokenName, useUserBalance } from './../../hooks'; import {
useCollateralBalance,
useTokenName,
useUserBalance,
} from "./../../hooks";
import { LendingReserve } from "../../models/lending"; import { LendingReserve } from "../../models/lending";
import { formatNumber } from "../../utils/utils"; import { formatNumber } from "../../utils/utils";
import { Button, Card, Typography } from "antd"; import { Button, Card, Typography } from "antd";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { useMint } from "../../contexts/accounts";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
const { Text } = Typography; const { Text } = Typography;
export const UserLendingCard = (props: { export const UserLendingCard = (props: {
className?: string; className?: string;
reserve: LendingReserve, reserve: LendingReserve;
address: PublicKey, address: PublicKey;
}) => { }) => {
const reserve = props.reserve; const reserve = props.reserve;
const address = props.address; const address = props.address;
@ -22,19 +25,28 @@ export const UserLendingCard = (props: {
const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint); const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
const { balance: collateralBalance } = useCollateralBalance(props.reserve); const { balance: collateralBalance } = useCollateralBalance(props.reserve);
// TODO: calculate // TODO: calculate
const borrowed = 0; const borrowed = 0;
const healthFactor = '--'; const healthFactor = "--";
const ltv = 0; const ltv = 0;
const available = 0; const available = 0;
return (
return <Card className={props.className} title={ <Card
<div style={{ display: 'flex', alignItems: 'center', fontSize: '1.2rem', justifyContent: 'center' }}> className={props.className}
title={
<div
style={{
display: "flex",
alignItems: "center",
fontSize: "1.2rem",
justifyContent: "center",
}}
>
Your Information Your Information
</div> </div>
}> }
>
<h3>Borrows</h3> <h3>Borrows</h3>
<div className="card-row"> <div className="card-row">
@ -50,18 +62,14 @@ export const UserLendingCard = (props: {
<Text type="secondary" className="card-cell "> <Text type="secondary" className="card-cell ">
Health factor: Health factor:
</Text> </Text>
<div className="card-cell "> <div className="card-cell ">{healthFactor}</div>
{healthFactor}
</div>
</div> </div>
<div className="card-row"> <div className="card-row">
<Text type="secondary" className="card-cell "> <Text type="secondary" className="card-cell ">
Loan to value: Loan to value:
</Text> </Text>
<div className="card-cell "> <div className="card-cell ">{formatNumber.format(ltv)}</div>
{formatNumber.format(ltv)}
</div>
</div> </div>
<div className="card-row"> <div className="card-row">
@ -93,7 +101,10 @@ export const UserLendingCard = (props: {
</div> </div>
</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}`}> <Link to={`/deposit/${address}`}>
<Button>Deposit</Button> <Button>Deposit</Button>
</Link> </Link>
@ -107,7 +118,6 @@ export const UserLendingCard = (props: {
<Button disabled={true}>Repay</Button> <Button disabled={true}>Repay</Button>
</Link> </Link>
</div> </div>
</Card>
);
</Card>; };
}

View File

@ -1,30 +1,39 @@
import React, { useCallback, useState } from "react"; import React, { useCallback, useState } from "react";
import { useCollateralBalance, useTokenName, useUserBalance } from '../../hooks'; import {
useCollateralBalance,
useTokenName,
useUserBalance,
} from "../../hooks";
import { LendingReserve } from "../../models/lending"; import { LendingReserve } from "../../models/lending";
import { TokenIcon } from "../TokenIcon"; import { TokenIcon } from "../TokenIcon";
import { Button, Card } from "antd"; import { Button, Card } from "antd";
import { NumericInput } from "../Input/numeric"; import { NumericInput } from "../Input/numeric";
import { useConnection } from "../../contexts/connection"; import { useConnection } from "../../contexts/connection";
import { useWallet } from "../../contexts/wallet"; import { useWallet } from "../../contexts/wallet";
import { withdraw } from '../../actions'; import { withdraw } from "../../actions";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import './style.less'; import "./style.less";
import { useMint } from "../../contexts/accounts";
export const WithdrawInput = (props: { className?: string, reserve: LendingReserve, address: PublicKey }) => { export const WithdrawInput = (props: {
className?: string;
reserve: LendingReserve;
address: PublicKey;
}) => {
const connection = useConnection(); const connection = useConnection();
const { wallet } = useWallet(); const { wallet } = useWallet();
const [value, setValue] = useState(''); const [value, setValue] = useState("");
const reserve = props.reserve; const reserve = props.reserve;
const address = props.address; const address = props.address;
const liquidityMint = useMint(reserve?.liquidityMint);
const name = useTokenName(reserve?.liquidityMint); const name = useTokenName(reserve?.liquidityMint);
const { balanceLamports: collateralBalanceLamports, accounts: fromAccounts } = useUserBalance(reserve?.collateralMint);
const { const {
balance: collateralBalanceInLiquidity balanceLamports: collateralBalanceLamports,
} = useCollateralBalance(reserve); accounts: fromAccounts,
} = useUserBalance(reserve?.collateralMint);
const { balance: collateralBalanceInLiquidity } = useCollateralBalance(
reserve
);
const onWithdraw = useCallback(() => { const onWithdraw = useCallback(() => {
withdraw( withdraw(
@ -36,26 +45,43 @@ export const WithdrawInput = (props: { className?: string, reserve: LendingReser
reserve, reserve,
address, address,
connection, connection,
wallet); wallet
}, [value, reserve, fromAccounts, address, liquidityMint]); );
}, [
connection,
wallet,
collateralBalanceLamports,
collateralBalanceInLiquidity,
value,
reserve,
fromAccounts,
address,
]);
const bodyStyle: React.CSSProperties = { const bodyStyle: React.CSSProperties = {
display: 'flex', display: "flex",
flex: 1, flex: 1,
justifyContent: 'center', justifyContent: "center",
alignItems: 'center', alignItems: "center",
height: '100%', height: "100%",
}; };
return <Card className={props.className} bodyStyle={bodyStyle}> return (
<Card className={props.className} bodyStyle={bodyStyle}>
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around' }}> <div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "space-around",
}}
>
<div className="withdraw-input-title"> <div className="withdraw-input-title">
How much would you like to withdraw? How much would you like to withdraw?
</div> </div>
<div className="token-input"> <div className="token-input">
<TokenIcon mintAddress={reserve?.liquidityMint} /> <TokenIcon mintAddress={reserve?.liquidityMint} />
<NumericInput value={value} <NumericInput
value={value}
onChange={(val: any) => { onChange={(val: any) => {
setValue(val); setValue(val);
}} }}
@ -71,7 +97,14 @@ export const WithdrawInput = (props: { className?: string, reserve: LendingReser
<div>{name}</div> <div>{name}</div>
</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> </div>
</Card >; </Card>
} );
};

View File

@ -3,7 +3,7 @@ import { useConnection } from "./connection";
import { useWallet } from "./wallet"; import { useWallet } from "./wallet";
import { AccountInfo, Connection, PublicKey } from "@solana/web3.js"; import { AccountInfo, Connection, PublicKey } from "@solana/web3.js";
import { programIds, WRAPPED_SOL_MINT } from "./../constants/ids"; 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 { TokenAccount } from "./../models";
import { chunks } from "./../utils/utils"; import { chunks } from "./../utils/utils";
import { EventEmitter } from "./../utils/eventEmitter"; import { EventEmitter } from "./../utils/eventEmitter";
@ -46,7 +46,10 @@ export const MintParser = (pubKey: PublicKey, info: AccountInfo<Buffer>) => {
return details; 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 buffer = Buffer.from(info.data);
const data = deserializeAccount(buffer); const data = deserializeAccount(buffer);
@ -118,8 +121,12 @@ export const cache = {
return query; return query;
}, },
add: (id: PublicKey | string, obj: AccountInfo<Buffer>, parser?: AccountParser) => { add: (
const address = typeof id === 'string' ? id : id?.toBase58(); id: PublicKey | string,
obj: AccountInfo<Buffer>,
parser?: AccountParser
) => {
const address = typeof id === "string" ? id : id?.toBase58();
const deserialize = parser ? parser : keyToAccountParser.get(address); const deserialize = parser ? parser : keyToAccountParser.get(address);
if (!deserialize) { if (!deserialize) {
throw new Error( throw new Error(
@ -160,7 +167,7 @@ export const cache = {
}, },
registerParser: (pubkey: PublicKey | string, parser: AccountParser) => { registerParser: (pubkey: PublicKey | string, parser: AccountParser) => {
if (pubkey) { if (pubkey) {
const address = typeof pubkey === 'string' ? pubkey : pubkey?.toBase58(); const address = typeof pubkey === "string" ? pubkey : pubkey?.toBase58();
keyToAccountParser.set(address, parser); keyToAccountParser.set(address, parser);
} }
@ -172,7 +179,7 @@ export const useAccountsContext = () => {
const context = useContext(AccountsContext); const context = useContext(AccountsContext);
return context; return context;
} };
function wrapNativeAccount( function wrapNativeAccount(
pubkey: PublicKey, pubkey: PublicKey,
@ -206,13 +213,16 @@ const UseNativeAccount = () => {
const [nativeAccount, setNativeAccount] = useState<AccountInfo<Buffer>>(); const [nativeAccount, setNativeAccount] = useState<AccountInfo<Buffer>>();
const updateCache = useCallback((account) => { const updateCache = useCallback(
(account) => {
const wrapped = wrapNativeAccount(wallet.publicKey, account); const wrapped = wrapNativeAccount(wallet.publicKey, account);
if (wrapped !== undefined && wallet) { if (wrapped !== undefined && wallet) {
cache.registerParser(wallet.publicKey.toBase58(), TokenAccountParser); cache.registerParser(wallet.publicKey?.toBase58(), TokenAccountParser);
genericCache.set(wallet.publicKey.toBase58(), wrapped as TokenAccount); genericCache.set(wallet.publicKey?.toBase58(), wrapped as TokenAccount);
} }
}, [wallet]); },
[wallet]
);
useEffect(() => { useEffect(() => {
if (!connection || !wallet?.publicKey) { if (!connection || !wallet?.publicKey) {
@ -231,7 +241,7 @@ const UseNativeAccount = () => {
setNativeAccount(acc); setNativeAccount(acc);
} }
}); });
}, [setNativeAccount, wallet, wallet.publicKey, connection]); }, [setNativeAccount, wallet, wallet.publicKey, connection, updateCache]);
return { nativeAccount }; return { nativeAccount };
}; };
@ -252,8 +262,7 @@ const precacheUserTokenAccounts = async (
const accounts = await connection.getTokenAccountsByOwner(owner, { const accounts = await connection.getTokenAccountsByOwner(owner, {
programId: programIds().token, programId: programIds().token,
}); });
accounts.value accounts.value.forEach((info) => {
.forEach((info) => {
cache.add(info.pubkey.toBase58(), info.account, TokenAccountParser); cache.add(info.pubkey.toBase58(), info.account, TokenAccountParser);
}); });
}; };
@ -266,15 +275,21 @@ export function AccountsProvider({ children = null as any }) {
const { nativeAccount } = UseNativeAccount(); const { nativeAccount } = UseNativeAccount();
const selectUserAccounts = useCallback(() => { const selectUserAccounts = useCallback(() => {
return cache.byParser(TokenAccountParser).map(id => cache.get(id)).filter( return cache
(a) => a && a.info.owner.toBase58() === wallet.publicKey.toBase58() .byParser(TokenAccountParser)
).map(a => a as TokenAccount); .map((id) => cache.get(id))
.filter(
(a) => a && a.info.owner.toBase58() === wallet.publicKey?.toBase58()
)
.map((a) => a as TokenAccount);
}, [wallet]); }, [wallet]);
useEffect(() => { useEffect(() => {
const accounts = selectUserAccounts().filter((a) => a !== undefined) as TokenAccount[]; const accounts = selectUserAccounts().filter(
(a) => a !== undefined
) as TokenAccount[];
setUserAccounts(accounts); setUserAccounts(accounts);
}, [nativeAccount, wallet, tokenAccounts]); }, [nativeAccount, wallet, tokenAccounts, selectUserAccounts]);
const publicKey = wallet?.publicKey; const publicKey = wallet?.publicKey;
useEffect(() => { useEffect(() => {
@ -296,10 +311,7 @@ export function AccountsProvider({ children = null as any }) {
if (info.accountInfo.data.length === AccountLayout.span) { if (info.accountInfo.data.length === AccountLayout.span) {
const data = deserializeAccount(info.accountInfo.data); const data = deserializeAccount(info.accountInfo.data);
if ( if (PRECACHED_OWNERS.has(data.owner.toBase58()) || !cache.get(id)) {
PRECACHED_OWNERS.has(data.owner.toBase58()) ||
!cache.get(id)
) {
cache.add(id, info.accountInfo, TokenAccountParser); cache.add(id, info.accountInfo, TokenAccountParser);
setTokenAccounts(selectUserAccounts()); setTokenAccounts(selectUserAccounts());
accountEmitter.raiseAccountUpdated(id); accountEmitter.raiseAccountUpdated(id);
@ -359,7 +371,8 @@ export const getMultipleAccounts = async (
const array = result const array = result
.map( .map(
(a) => (a) =>
a.array.map((acc) => { a.array
.map((acc) => {
if (!acc) { if (!acc) {
return; return;
} }
@ -370,7 +383,8 @@ export const getMultipleAccounts = async (
data: Buffer.from(data[0], "base64"), data: Buffer.from(data[0], "base64"),
} as AccountInfo<Buffer>; } as AccountInfo<Buffer>;
return obj; return obj;
}).filter(_ => _) as AccountInfo<Buffer>[] })
.filter((_) => _) as AccountInfo<Buffer>[]
) )
.flat(); .flat();
return { keys, array }; return { keys, array };
@ -412,15 +426,15 @@ export function useMint(key?: string | PublicKey) {
cache cache
.query(connection, id, MintParser) .query(connection, id, MintParser)
.then(acc => setMint(acc.info as any)) .then((acc) => setMint(acc.info as any))
.catch((err) => .catch((err) => console.log(err));
console.log(err)
);
const dispose = cache.emitter.onCache((e) => { const dispose = cache.emitter.onCache((e) => {
const event = e; const event = e;
if (event.id === id) { 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 () => { return () => {
@ -443,9 +457,9 @@ export function useAccount(pubKey?: PublicKey) {
return; return;
} }
const acc = await cache.query(connection, key, TokenAccountParser).catch((err) => const acc = await cache
console.log(err) .query(connection, key, TokenAccountParser)
); .catch((err) => console.log(err));
if (acc) { if (acc) {
setAccount(acc); setAccount(acc);
} }

View File

@ -10,13 +10,18 @@ import React, { useContext, useEffect, useMemo, useState } from "react";
import { setProgramIds } from "./../constants/ids"; import { setProgramIds } from "./../constants/ids";
import { notify } from "./../utils/notifications"; import { notify } from "./../utils/notifications";
import { ExplorerLink } from "../components/ExplorerLink"; import { ExplorerLink } from "../components/ExplorerLink";
import LocalTokens from '../config/tokens.json'; import LocalTokens from "../config/tokens.json";
export type ENV = "mainnet-beta" | "testnet" | "devnet" | "localnet" | "lending"; export type ENV =
| "mainnet-beta"
| "testnet"
| "devnet"
| "localnet"
| "lending";
export const ENDPOINTS = [ export const ENDPOINTS = [
{ {
name: 'lending' as ENV, name: "lending" as ENV,
endpoint: "https://tln.solana.com", endpoint: "https://tln.solana.com",
}, },
{ {
@ -88,7 +93,7 @@ export function ConnectionProvider({ children = undefined as any }) {
.then((res) => { .then((res) => {
return res.json(); return res.json();
}) })
.catch(err => []) .catch((err) => [])
.then((list: KnownToken[]) => { .then((list: KnownToken[]) => {
const knownMints = [...LocalTokens, ...list].reduce((map, item) => { const knownMints = [...LocalTokens, ...list].reduce((map, item) => {
map.set(item.mintAddress, item); map.set(item.mintAddress, item);

View File

@ -1,14 +1,23 @@
import React, { useCallback, useEffect, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
import { useConnection } from "./connection"; import { useConnection } from "./connection";
import { LENDING_PROGRAM_ID } from "./../constants/ids"; import { LENDING_PROGRAM_ID } from "./../constants/ids";
import { LendingMarketParser, isLendingReserve, isLendingMarket, LendingReserveParser, LendingReserve } from "./../models/lending"; import {
import { cache, getMultipleAccounts, MintParser, ParsedAccount } from "./accounts"; LendingMarketParser,
isLendingReserve,
isLendingMarket,
LendingReserveParser,
LendingReserve,
} from "./../models/lending";
import {
cache,
getMultipleAccounts,
MintParser,
ParsedAccount,
} from "./accounts";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import { DexMarketParser } from "../models/dex"; import { DexMarketParser } from "../models/dex";
export interface LendingContextState { export interface LendingContextState {}
}
const LendingContext = React.createContext<LendingContextState | null>(null); const LendingContext = React.createContext<LendingContextState | null>(null);
@ -17,14 +26,13 @@ export function LendingProvider({ children = null as any }) {
return ( return (
<LendingContext.Provider <LendingContext.Provider
value={{ value={{
accounts accounts,
}} }}
> >
{children} {children}
</LendingContext.Provider> </LendingContext.Provider>
); );
}; }
export const useLending = () => { export const useLending = () => {
const connection = useConnection(); const connection = useConnection();
@ -32,9 +40,17 @@ export const useLending = () => {
const processAccount = useCallback((item) => { const processAccount = useCallback((item) => {
if (isLendingReserve(item.account)) { 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)) { } 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 queryLendingAccounts = async () => {
const accounts = (await connection.getProgramAccounts(LENDING_PROGRAM_ID)) const accounts = (await connection.getProgramAccounts(LENDING_PROGRAM_ID))
.map(processAccount) .map(processAccount)
.filter(item => item !== undefined); .filter((item) => item !== undefined);
const toQuery = [ const toQuery = [
...accounts.filter(acc => (acc?.info as LendingReserve).lendingMarket !== undefined) ...accounts
.map(acc => acc as ParsedAccount<LendingReserve>) .filter(
.map(acc => { (acc) => (acc?.info as LendingReserve).lendingMarket !== undefined
)
.map((acc) => acc as ParsedAccount<LendingReserve>)
.map((acc) => {
const result = [ const result = [
cache.registerParser(acc?.info.collateralMint.toBase58(), MintParser), cache.registerParser(
cache.registerParser(acc?.info.liquidityMint.toBase58(), MintParser), acc?.info.collateralMint.toBase58(),
MintParser
),
cache.registerParser(
acc?.info.liquidityMint.toBase58(),
MintParser
),
// ignore dex if its not set // ignore dex if its not set
cache.registerParser(acc?.info.dexMarketOption ? acc?.info.dexMarket.toBase58() : '', DexMarketParser), cache.registerParser(
].filter(_ => _); acc?.info.dexMarketOption ? acc?.info.dexMarket.toBase58() : "",
DexMarketParser
),
].filter((_) => _);
return result; return result;
}) }),
].flat() as string[]; ].flat() as string[];
// This will pre-cache all accounts used by pools // This will pre-cache all accounts used by pools
@ -76,12 +104,10 @@ export const useLending = () => {
return accounts; return accounts;
}; };
Promise.all([ Promise.all([queryLendingAccounts()]).then((all) => {
queryLendingAccounts(),
]).then((all) => {
setLendingAccounts(all.flat()); setLendingAccounts(all.flat());
}); });
}, [connection]); }, [connection, processAccount]);
useEffect(() => { useEffect(() => {
const subID = connection.onProgramAccountChange( const subID = connection.onProgramAccountChange(
@ -100,7 +126,7 @@ export const useLending = () => {
return () => { return () => {
connection.removeProgramAccountChangeListener(subID); connection.removeProgramAccountChangeListener(subID);
}; };
}, [connection, lendingAccounts]); }, [connection, lendingAccounts, processAccount]);
return { accounts: lendingAccounts }; return { accounts: lendingAccounts };
}; };

View File

@ -1,13 +1,8 @@
import React, { useCallback, useContext, useEffect, useState } from "react"; import React, { useCallback, useContext, useEffect, useState } from "react";
import { MINT_TO_MARKET } from "./../models/marketOverrides"; import { MINT_TO_MARKET } from "./../models/marketOverrides";
import { import { STABLE_COINS } from "./../utils/utils";
STABLE_COINS,
} from "./../utils/utils";
import { useConnectionConfig } from "./connection"; import { useConnectionConfig } from "./connection";
import { import { cache, getMultipleAccounts } from "./accounts";
cache,
getMultipleAccounts,
} from "./accounts";
import { Market, MARKETS, Orderbook, TOKEN_MINTS } from "@project-serum/serum"; import { Market, MARKETS, Orderbook, TOKEN_MINTS } from "@project-serum/serum";
import { AccountInfo, Connection, PublicKey } from "@solana/web3.js"; import { AccountInfo, Connection, PublicKey } from "@solana/web3.js";
import { useMemo } from "react"; import { useMemo } from "react";
@ -39,14 +34,10 @@ export function MarketProvider({ children = null as any }) {
]); ]);
// TODO: identify which markets to query ... // TODO: identify which markets to query ...
const mints = useMemo(() => [ const mints = useMemo(() => [] as PublicKey[], []);
] as PublicKey[], []);
const marketByMint = useMemo(() => { const marketByMint = useMemo(() => {
return [ return [...new Set(mints).values()].reduce((acc, key) => {
...new Set(mints).values(),
].reduce((acc, key) => {
const mintAddress = key.toBase58(); const mintAddress = key.toBase58();
const SERUM_TOKEN = TOKEN_MINTS.find( const SERUM_TOKEN = TOKEN_MINTS.find(
@ -240,8 +231,6 @@ export const useMidPriceInUSD = (mint: string) => {
return { price, isBase: price === 1.0 }; return { price, isBase: price === 1.0 };
}; };
const getMidPrice = (marketAddress?: string, mintAddress?: string) => { const getMidPrice = (marketAddress?: string, mintAddress?: string) => {
const SERUM_TOKEN = TOKEN_MINTS.find( const SERUM_TOKEN = TOKEN_MINTS.find(
(a) => a.address.toBase58() === mintAddress (a) => a.address.toBase58() === mintAddress

View File

@ -1,6 +1,6 @@
export * from './useUserAccounts'; export * from "./useUserAccounts";
export * from './useAccountByMint'; export * from "./useAccountByMint";
export * from './useLendingReserves'; export * from "./useLendingReserves";
export * from './useTokenName'; export * from "./useTokenName";
export * from './useUserBalance'; export * from "./useUserBalance";
export * from './useCollateralBalance'; export * from "./useCollateralBalance";

View File

@ -1,4 +1,4 @@
import { useUserAccounts } from './useUserAccounts'; import { useUserAccounts } from "./useUserAccounts";
export const useAccountByMint = (mint: string) => { export const useAccountByMint = (mint: string) => {
const { userAccounts } = useUserAccounts(); const { userAccounts } = useUserAccounts();

View File

@ -14,6 +14,6 @@ export function useCollateralBalance(reserve?: LendingReserve) {
return { return {
balance: fromLamports(collateralRatioLamports, mint), balance: fromLamports(collateralRatioLamports, mint),
balanceLamports: collateralRatioLamports, balanceLamports: collateralRatioLamports,
accounts accounts,
}; };
} }

View File

@ -1,14 +1,19 @@
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { LendingReserve, LendingReserveParser } from "../models/lending"; import { LendingReserve, LendingReserveParser } from "../models/lending";
import { cache, ParsedAccount } from './../contexts/accounts'; import { cache, ParsedAccount } from "./../contexts/accounts";
const getLendingReserves = () => { 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() { export function useLendingReserves() {
const [reserveAccounts, setReserveAccounts] = useState<ParsedAccount<LendingReserve>[]>([]); const [reserveAccounts, setReserveAccounts] = useState<
ParsedAccount<LendingReserve>[]
>([]);
useEffect(() => { useEffect(() => {
setReserveAccounts(getLendingReserves()); setReserveAccounts(getLendingReserves());
@ -22,7 +27,7 @@ export function useLendingReserves() {
return () => { return () => {
dispose(); dispose();
}; };
}, [setReserveAccounts]) }, [setReserveAccounts]);
return { return {
reserveAccounts, reserveAccounts,
@ -30,8 +35,10 @@ export function useLendingReserves() {
} }
export function useLendingReserve(address: string | PublicKey) { export function useLendingReserve(address: string | PublicKey) {
const id = typeof address === 'string' ? address : address?.toBase58(); const id = typeof address === "string" ? address : address?.toBase58();
const [reserveAccount, setReserveAccount] = useState<ParsedAccount<LendingReserve>>(); const [reserveAccount, setReserveAccount] = useState<
ParsedAccount<LendingReserve>
>();
useEffect(() => { useEffect(() => {
setReserveAccount(cache.get(id)); setReserveAccount(cache.get(id));
@ -45,7 +52,7 @@ export function useLendingReserve(address: string | PublicKey) {
return () => { return () => {
dispose(); dispose();
}; };
}, [setReserveAccount]) }, [id, setReserveAccount]);
return reserveAccount; return reserveAccount;
} }

View File

@ -4,6 +4,7 @@ import { getTokenName } from "../utils/utils";
export function useTokenName(mintAddress?: string | PublicKey) { export function useTokenName(mintAddress?: string | PublicKey) {
const { tokenMap } = useConnectionConfig(); const { tokenMap } = useConnectionConfig();
const address = typeof mintAddress === 'string' ? mintAddress : mintAddress?.toBase58(); const address =
typeof mintAddress === "string" ? mintAddress : mintAddress?.toBase58();
return getTokenName(tokenMap, address); return getTokenName(tokenMap, address);
} }

View File

@ -1,9 +1,9 @@
import { TokenAccount } from "../models"; import { TokenAccount } from "../models";
import { useAccountsContext } from './../contexts/accounts'; import { useAccountsContext } from "./../contexts/accounts";
export function useUserAccounts() { export function useUserAccounts() {
const context = useAccountsContext(); const context = useAccountsContext();
return { return {
userAccounts: context.userAccounts as TokenAccount[], userAccounts: context.userAccounts as TokenAccount[],
}; };
} }

View File

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

View File

@ -20,12 +20,18 @@ export const OrderBookParser = (id: PublicKey, acc: AccountInfo<Buffer>) => {
return details; return details;
}; };
const DEFAULT_DEX_ID = new PublicKey('EUqojwWA2rd19FZrzeBncJsm38Jm1hEhE3zsmX3bRc2o'); const DEFAULT_DEX_ID = new PublicKey(
"EUqojwWA2rd19FZrzeBncJsm38Jm1hEhE3zsmX3bRc2o"
);
export const DexMarketParser = (pubkey: PublicKey, acc: AccountInfo<Buffer>) => { export const DexMarketParser = (
const market = MARKETS.find(m => m.address.equals(pubkey)); pubkey: PublicKey,
const decoded = Market.getLayout(market?.programId || DEFAULT_DEX_ID) acc: AccountInfo<Buffer>
.decode(acc.data); ) => {
const market = MARKETS.find((m) => m.address.equals(pubkey));
const decoded = Market.getLayout(market?.programId || DEFAULT_DEX_ID).decode(
acc.data
);
const details = { const details = {
pubkey, pubkey,
@ -41,4 +47,4 @@ export const DexMarketParser = (pubkey: PublicKey, acc: AccountInfo<Buffer>) =>
cache.registerParser(details.info.asks, OrderBookParser); cache.registerParser(details.info.asks, OrderBookParser);
return details; return details;
} };

View File

@ -1,2 +1,2 @@
export * from "./account"; export * from "./account";
export * from './lending'; export * from "./lending";

View File

@ -8,7 +8,7 @@ import BN from "bn.js";
import * as BufferLayout from "buffer-layout"; import * as BufferLayout from "buffer-layout";
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids"; import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids";
import * as Layout from "./../../utils/layout"; 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 /// Borrow tokens from a reserve by depositing collateral tokens. The number of borrowed tokens
/// is calculated by market price. The debt obligation is tokenized. /// is calculated by market price. The debt obligation is tokenized.
@ -49,7 +49,7 @@ export const borrowInstruction = (
dexMarket: PublicKey, dexMarket: PublicKey,
dexOrderBookSide: PublicKey, dexOrderBookSide: PublicKey,
memory: PublicKey, memory: PublicKey
): TransactionInstruction => { ): TransactionInstruction => {
const dataLayout = BufferLayout.struct([ const dataLayout = BufferLayout.struct([
BufferLayout.u8("instruction"), BufferLayout.u8("instruction"),
@ -69,9 +69,17 @@ export const borrowInstruction = (
{ pubkey: from, isSigner: false, isWritable: true }, { pubkey: from, isSigner: false, isWritable: true },
{ pubkey: to, isSigner: false, isWritable: true }, { pubkey: to, isSigner: false, isWritable: true },
{ pubkey: depositReserve, 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: borrowReserve, isSigner: false, isWritable: true },
{ pubkey: borrowReserveLiquiditySupply, isSigner: false, isWritable: false }, {
pubkey: borrowReserveLiquiditySupply,
isSigner: false,
isWritable: false,
},
{ pubkey: obligation, isSigner: false, isWritable: true }, { pubkey: obligation, isSigner: false, isWritable: true },
{ pubkey: obligationMint, isSigner: false, isWritable: true }, { pubkey: obligationMint, isSigner: false, isWritable: true },
{ pubkey: obligationTokenOutput, isSigner: false, isWritable: true }, { pubkey: obligationTokenOutput, isSigner: false, isWritable: true },

View File

@ -1,5 +1,5 @@
export * from './market'; export * from "./market";
export * from './reserve'; export * from "./reserve";
export * from './obligation'; export * from "./obligation";
export * from './lending'; export * from "./lending";
export * from './borrow'; export * from "./borrow";

View File

@ -4,5 +4,5 @@ export enum LendingInstruction {
DepositReserveLiquidity = 2, DepositReserveLiquidity = 2,
WithdrawReserveLiquidity = 3, WithdrawReserveLiquidity = 3,
BorrowReserveLiquidity = 4, BorrowReserveLiquidity = 4,
RepayReserveLiquidity = 5 RepayReserveLiquidity = 5,
} }

View File

@ -1,15 +1,9 @@
import { import { AccountInfo, PublicKey } from "@solana/web3.js";
AccountInfo,
PublicKey,
} from "@solana/web3.js";
import * as BufferLayout from "buffer-layout"; import * as BufferLayout from "buffer-layout";
import * as Layout from "./../../utils/layout"; import * as Layout from "./../../utils/layout";
export const LendingMarketLayout: typeof BufferLayout.Structure = BufferLayout.struct( export const LendingMarketLayout: typeof BufferLayout.Structure = BufferLayout.struct(
[ [BufferLayout.u8("isInitialized"), Layout.publicKey("quoteMint")]
BufferLayout.u8("isInitialized"),
Layout.publicKey("quoteMint"),
]
); );
export interface LendingMarket { export interface LendingMarket {
@ -19,9 +13,12 @@ export interface LendingMarket {
export const isLendingMarket = (info: AccountInfo<Buffer>) => { export const isLendingMarket = (info: AccountInfo<Buffer>) => {
return info.data.length === LendingMarketLayout.span; 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 buffer = Buffer.from(info.data);
const data = LendingMarketLayout.decode(buffer); const data = LendingMarketLayout.decode(buffer);
@ -36,6 +33,5 @@ export const LendingMarketParser = (pubKey: PublicKey, info: AccountInfo<Buffer>
return details; return details;
}; };
// TODO: // TODO:
// create instructions for init // create instructions for init

View File

@ -1,6 +1,4 @@
import { import { PublicKey } from "@solana/web3.js";
PublicKey,
} from "@solana/web3.js";
import BN from "bn.js"; import BN from "bn.js";
import * as BufferLayout from "buffer-layout"; import * as BufferLayout from "buffer-layout";
import * as Layout from "./../../utils/layout"; import * as Layout from "./../../utils/layout";

View File

@ -9,7 +9,7 @@ import BN from "bn.js";
import * as BufferLayout from "buffer-layout"; import * as BufferLayout from "buffer-layout";
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids"; import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../../constants/ids";
import * as Layout from "./../../utils/layout"; import * as Layout from "./../../utils/layout";
import { LendingInstruction } from './lending'; import { LendingInstruction } from "./lending";
export const LendingReserveLayout: typeof BufferLayout.Structure = BufferLayout.struct( export const LendingReserveLayout: typeof BufferLayout.Structure = BufferLayout.struct(
[ [
@ -21,10 +21,11 @@ export const LendingReserveLayout: typeof BufferLayout.Structure = BufferLayout.
Layout.publicKey("collateralMint"), Layout.publicKey("collateralMint"),
Layout.publicKey("collateralSupply"), Layout.publicKey("collateralSupply"),
// TODO: replace u32 option with generic quivalent // TODO: replace u32 option with generic quivalent
BufferLayout.u32('dexMarketOption'), BufferLayout.u32("dexMarketOption"),
Layout.publicKey("dexMarket"), Layout.publicKey("dexMarket"),
BufferLayout.struct([ BufferLayout.struct(
[
/// Max utilization rate as a percent /// Max utilization rate as a percent
BufferLayout.u8("maxUtilizationRate"), BufferLayout.u8("maxUtilizationRate"),
/// The ratio of the loan to the value of the collateral as a percent /// 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"), BufferLayout.u8("liquidationBonus"),
/// The percent at which an obligation is considered unhealthy /// The percent at which an obligation is considered unhealthy
BufferLayout.u8("liquidationThreshold"), BufferLayout.u8("liquidationThreshold"),
], "config"), ],
"config"
),
Layout.uint128("cumulativeBorrowRate"), Layout.uint128("cumulativeBorrowRate"),
Layout.uint128("totalBorrows"), Layout.uint128("totalBorrows"),
@ -47,7 +50,7 @@ export const isLendingReserve = (info: AccountInfo<Buffer>) => {
console.log(LendingReserveLayout.span); console.log(LendingReserveLayout.span);
console.log(info.data.length); console.log(info.data.length);
return info.data.length === LendingReserveLayout.span; return info.data.length === LendingReserveLayout.span;
} };
export interface LendingReserve { export interface LendingReserve {
lastUpdateSlot: BN; lastUpdateSlot: BN;
@ -63,11 +66,11 @@ export interface LendingReserve {
dexMarketPrice: BN; // what is precision on the price? dexMarketPrice: BN; // what is precision on the price?
config: { config: {
maxUtilizationRate: number, maxUtilizationRate: number;
loanToValueRatio: number, loanToValueRatio: number;
liquidationBonus: number, liquidationBonus: number;
liquidationThreshold: number, liquidationThreshold: number;
} };
// collateralFactor: number; // collateralFactor: number;
cumulativeBorrowRate: BN; cumulativeBorrowRate: BN;
@ -80,7 +83,10 @@ export interface LendingReserve {
// Layout.uint128("total_borrows"), // 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 buffer = Buffer.from(info.data);
const data = LendingReserveLayout.decode(buffer); const data = LendingReserveLayout.decode(buffer);
@ -110,12 +116,12 @@ export const initReserveInstruction = (
lendingMarket: PublicKey, lendingMarket: PublicKey,
lendingMarketAuthority: PublicKey, lendingMarketAuthority: PublicKey,
dexMarket: PublicKey, // TODO: optional dexMarket: PublicKey // TODO: optional
): TransactionInstruction => { ): TransactionInstruction => {
const dataLayout = BufferLayout.struct([ const dataLayout = BufferLayout.struct([
BufferLayout.u8("instruction"), BufferLayout.u8("instruction"),
Layout.uint64("liquidityAmount"), Layout.uint64("liquidityAmount"),
BufferLayout.u8("maxUtilizationRate") BufferLayout.u8("maxUtilizationRate"),
]); ]);
const data = Buffer.alloc(dataLayout.span); const data = Buffer.alloc(dataLayout.span);
@ -172,7 +178,7 @@ export const depositInstruction = (
reserveAuthority: PublicKey, reserveAuthority: PublicKey,
reserveAccount: PublicKey, reserveAccount: PublicKey,
reserveSupply: PublicKey, reserveSupply: PublicKey,
collateralMint: PublicKey, collateralMint: PublicKey
): TransactionInstruction => { ): TransactionInstruction => {
const dataLayout = BufferLayout.struct([ const dataLayout = BufferLayout.struct([
BufferLayout.u8("instruction"), BufferLayout.u8("instruction"),
@ -212,7 +218,7 @@ export const withdrawInstruction = (
reserveAccount: PublicKey, reserveAccount: PublicKey,
collateralMint: PublicKey, collateralMint: PublicKey,
reserveSupply: PublicKey, reserveSupply: PublicKey,
authority: PublicKey, authority: PublicKey
): TransactionInstruction => { ): TransactionInstruction => {
const dataLayout = BufferLayout.struct([ const dataLayout = BufferLayout.struct([
BufferLayout.u8("instruction"), BufferLayout.u8("instruction"),
@ -244,4 +250,3 @@ export const withdrawInstruction = (
data, data,
}); });
}; };

View File

@ -17,7 +17,7 @@ import {
BorrowReserveView, BorrowReserveView,
WithdrawView, WithdrawView,
FaucetView, FaucetView,
} from './views'; } from "./views";
export function Routes() { export function Routes() {
return ( return (
@ -31,13 +31,27 @@ export function Routes() {
<AppLayout> <AppLayout>
<Switch> <Switch>
<Route exact path="/" component={() => <HomeView />} /> <Route exact path="/" component={() => <HomeView />} />
<Route exact path="/dashboard" children={<DashboardView />} /> <Route
exact
path="/dashboard"
children={<DashboardView />}
/>
<Route path="/reserve/:id" children={<ReserveView />} /> <Route path="/reserve/:id" children={<ReserveView />} />
<Route exact path="/deposit" component={() => <DepositView />} /> <Route
<Route path="/deposit/:id" children={<DepositReserveView />} /> exact
path="/deposit"
component={() => <DepositView />}
/>
<Route
path="/deposit/:id"
children={<DepositReserveView />}
/>
<Route path="/withdraw/:id" children={<WithdrawView />} /> <Route path="/withdraw/:id" children={<WithdrawView />} />
<Route exact path="/borrow" children={<BorrowView />} /> <Route exact path="/borrow" children={<BorrowView />} />
<Route path="/borrow/:id" children={<BorrowReserveView />} /> <Route
path="/borrow/:id"
children={<BorrowReserveView />}
/>
<Route exact path="/faucet" children={<FaucetView />} /> <Route exact path="/faucet" children={<FaucetView />} />
</Switch> </Switch>
</AppLayout> </AppLayout>

View File

@ -44,7 +44,7 @@ export class EventEmitter {
onCache(callback: (args: CacheUpdateEvent) => void) { onCache(callback: (args: CacheUpdateEvent) => void) {
this.emitter.on(CacheUpdateEvent.type, callback); this.emitter.on(CacheUpdateEvent.type, callback);
return () => this.emitter.removeListener(CacheUpdateEvent .type, callback); return () => this.emitter.removeListener(CacheUpdateEvent.type, callback);
} }
raiseAccountUpdated(id: string) { raiseAccountUpdated(id: string) {

View File

@ -32,15 +32,14 @@ export const uint64 = (property = "uint64"): unknown => {
const _decode = layout.decode.bind(layout); const _decode = layout.decode.bind(layout);
const _encode = layout.encode.bind(layout); const _encode = layout.encode.bind(layout);
layout.decode = (buffer: Buffer, offset: number) => { layout.decode = (buffer: Buffer, offset: number) => {
const data = _decode(buffer, offset); const data = _decode(buffer, offset);
return new BN( return new BN(
[...data] [...data]
.reverse() .reverse()
.map(i => `00${i.toString(16)}`.slice(-2)) .map((i) => `00${i.toString(16)}`.slice(-2))
.join(''), .join(""),
16, 16
); );
}; };
@ -65,15 +64,14 @@ export const uint128 = (property = "uint128"): unknown => {
const _decode = layout.decode.bind(layout); const _decode = layout.decode.bind(layout);
const _encode = layout.encode.bind(layout); const _encode = layout.encode.bind(layout);
layout.decode = (buffer: Buffer, offset: number) => { layout.decode = (buffer: Buffer, offset: number) => {
const data = _decode(buffer, offset); const data = _decode(buffer, offset);
return new BN( return new BN(
[...data] [...data]
.reverse() .reverse()
.map(i => `00${i.toString(16)}`.slice(-2)) .map((i) => `00${i.toString(16)}`.slice(-2))
.join(''), .join(""),
16, 16
); );
}; };
@ -89,7 +87,6 @@ export const uint128 = (property = "uint128"): unknown => {
return _encode(b, buffer, offset); return _encode(b, buffer, offset);
}; };
return layout; return layout;
}; };

View File

@ -53,7 +53,7 @@ export function getTokenName(
shorten = true shorten = true
): string { ): string {
if (!mintAddress) { if (!mintAddress) {
return 'N/A'; return "N/A";
} }
const knownSymbol = map.get(mintAddress)?.tokenSymbol; const knownSymbol = map.get(mintAddress)?.tokenSymbol;
@ -66,9 +66,10 @@ export function getTokenName(
export function getTokenIcon( export function getTokenIcon(
map: KnownTokenMap, map: KnownTokenMap,
mintAddress?: string | PublicKey, mintAddress?: string | PublicKey
): string | undefined { ): string | undefined {
const address = typeof mintAddress === 'string' ? mintAddress : mintAddress?.toBase58(); const address =
typeof mintAddress === "string" ? mintAddress : mintAddress?.toBase58();
if (!address) { if (!address) {
return; return;
} }
@ -101,7 +102,7 @@ export function toLamports(
typeof account === "number" ? account : account.info.amount?.toNumber(); typeof account === "number" ? account : account.info.amount?.toNumber();
const precision = Math.pow(10, mint?.decimals || 0); const precision = Math.pow(10, mint?.decimals || 0);
return (amount * precision); return amount * precision;
} }
export function fromLamports( export function fromLamports(
@ -171,12 +172,12 @@ const numberFormater = new Intl.NumberFormat("en-US", {
export const formatNumber = { export const formatNumber = {
format: (val?: number) => { format: (val?: number) => {
if (!val) { if (!val) {
return '--'; return "--";
} }
return numberFormater.format(val); return numberFormater.format(val);
} },
} };
export const formatPct = new Intl.NumberFormat("en-US", { export const formatPct = new Intl.NumberFormat("en-US", {
style: "percent", style: "percent",

View File

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { useLendingReserves } from '../../hooks'; import { useLendingReserves } from "../../hooks";
import { BorrowItem } from './item'; import { BorrowItem } from "./item";
import './itemStyle.less'; import "./itemStyle.less";
export const BorrowView = () => { export const BorrowView = () => {
const { reserveAccounts } = useLendingReserves(); const { reserveAccounts } = useLendingReserves();
@ -13,7 +13,9 @@ export const BorrowView = () => {
<div>APY</div> <div>APY</div>
<div>Action</div> <div>Action</div>
</div> </div>
{reserveAccounts.map(account => <BorrowItem reserve={account.info} address={account.pubkey} />)} {reserveAccounts.map((account) => (
<BorrowItem reserve={account.info} address={account.pubkey} />
))}
</div> </div>
); );
} };

View File

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { useCollateralBalance, useTokenName } from '../../hooks'; import { useCollateralBalance, useTokenName } from "../../hooks";
import { LendingReserve } from "../../models/lending"; import { LendingReserve } from "../../models/lending";
import { TokenIcon } from "../../components/TokenIcon"; import { TokenIcon } from "../../components/TokenIcon";
import { formatNumber } from "../../utils/utils"; import { formatNumber } from "../../utils/utils";
@ -7,17 +7,26 @@ import { Button, Card } from "antd";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { PublicKey } from "@solana/web3.js"; 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); const name = useTokenName(props.reserve.liquidityMint);
// TODO: calculate avilable amount... based on total owned collateral across all the reserves // TODO: calculate avilable amount... based on total owned collateral across all the reserves
const { balance: collateralBalance } = useCollateralBalance(props.reserve); const { balance: collateralBalance } = useCollateralBalance(props.reserve);
return <Link to={`/borrow/${props.address.toBase58()}`}> return (
<Link to={`/borrow/${props.address.toBase58()}`}>
<Card> <Card>
<div className="borrow-item"> <div className="borrow-item">
<span style={{ display: 'flex' }}><TokenIcon mintAddress={props.reserve.liquidityMint} />{name}</span> <span style={{ display: "flex" }}>
<div>{formatNumber.format(collateralBalance)} {name}</div> <TokenIcon mintAddress={props.reserve.liquidityMint} />
{name}
</span>
<div>
{formatNumber.format(collateralBalance)} {name}
</div>
<div>--</div> <div>--</div>
<div> <div>
<Button> <Button>
@ -26,5 +35,6 @@ export const BorrowItem = (props: { reserve: LendingReserve, address: PublicKey
</div> </div>
</div> </div>
</Card> </Card>
</Link>; </Link>
} );
};

View File

@ -1,10 +1,13 @@
import React, { } from "react"; import React from "react";
import { useLendingReserve } from '../../hooks'; import { useLendingReserve } from "../../hooks";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import './style.less'; import "./style.less";
import { BorrowInput } from '../../components/BorrowInput'; import { BorrowInput } from "../../components/BorrowInput";
import { SideReserveOverview, SideReserveOverviewMode } from '../../components/SideReserveOverview'; import {
SideReserveOverview,
SideReserveOverviewMode,
} from "../../components/SideReserveOverview";
export const BorrowReserveView = () => { export const BorrowReserveView = () => {
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
@ -15,17 +18,21 @@ export const BorrowReserveView = () => {
return null; return null;
} }
return <div className="borrow-reserve"> return (
<div className="borrow-reserve">
<div className="borrow-reserve-container"> <div className="borrow-reserve-container">
<BorrowInput <BorrowInput
className="borrow-reserve-item borrow-reserve-item-left" className="borrow-reserve-item borrow-reserve-item-left"
reserve={reserve} reserve={reserve}
address={lendingReserve.pubkey} /> address={lendingReserve.pubkey}
/>
<SideReserveOverview <SideReserveOverview
className="borrow-reserve-item borrow-reserve-item-right" className="borrow-reserve-item borrow-reserve-item-right"
reserve={reserve} reserve={reserve}
address={lendingReserve.pubkey} address={lendingReserve.pubkey}
mode={SideReserveOverviewMode.Borrow} /> mode={SideReserveOverviewMode.Borrow}
/>
</div> </div>
</div>; </div>
} );
};

View File

@ -1,11 +1,9 @@
import React from "react"; import React from "react";
export const DashboardView = () => { export const DashboardView = () => {
return (
return <div className="flexColumn"> <div className="flexColumn">
DASHBOARD: DASHBOARD: TODO: 1. Add deposits 2. Add obligations
TODO: </div>
1. Add deposits );
2. Add obligations };
</div>;
}

View File

@ -1 +1 @@
export * from './view'; export * from "./view";

View File

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { useLendingReserves } from '../../../hooks'; import { useLendingReserves } from "../../../hooks";
import { ReserveItem } from './item'; import { ReserveItem } from "./item";
import './itemStyle.less'; import "./itemStyle.less";
export const DepositView = () => { export const DepositView = () => {
const { reserveAccounts } = useLendingReserves(); const { reserveAccounts } = useLendingReserves();
@ -14,7 +14,9 @@ export const DepositView = () => {
<div>APY</div> <div>APY</div>
<div>Action</div> <div>Action</div>
</div> </div>
{reserveAccounts.map(account => <ReserveItem reserve={account.info} address={account.pubkey} />)} {reserveAccounts.map((account) => (
<ReserveItem reserve={account.info} address={account.pubkey} />
))}
</div> </div>
); );
}; };

View File

@ -1,5 +1,9 @@
import React from "react"; import React from "react";
import { useCollateralBalance, useTokenName, useUserBalance } from '../../../hooks'; import {
useCollateralBalance,
useTokenName,
useUserBalance,
} from "../../../hooks";
import { LendingReserve } from "../../../models/lending"; import { LendingReserve } from "../../../models/lending";
import { TokenIcon } from "../../../components/TokenIcon"; import { TokenIcon } from "../../../components/TokenIcon";
import { formatNumber } from "../../../utils/utils"; import { formatNumber } from "../../../utils/utils";
@ -7,17 +11,28 @@ import { Button, Card } from "antd";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
export const ReserveItem = (props: { reserve: LendingReserve, address: PublicKey }) => { export const ReserveItem = (props: {
reserve: LendingReserve;
address: PublicKey;
}) => {
const name = useTokenName(props.reserve.liquidityMint); const name = useTokenName(props.reserve.liquidityMint);
const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint); const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint);
const { balance: collateralBalance } = useCollateralBalance(props.reserve); const { balance: collateralBalance } = useCollateralBalance(props.reserve);
return <Link to={`/deposit/${props.address.toBase58()}`}> return (
<Link to={`/deposit/${props.address.toBase58()}`}>
<Card> <Card>
<div className="deposit-item"> <div className="deposit-item">
<span style={{ display: 'flex' }}><TokenIcon mintAddress={props.reserve.liquidityMint} />{name}</span> <span style={{ display: "flex" }}>
<div>{formatNumber.format(tokenBalance)} {name}</div> <TokenIcon mintAddress={props.reserve.liquidityMint} />
<div>{formatNumber.format(collateralBalance)} {name}</div> {name}
</span>
<div>
{formatNumber.format(tokenBalance)} {name}
</div>
<div>
{formatNumber.format(collateralBalance)} {name}
</div>
<div>--</div> <div>--</div>
<div> <div>
<Button> <Button>
@ -26,5 +41,6 @@ export const ReserveItem = (props: { reserve: LendingReserve, address: PublicKey
</div> </div>
</div> </div>
</Card> </Card>
</Link>; </Link>
} );
};

View File

@ -1,11 +1,14 @@
import React, { } from "react"; import React from "react";
import { useLendingReserve } from '../../hooks'; import { useLendingReserve } from "../../hooks";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import './style.less'; import "./style.less";
import { DepositInput } from '../../components/DepositInput'; import { DepositInput } from "../../components/DepositInput";
import { DepositInfoLine } from '../../components/DepositInfoLine'; import { DepositInfoLine } from "../../components/DepositInfoLine";
import { SideReserveOverview, SideReserveOverviewMode } from '../../components/SideReserveOverview'; import {
SideReserveOverview,
SideReserveOverviewMode,
} from "../../components/SideReserveOverview";
export const DepositReserveView = () => { export const DepositReserveView = () => {
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
@ -16,21 +19,26 @@ export const DepositReserveView = () => {
return null; return null;
} }
return <div className="deposit-reserve"> return (
<div className="deposit-reserve">
<DepositInfoLine <DepositInfoLine
className="deposit-reserve-item" className="deposit-reserve-item"
reserve={reserve} reserve={reserve}
address={lendingReserve.pubkey} /> address={lendingReserve.pubkey}
/>
<div className="deposit-reserve-container"> <div className="deposit-reserve-container">
<DepositInput <DepositInput
className="deposit-reserve-item deposit-reserve-item-left" className="deposit-reserve-item deposit-reserve-item-left"
reserve={reserve} reserve={reserve}
address={lendingReserve.pubkey} /> address={lendingReserve.pubkey}
/>
<SideReserveOverview <SideReserveOverview
className="deposit-reserve-item deposit-reserve-item-right" className="deposit-reserve-item deposit-reserve-item-right"
reserve={reserve} reserve={reserve}
address={lendingReserve.pubkey} address={lendingReserve.pubkey}
mode={SideReserveOverviewMode.Deposit} /> mode={SideReserveOverviewMode.Deposit}
/>
</div> </div>
</div>; </div>
} );
};

View File

@ -10,27 +10,36 @@ export const FaucetView = () => {
const airdrop = useCallback(() => { const airdrop = useCallback(() => {
connection.requestAirdrop(wallet.publicKey, 1 * LAMPORTS_PER_SOL); connection.requestAirdrop(wallet.publicKey, 1 * LAMPORTS_PER_SOL);
}, [wallet, connection]) }, [wallet, connection]);
const bodyStyle: React.CSSProperties = { const bodyStyle: React.CSSProperties = {
display: 'flex', display: "flex",
flex: 1, flex: 1,
justifyContent: 'center', justifyContent: "center",
alignItems: 'center', alignItems: "center",
height: '100%', height: "100%",
}; };
return ( return (
<div className="flexColumn" style={{ flex: 1 }}> <div className="flexColumn" style={{ flex: 1 }}>
<Card title={'Faucet'} bodyStyle={bodyStyle} style={{ flex: 1 }}> <Card title={"Faucet"} bodyStyle={bodyStyle} style={{ flex: 1 }}>
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around', alignItems: 'center' }}> <div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "space-around",
alignItems: "center",
}}
>
<div className="deposit-input-title" style={{ margin: 10 }}> <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> </div>
<Button type="primary" onClick={airdrop} >Give me SOL</Button> <Button type="primary" onClick={airdrop}>
Give me SOL
</Button>
</div> </div>
</Card> </Card>
</div> </div>
); );
} };

View File

@ -1,14 +1,15 @@
import React from "react"; import React from "react";
import { useLendingReserves } from '../../hooks'; import { useLendingReserves } from "../../hooks";
import { LendingReserveItem } from "./item"; import { LendingReserveItem } from "./item";
import './itemStyle.less'; import "./itemStyle.less";
export const HomeView = () => { export const HomeView = () => {
const { reserveAccounts } = useLendingReserves(); const { reserveAccounts } = useLendingReserves();
// TODO: add total Liquidity amount ... // TODO: add total Liquidity amount ...
return <div className="flexColumn"> return (
<div className="flexColumn">
<div className="home-item home-header"> <div className="home-item home-header">
<div>Asset</div> <div>Asset</div>
<div>Market Size</div> <div>Market Size</div>
@ -16,6 +17,9 @@ export const HomeView = () => {
<div>Deposit APY</div> <div>Deposit APY</div>
<div>Borrow APY</div> <div>Borrow APY</div>
</div> </div>
{reserveAccounts.map(account => <LendingReserveItem reserve={account.info} address={account.pubkey} />)} {reserveAccounts.map((account) => (
</div>; <LendingReserveItem reserve={account.info} address={account.pubkey} />
} ))}
</div>
);
};

View File

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { useTokenName } from '../../hooks'; import { useTokenName } from "../../hooks";
import { LendingReserve } from "../../models/lending"; import { LendingReserve } from "../../models/lending";
import { TokenIcon } from "../../components/TokenIcon"; import { TokenIcon } from "../../components/TokenIcon";
import { formatNumber, fromLamports } from "../../utils/utils"; import { formatNumber, fromLamports } from "../../utils/utils";
@ -8,26 +8,40 @@ import { Link } from "react-router-dom";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import { useMint } from "../../contexts/accounts"; 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 name = useTokenName(props.reserve.liquidityMint);
const liquidityMint = useMint(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(); const totalBorrows = props.reserve.totalBorrows.toString();
console.log(liquidityMint); console.log(liquidityMint);
return <Link to={`/reserve/${props.address.toBase58()}`}> return (
<Link to={`/reserve/${props.address.toBase58()}`}>
<Card> <Card>
<div className="home-item"> <div className="home-item">
<span style={{ display: 'flex' }}><TokenIcon mintAddress={props.reserve.liquidityMint} />{name}</span> <span style={{ display: "flex" }}>
<div>{formatNumber.format(totalLiquidity)} {name}</div> <TokenIcon mintAddress={props.reserve.liquidityMint} />
<div>{totalBorrows} {name}</div> {name}
</span>
<div>
{formatNumber.format(totalLiquidity)} {name}
</div>
<div>
{totalBorrows} {name}
</div>
<div>--</div> <div>--</div>
<div>--</div> <div>--</div>
</div> </div>
</Card> </Card>
</Link>; </Link>
} );
};

View File

@ -1,9 +1,9 @@
export { HomeView } from './home'; export { HomeView } from "./home";
export { BorrowView } from './borrow'; export { BorrowView } from "./borrow";
export { BorrowReserveView } from './borrowReserve'; export { BorrowReserveView } from "./borrowReserve";
export { DashboardView } from './dashboard'; export { DashboardView } from "./dashboard";
export { DepositView } from './deposit'; export { DepositView } from "./deposit";
export { DepositReserveView } from './depositReserve'; export { DepositReserveView } from "./depositReserve";
export { ReserveView } from './reserve'; export { ReserveView } from "./reserve";
export { WithdrawView } from './withdraw'; export { WithdrawView } from "./withdraw";
export { FaucetView } from './faucet'; export { FaucetView } from "./faucet";

View File

@ -1,10 +1,10 @@
import React, { } from "react"; import React from "react";
import { useLendingReserve } from './../../hooks'; import { useLendingReserve } from "./../../hooks";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import './style.less'; import "./style.less";
import { UserLendingCard } from './../../components/UserLendingCard'; import { UserLendingCard } from "./../../components/UserLendingCard";
import { ReserveStatus } from './../../components/ReserveStatus'; import { ReserveStatus } from "./../../components/ReserveStatus";
export const ReserveView = () => { export const ReserveView = () => {
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
@ -15,16 +15,20 @@ export const ReserveView = () => {
return null; return null;
} }
return <div className="reserve-overview"> return (
<div className="reserve-overview">
<div className="reserve-overview-container"> <div className="reserve-overview-container">
<ReserveStatus <ReserveStatus
className="reserve-overview-item reserve-overview-item-left" className="reserve-overview-item reserve-overview-item-left"
reserve={reserve} reserve={reserve}
address={lendingReserve.pubkey} /> address={lendingReserve.pubkey}
/>
<UserLendingCard <UserLendingCard
className="reserve-overview-item reserve-overview-item-right" className="reserve-overview-item reserve-overview-item-right"
reserve={reserve} reserve={reserve}
address={lendingReserve.pubkey} /> address={lendingReserve.pubkey}
/>
</div> </div>
</div>; </div>
} );
};

View File

@ -1,11 +1,14 @@
import React, { } from "react"; import React from "react";
import { useLendingReserve } from '../../hooks'; import { useLendingReserve } from "../../hooks";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import './style.less'; import "./style.less";
import { WithdrawInput } from '../../components/WithdrawInput'; import { WithdrawInput } from "../../components/WithdrawInput";
import { DepositInfoLine } from '../../components/DepositInfoLine'; import { DepositInfoLine } from "../../components/DepositInfoLine";
import { SideReserveOverview, SideReserveOverviewMode } from '../../components/SideReserveOverview'; import {
SideReserveOverview,
SideReserveOverviewMode,
} from "../../components/SideReserveOverview";
export const WithdrawView = () => { export const WithdrawView = () => {
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
@ -16,21 +19,26 @@ export const WithdrawView = () => {
return null; return null;
} }
return <div className="deposit-reserve"> return (
<div className="deposit-reserve">
<DepositInfoLine <DepositInfoLine
className="deposit-reserve-item" className="deposit-reserve-item"
reserve={reserve} reserve={reserve}
address={lendingReserve.pubkey} /> address={lendingReserve.pubkey}
/>
<div className="deposit-reserve-container"> <div className="deposit-reserve-container">
<WithdrawInput <WithdrawInput
className="deposit-reserve-item deposit-reserve-item-left" className="deposit-reserve-item deposit-reserve-item-left"
reserve={reserve} reserve={reserve}
address={lendingReserve.pubkey} /> address={lendingReserve.pubkey}
/>
<SideReserveOverview <SideReserveOverview
className="deposit-reserve-item deposit-reserve-item-right" className="deposit-reserve-item deposit-reserve-item-right"
reserve={reserve} reserve={reserve}
address={lendingReserve.pubkey} address={lendingReserve.pubkey}
mode={SideReserveOverviewMode.Deposit} /> mode={SideReserveOverviewMode.Deposit}
/>
</div> </div>
</div>; </div>
} );
};