feat: add slider support

This commit is contained in:
bartosz-lipinski 2020-12-01 00:14:35 -06:00
parent 2682001514
commit d02ea83f1d
14 changed files with 147 additions and 58 deletions

View File

@ -11,20 +11,18 @@ import {
initReserveInstruction,
LendingReserve,
} from "./../models/lending";
import { AccountLayout, MintInfo, Token } from "@solana/spl-token";
import { AccountLayout, Token } from "@solana/spl-token";
import { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids";
import {
createUninitializedAccount,
ensureSplAccount,
findOrCreateAccountByMint,
} from "./account";
import { cache, MintParser, ParsedAccount } from "../contexts/accounts";
import { TokenAccount } from "../models";
import { toLamports } from "../utils/utils";
export const deposit = async (
from: TokenAccount,
amount: number,
amountLamports: number,
reserve: LendingReserve,
reserveAddress: PublicKey,
connection: Connection,
@ -55,13 +53,6 @@ export const deposit = async (
LENDING_PROGRAM_ID
);
const mint = (await cache.query(
connection,
reserve.liquidityMint,
MintParser
)) as ParsedAccount<MintInfo>;
const amountLamports = toLamports(amount, mint?.info);
const fromAccount = ensureSplAccount(
instructions,
cleanupInstructions,

View File

@ -1,8 +1,13 @@
import React, { useCallback, useState } from "react";
import { useTokenName, useUserBalance } from "../../hooks";
import {
InputType,
useSliderInput,
useTokenName,
useUserBalance,
} from "../../hooks";
import { LendingReserve } from "../../models/lending";
import { TokenIcon } from "../TokenIcon";
import { Button, Card, Spin } from "antd";
import { Button, Card, Slider, Spin } from "antd";
import { NumericInput } from "../Input/numeric";
import { useConnection } from "../../contexts/connection";
import { useWallet } from "../../contexts/wallet";
@ -11,6 +16,7 @@ import { PublicKey } from "@solana/web3.js";
import "./style.less";
import { LoadingOutlined } from "@ant-design/icons";
import { ActionConfirmation } from "./../ActionConfirmation";
import { marks } from "../../constants";
const antIcon = <LoadingOutlined style={{ fontSize: 24 }} spin />;
@ -21,7 +27,6 @@ export const DepositInput = (props: {
}) => {
const connection = useConnection();
const { wallet } = useWallet();
const [value, setValue] = useState("");
const [pendingTx, setPendingTx] = useState(false);
const [showConfirmation, setShowConfirmation] = useState(false);
@ -29,9 +34,24 @@ export const DepositInput = (props: {
const address = props.address;
const name = useTokenName(reserve?.liquidityMint);
const { accounts: fromAccounts } = useUserBalance(reserve?.liquidityMint);
const { accounts: fromAccounts, balance, balanceLamports } = useUserBalance(
reserve?.liquidityMint
);
// const collateralBalance = useUserBalance(reserve?.collateralMint);
const convert = useCallback(
(val: string | number) => {
if (typeof val === "string") {
return (parseFloat(val) / balance) * 100;
} else {
return ((val * balance) / 100).toFixed(2);
}
},
[balance]
);
const { value, setValue, mark, setMark, type } = useSliderInput(convert);
const onDeposit = useCallback(() => {
setPendingTx(true);
@ -39,7 +59,9 @@ export const DepositInput = (props: {
try {
await deposit(
fromAccounts[0],
parseFloat(value),
type === InputType.Slider
? (mark * balanceLamports) / 100
: Math.ceil(balanceLamports * (parseFloat(value) / balance)),
reserve,
address,
connection,
@ -54,7 +76,19 @@ export const DepositInput = (props: {
setPendingTx(false);
}
})();
}, [connection, wallet, value, reserve, fromAccounts, address]);
}, [
connection,
setValue,
balanceLamports,
balance,
wallet,
value,
mark,
type,
reserve,
fromAccounts,
address,
]);
const bodyStyle: React.CSSProperties = {
display: "flex",
@ -98,6 +132,8 @@ export const DepositInput = (props: {
<div>{name}</div>
</div>
<Slider marks={marks} value={mark} onChange={setMark} />
<Button
type="primary"
onClick={onDeposit}

View File

@ -1,19 +1,21 @@
import React, { useCallback, useState } from "react";
import {
InputType,
useCollateralBalance,
useSliderInput,
useTokenName,
useUserBalance,
} from "../../hooks";
import { LendingReserve } from "../../models/lending";
import { TokenIcon } from "../TokenIcon";
import { Button, Card, Spin } from "antd";
import { Button, Card, Slider, Spin } from "antd";
import { NumericInput } from "../Input/numeric";
import { useConnection } from "../../contexts/connection";
import { useWallet } from "../../contexts/wallet";
import { withdraw } from "../../actions";
import { PublicKey } from "@solana/web3.js";
import "./style.less";
import { LABELS } from "../../constants";
import { LABELS, marks } from "../../constants";
import { LoadingOutlined } from "@ant-design/icons";
import { ActionConfirmation } from "./../ActionConfirmation";
@ -26,7 +28,6 @@ export const WithdrawInput = (props: {
}) => {
const connection = useConnection();
const { wallet } = useWallet();
const [value, setValue] = useState("");
const [pendingTx, setPendingTx] = useState(false);
const [showConfirmation, setShowConfirmation] = useState(false);
@ -42,6 +43,19 @@ export const WithdrawInput = (props: {
reserve
);
const convert = useCallback(
(val: string | number) => {
if (typeof val === "string") {
return (parseFloat(val) / collateralBalanceInLiquidity) * 100;
} else {
return ((val * collateralBalanceInLiquidity) / 100).toFixed(2);
}
},
[collateralBalanceInLiquidity]
);
const { value, setValue, mark, setMark, type } = useSliderInput(convert);
const onWithdraw = useCallback(() => {
setPendingTx(true);
@ -49,10 +63,12 @@ export const WithdrawInput = (props: {
try {
await withdraw(
fromAccounts[0],
Math.ceil(
collateralBalanceLamports *
(parseFloat(value) / collateralBalanceInLiquidity)
),
type === InputType.Slider
? (mark * collateralBalanceLamports) / 100
: Math.ceil(
collateralBalanceLamports *
(parseFloat(value) / collateralBalanceInLiquidity)
),
reserve,
address,
connection,
@ -68,14 +84,17 @@ export const WithdrawInput = (props: {
}
})();
}, [
connection,
wallet,
collateralBalanceLamports,
collateralBalanceInLiquidity,
value,
reserve,
fromAccounts,
address,
collateralBalanceInLiquidity,
collateralBalanceLamports,
connection,
fromAccounts,
mark,
reserve,
setValue,
type,
value,
wallet,
]);
const bodyStyle: React.CSSProperties = {
@ -103,9 +122,7 @@ export const WithdrawInput = (props: {
<TokenIcon mintAddress={reserve?.liquidityMint} />
<NumericInput
value={value}
onChange={(val: any) => {
setValue(val);
}}
onChange={setValue}
autoFocus={true}
style={{
fontSize: 20,
@ -118,6 +135,8 @@ export const WithdrawInput = (props: {
<div>{name}</div>
</div>
<Slider marks={marks} value={mark} onChange={setMark} />
<Button
type="primary"
onClick={onWithdraw}

View File

@ -1,3 +1,4 @@
export * from "./ids";
export * from "./labels";
export * from "./math";
export * from "./marks";

7
src/constants/marks.ts Normal file
View File

@ -0,0 +1,7 @@
export const marks = {
0: "0%",
25: "25%",
50: "50%",
75: "75%",
100: "100%",
};

View File

@ -83,7 +83,9 @@ export const useLending = () => {
setLendingAccounts([]);
const queryLendingAccounts = async () => {
const programAccounts = (await connection.getProgramAccounts(LENDING_PROGRAM_ID));
const programAccounts = await connection.getProgramAccounts(
LENDING_PROGRAM_ID
);
const accounts = programAccounts
.map(processAccount)
@ -116,7 +118,6 @@ export const useLending = () => {
}),
].flat() as string[];
// This will pre-cache all accounts used by pools
// All those accounts are updated whenever there is a change
await getMultipleAccounts(connection, toQuery, "single").then(
@ -130,9 +131,7 @@ export const useLending = () => {
);
// HACK: fix, force account refresh
programAccounts
.map(processAccount)
.filter((item) => item !== undefined);
programAccounts.map(processAccount).filter((item) => item !== undefined);
return accounts;
};

View File

@ -89,7 +89,7 @@ export function MarketProvider({ children = null as any }) {
allMarkets.filter((a) => cache.get(a) === undefined),
"single"
).then(({ keys, array }) => {
allMarkets.forEach(() => { });
allMarkets.forEach(() => {});
return array.map((item, index) => {
const marketAddress = keys[index];
@ -158,7 +158,7 @@ export function MarketProvider({ children = null as any }) {
const info = marketByMint.get(mintAddress);
const market = cache.get(info?.marketInfo.address.toBase58() || "");
if (!market) {
return () => { };
return () => {};
}
// TODO: get recent volume

View File

@ -9,3 +9,4 @@ export * from "./useUserObligations";
export * from "./useUserObligationByReserve";
export * from "./useBorrowedAmount";
export * from "./useUserDeposits";
export * from "./useSliderInput";

View File

@ -21,6 +21,7 @@ export function useCollateralBalance(
return {
balance: fromLamports(collateralRatioLamports, mint),
balanceLamports: collateralRatioLamports,
mint: reserve?.collateralMint,
accounts,
};
}

View File

@ -0,0 +1,37 @@
import { useCallback, useState } from "react";
export enum InputType {
Input = 0,
Slider = 1,
}
export const useSliderInput = (
convert: (val: string | number) => string | number
) => {
const [value, setValue] = useState("");
const [mark, setMark] = useState(0);
const [type, setType] = useState(InputType.Slider);
return {
value,
setValue: useCallback(
(val: string) => {
console.log(val);
setType(InputType.Input);
setValue(val);
setMark(convert(val) as number);
},
[setType, setValue, setMark, convert]
),
mark,
setMark: useCallback(
(val: number) => {
setType(InputType.Input);
setMark(val);
setValue(convert(val) as string);
},
[setType, setValue, setMark, convert]
),
type,
};
};

View File

@ -176,10 +176,10 @@ export const calculateUtilizationRatio = (reserve: LendingReserve) => {
);
};
export const reserveMarketCap = (reserve?: LendingReserve ) => {
const available = (reserve?.availableLiquidity.toNumber() || 0) ;
const borrowed = wadToLamports(reserve?.borrowedLiquidityWad).toNumber() ;
export const reserveMarketCap = (reserve?: LendingReserve) => {
const available = reserve?.availableLiquidity.toNumber() || 0;
const borrowed = wadToLamports(reserve?.borrowedLiquidityWad).toNumber();
const total = available + borrowed;
return total;
}
};

View File

@ -1,8 +1,5 @@
import React from "react";
import {
useCollateralBalance,
useTokenName,
} from "../../hooks";
import { useCollateralBalance, useTokenName } from "../../hooks";
import { calculateBorrowAPY, LendingReserve } from "../../models/lending";
import { TokenIcon } from "../../components/TokenIcon";
import { formatNumber, formatPct } from "../../utils/utils";

View File

@ -14,14 +14,12 @@ export const DashboardView = () => {
return (
<div className="dashboard-container">
{!connected && (
<div className="dashboard-info">
{LABELS.DASHBOARD_INFO}
</div>
<div className="dashboard-info">{LABELS.DASHBOARD_INFO}</div>
)}
{connected &&
userDeposits.length === 0 &&
userObligations.length === 0 && (
<div className="dashboard-info">{LABELS.NO_LOANS_NO_DEPOSITS}</div>
<div className="dashboard-info">{LABELS.NO_LOANS_NO_DEPOSITS}</div>
)}
{userDeposits.length > 0 && (
<div className="dashboard-left">

View File

@ -20,13 +20,17 @@ export const HomeView = () => {
const marketCapLamports = reserveMarketCap(item.info);
const localCache = cache;
const mint = localCache.get(item.info.liquidityMint.toBase58()) as ParsedAccount<MintInfo>;
const mint = localCache.get(
item.info.liquidityMint.toBase58()
) as ParsedAccount<MintInfo>;
if (!mint) {
return result;
}
const marketCap = fromLamports(marketCapLamports, mint?.info) * midPriceInUSD(mint?.pubkey.toBase58());
const marketCap =
fromLamports(marketCapLamports, mint?.info) *
midPriceInUSD(mint?.pubkey.toBase58());
return result + marketCap;
}, 0);
@ -43,8 +47,6 @@ export const HomeView = () => {
return () => {
dispose();
};
}, [marketEmitter, midPriceInUSD, setTotalMarketSize, reserveAccounts]);
return (