feat: add slider support
This commit is contained in:
parent
2682001514
commit
d02ea83f1d
|
@ -11,20 +11,18 @@ import {
|
||||||
initReserveInstruction,
|
initReserveInstruction,
|
||||||
LendingReserve,
|
LendingReserve,
|
||||||
} from "./../models/lending";
|
} 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 { LENDING_PROGRAM_ID, TOKEN_PROGRAM_ID } from "../constants/ids";
|
||||||
import {
|
import {
|
||||||
createUninitializedAccount,
|
createUninitializedAccount,
|
||||||
ensureSplAccount,
|
ensureSplAccount,
|
||||||
findOrCreateAccountByMint,
|
findOrCreateAccountByMint,
|
||||||
} from "./account";
|
} from "./account";
|
||||||
import { cache, MintParser, ParsedAccount } from "../contexts/accounts";
|
|
||||||
import { TokenAccount } from "../models";
|
import { TokenAccount } from "../models";
|
||||||
import { toLamports } from "../utils/utils";
|
|
||||||
|
|
||||||
export const deposit = async (
|
export const deposit = async (
|
||||||
from: TokenAccount,
|
from: TokenAccount,
|
||||||
amount: number,
|
amountLamports: number,
|
||||||
reserve: LendingReserve,
|
reserve: LendingReserve,
|
||||||
reserveAddress: PublicKey,
|
reserveAddress: PublicKey,
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
|
@ -55,13 +53,6 @@ export const deposit = async (
|
||||||
LENDING_PROGRAM_ID
|
LENDING_PROGRAM_ID
|
||||||
);
|
);
|
||||||
|
|
||||||
const mint = (await cache.query(
|
|
||||||
connection,
|
|
||||||
reserve.liquidityMint,
|
|
||||||
MintParser
|
|
||||||
)) as ParsedAccount<MintInfo>;
|
|
||||||
const amountLamports = toLamports(amount, mint?.info);
|
|
||||||
|
|
||||||
const fromAccount = ensureSplAccount(
|
const fromAccount = ensureSplAccount(
|
||||||
instructions,
|
instructions,
|
||||||
cleanupInstructions,
|
cleanupInstructions,
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
import React, { useCallback, useState } from "react";
|
import React, { useCallback, useState } from "react";
|
||||||
import { useTokenName, useUserBalance } from "../../hooks";
|
import {
|
||||||
|
InputType,
|
||||||
|
useSliderInput,
|
||||||
|
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, Spin } from "antd";
|
import { Button, Card, Slider, Spin } 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";
|
||||||
|
@ -11,6 +16,7 @@ import { PublicKey } from "@solana/web3.js";
|
||||||
import "./style.less";
|
import "./style.less";
|
||||||
import { LoadingOutlined } from "@ant-design/icons";
|
import { LoadingOutlined } from "@ant-design/icons";
|
||||||
import { ActionConfirmation } from "./../ActionConfirmation";
|
import { ActionConfirmation } from "./../ActionConfirmation";
|
||||||
|
import { marks } from "../../constants";
|
||||||
|
|
||||||
const antIcon = <LoadingOutlined style={{ fontSize: 24 }} spin />;
|
const antIcon = <LoadingOutlined style={{ fontSize: 24 }} spin />;
|
||||||
|
|
||||||
|
@ -21,7 +27,6 @@ export const DepositInput = (props: {
|
||||||
}) => {
|
}) => {
|
||||||
const connection = useConnection();
|
const connection = useConnection();
|
||||||
const { wallet } = useWallet();
|
const { wallet } = useWallet();
|
||||||
const [value, setValue] = useState("");
|
|
||||||
const [pendingTx, setPendingTx] = useState(false);
|
const [pendingTx, setPendingTx] = useState(false);
|
||||||
const [showConfirmation, setShowConfirmation] = useState(false);
|
const [showConfirmation, setShowConfirmation] = useState(false);
|
||||||
|
|
||||||
|
@ -29,9 +34,24 @@ export const DepositInput = (props: {
|
||||||
const address = props.address;
|
const address = props.address;
|
||||||
|
|
||||||
const name = useTokenName(reserve?.liquidityMint);
|
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 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(() => {
|
const onDeposit = useCallback(() => {
|
||||||
setPendingTx(true);
|
setPendingTx(true);
|
||||||
|
|
||||||
|
@ -39,7 +59,9 @@ export const DepositInput = (props: {
|
||||||
try {
|
try {
|
||||||
await deposit(
|
await deposit(
|
||||||
fromAccounts[0],
|
fromAccounts[0],
|
||||||
parseFloat(value),
|
type === InputType.Slider
|
||||||
|
? (mark * balanceLamports) / 100
|
||||||
|
: Math.ceil(balanceLamports * (parseFloat(value) / balance)),
|
||||||
reserve,
|
reserve,
|
||||||
address,
|
address,
|
||||||
connection,
|
connection,
|
||||||
|
@ -54,7 +76,19 @@ export const DepositInput = (props: {
|
||||||
setPendingTx(false);
|
setPendingTx(false);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, [connection, wallet, value, reserve, fromAccounts, address]);
|
}, [
|
||||||
|
connection,
|
||||||
|
setValue,
|
||||||
|
balanceLamports,
|
||||||
|
balance,
|
||||||
|
wallet,
|
||||||
|
value,
|
||||||
|
mark,
|
||||||
|
type,
|
||||||
|
reserve,
|
||||||
|
fromAccounts,
|
||||||
|
address,
|
||||||
|
]);
|
||||||
|
|
||||||
const bodyStyle: React.CSSProperties = {
|
const bodyStyle: React.CSSProperties = {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -98,6 +132,8 @@ export const DepositInput = (props: {
|
||||||
<div>{name}</div>
|
<div>{name}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Slider marks={marks} value={mark} onChange={setMark} />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={onDeposit}
|
onClick={onDeposit}
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
import React, { useCallback, useState } from "react";
|
import React, { useCallback, useState } from "react";
|
||||||
import {
|
import {
|
||||||
|
InputType,
|
||||||
useCollateralBalance,
|
useCollateralBalance,
|
||||||
|
useSliderInput,
|
||||||
useTokenName,
|
useTokenName,
|
||||||
useUserBalance,
|
useUserBalance,
|
||||||
} from "../../hooks";
|
} from "../../hooks";
|
||||||
import { LendingReserve } from "../../models/lending";
|
import { LendingReserve } from "../../models/lending";
|
||||||
import { TokenIcon } from "../TokenIcon";
|
import { TokenIcon } from "../TokenIcon";
|
||||||
import { Button, Card, Spin } from "antd";
|
import { Button, Card, Slider, Spin } 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 { LABELS } from "../../constants";
|
import { LABELS, marks } from "../../constants";
|
||||||
import { LoadingOutlined } from "@ant-design/icons";
|
import { LoadingOutlined } from "@ant-design/icons";
|
||||||
import { ActionConfirmation } from "./../ActionConfirmation";
|
import { ActionConfirmation } from "./../ActionConfirmation";
|
||||||
|
|
||||||
|
@ -26,7 +28,6 @@ export const WithdrawInput = (props: {
|
||||||
}) => {
|
}) => {
|
||||||
const connection = useConnection();
|
const connection = useConnection();
|
||||||
const { wallet } = useWallet();
|
const { wallet } = useWallet();
|
||||||
const [value, setValue] = useState("");
|
|
||||||
const [pendingTx, setPendingTx] = useState(false);
|
const [pendingTx, setPendingTx] = useState(false);
|
||||||
const [showConfirmation, setShowConfirmation] = useState(false);
|
const [showConfirmation, setShowConfirmation] = useState(false);
|
||||||
|
|
||||||
|
@ -42,6 +43,19 @@ export const WithdrawInput = (props: {
|
||||||
reserve
|
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(() => {
|
const onWithdraw = useCallback(() => {
|
||||||
setPendingTx(true);
|
setPendingTx(true);
|
||||||
|
|
||||||
|
@ -49,7 +63,9 @@ export const WithdrawInput = (props: {
|
||||||
try {
|
try {
|
||||||
await withdraw(
|
await withdraw(
|
||||||
fromAccounts[0],
|
fromAccounts[0],
|
||||||
Math.ceil(
|
type === InputType.Slider
|
||||||
|
? (mark * collateralBalanceLamports) / 100
|
||||||
|
: Math.ceil(
|
||||||
collateralBalanceLamports *
|
collateralBalanceLamports *
|
||||||
(parseFloat(value) / collateralBalanceInLiquidity)
|
(parseFloat(value) / collateralBalanceInLiquidity)
|
||||||
),
|
),
|
||||||
|
@ -68,14 +84,17 @@ export const WithdrawInput = (props: {
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, [
|
}, [
|
||||||
connection,
|
|
||||||
wallet,
|
|
||||||
collateralBalanceLamports,
|
|
||||||
collateralBalanceInLiquidity,
|
|
||||||
value,
|
|
||||||
reserve,
|
|
||||||
fromAccounts,
|
|
||||||
address,
|
address,
|
||||||
|
collateralBalanceInLiquidity,
|
||||||
|
collateralBalanceLamports,
|
||||||
|
connection,
|
||||||
|
fromAccounts,
|
||||||
|
mark,
|
||||||
|
reserve,
|
||||||
|
setValue,
|
||||||
|
type,
|
||||||
|
value,
|
||||||
|
wallet,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const bodyStyle: React.CSSProperties = {
|
const bodyStyle: React.CSSProperties = {
|
||||||
|
@ -103,9 +122,7 @@ export const WithdrawInput = (props: {
|
||||||
<TokenIcon mintAddress={reserve?.liquidityMint} />
|
<TokenIcon mintAddress={reserve?.liquidityMint} />
|
||||||
<NumericInput
|
<NumericInput
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(val: any) => {
|
onChange={setValue}
|
||||||
setValue(val);
|
|
||||||
}}
|
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
style={{
|
style={{
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
|
@ -118,6 +135,8 @@ export const WithdrawInput = (props: {
|
||||||
<div>{name}</div>
|
<div>{name}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Slider marks={marks} value={mark} onChange={setMark} />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={onWithdraw}
|
onClick={onWithdraw}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
export * from "./ids";
|
export * from "./ids";
|
||||||
export * from "./labels";
|
export * from "./labels";
|
||||||
export * from "./math";
|
export * from "./math";
|
||||||
|
export * from "./marks";
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
export const marks = {
|
||||||
|
0: "0%",
|
||||||
|
25: "25%",
|
||||||
|
50: "50%",
|
||||||
|
75: "75%",
|
||||||
|
100: "100%",
|
||||||
|
};
|
|
@ -83,7 +83,9 @@ export const useLending = () => {
|
||||||
setLendingAccounts([]);
|
setLendingAccounts([]);
|
||||||
|
|
||||||
const queryLendingAccounts = async () => {
|
const queryLendingAccounts = async () => {
|
||||||
const programAccounts = (await connection.getProgramAccounts(LENDING_PROGRAM_ID));
|
const programAccounts = await connection.getProgramAccounts(
|
||||||
|
LENDING_PROGRAM_ID
|
||||||
|
);
|
||||||
|
|
||||||
const accounts = programAccounts
|
const accounts = programAccounts
|
||||||
.map(processAccount)
|
.map(processAccount)
|
||||||
|
@ -116,7 +118,6 @@ export const useLending = () => {
|
||||||
}),
|
}),
|
||||||
].flat() as string[];
|
].flat() as string[];
|
||||||
|
|
||||||
|
|
||||||
// This will pre-cache all accounts used by pools
|
// This will pre-cache all accounts used by pools
|
||||||
// All those accounts are updated whenever there is a change
|
// All those accounts are updated whenever there is a change
|
||||||
await getMultipleAccounts(connection, toQuery, "single").then(
|
await getMultipleAccounts(connection, toQuery, "single").then(
|
||||||
|
@ -130,9 +131,7 @@ export const useLending = () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
// HACK: fix, force account refresh
|
// HACK: fix, force account refresh
|
||||||
programAccounts
|
programAccounts.map(processAccount).filter((item) => item !== undefined);
|
||||||
.map(processAccount)
|
|
||||||
.filter((item) => item !== undefined);
|
|
||||||
|
|
||||||
return accounts;
|
return accounts;
|
||||||
};
|
};
|
||||||
|
|
|
@ -89,7 +89,7 @@ export function MarketProvider({ children = null as any }) {
|
||||||
allMarkets.filter((a) => cache.get(a) === undefined),
|
allMarkets.filter((a) => cache.get(a) === undefined),
|
||||||
"single"
|
"single"
|
||||||
).then(({ keys, array }) => {
|
).then(({ keys, array }) => {
|
||||||
allMarkets.forEach(() => { });
|
allMarkets.forEach(() => {});
|
||||||
|
|
||||||
return array.map((item, index) => {
|
return array.map((item, index) => {
|
||||||
const marketAddress = keys[index];
|
const marketAddress = keys[index];
|
||||||
|
@ -158,7 +158,7 @@ export function MarketProvider({ children = null as any }) {
|
||||||
const info = marketByMint.get(mintAddress);
|
const info = marketByMint.get(mintAddress);
|
||||||
const market = cache.get(info?.marketInfo.address.toBase58() || "");
|
const market = cache.get(info?.marketInfo.address.toBase58() || "");
|
||||||
if (!market) {
|
if (!market) {
|
||||||
return () => { };
|
return () => {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: get recent volume
|
// TODO: get recent volume
|
||||||
|
|
|
@ -9,3 +9,4 @@ export * from "./useUserObligations";
|
||||||
export * from "./useUserObligationByReserve";
|
export * from "./useUserObligationByReserve";
|
||||||
export * from "./useBorrowedAmount";
|
export * from "./useBorrowedAmount";
|
||||||
export * from "./useUserDeposits";
|
export * from "./useUserDeposits";
|
||||||
|
export * from "./useSliderInput";
|
||||||
|
|
|
@ -21,6 +21,7 @@ export function useCollateralBalance(
|
||||||
return {
|
return {
|
||||||
balance: fromLamports(collateralRatioLamports, mint),
|
balance: fromLamports(collateralRatioLamports, mint),
|
||||||
balanceLamports: collateralRatioLamports,
|
balanceLamports: collateralRatioLamports,
|
||||||
|
mint: reserve?.collateralMint,
|
||||||
accounts,
|
accounts,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
};
|
||||||
|
};
|
|
@ -176,10 +176,10 @@ export const calculateUtilizationRatio = (reserve: LendingReserve) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const reserveMarketCap = (reserve?: LendingReserve ) => {
|
export const reserveMarketCap = (reserve?: LendingReserve) => {
|
||||||
const available = (reserve?.availableLiquidity.toNumber() || 0) ;
|
const available = reserve?.availableLiquidity.toNumber() || 0;
|
||||||
const borrowed = wadToLamports(reserve?.borrowedLiquidityWad).toNumber() ;
|
const borrowed = wadToLamports(reserve?.borrowedLiquidityWad).toNumber();
|
||||||
const total = available + borrowed;
|
const total = available + borrowed;
|
||||||
|
|
||||||
return total;
|
return total;
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import { useCollateralBalance, useTokenName } from "../../hooks";
|
||||||
useCollateralBalance,
|
|
||||||
useTokenName,
|
|
||||||
} from "../../hooks";
|
|
||||||
import { calculateBorrowAPY, LendingReserve } from "../../models/lending";
|
import { calculateBorrowAPY, LendingReserve } from "../../models/lending";
|
||||||
import { TokenIcon } from "../../components/TokenIcon";
|
import { TokenIcon } from "../../components/TokenIcon";
|
||||||
import { formatNumber, formatPct } from "../../utils/utils";
|
import { formatNumber, formatPct } from "../../utils/utils";
|
||||||
|
|
|
@ -14,9 +14,7 @@ export const DashboardView = () => {
|
||||||
return (
|
return (
|
||||||
<div className="dashboard-container">
|
<div className="dashboard-container">
|
||||||
{!connected && (
|
{!connected && (
|
||||||
<div className="dashboard-info">
|
<div className="dashboard-info">{LABELS.DASHBOARD_INFO}</div>
|
||||||
{LABELS.DASHBOARD_INFO}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
{connected &&
|
{connected &&
|
||||||
userDeposits.length === 0 &&
|
userDeposits.length === 0 &&
|
||||||
|
|
|
@ -20,13 +20,17 @@ export const HomeView = () => {
|
||||||
const marketCapLamports = reserveMarketCap(item.info);
|
const marketCapLamports = reserveMarketCap(item.info);
|
||||||
|
|
||||||
const localCache = cache;
|
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) {
|
if (!mint) {
|
||||||
return result;
|
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;
|
return result + marketCap;
|
||||||
}, 0);
|
}, 0);
|
||||||
|
@ -43,8 +47,6 @@ export const HomeView = () => {
|
||||||
return () => {
|
return () => {
|
||||||
dispose();
|
dispose();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}, [marketEmitter, midPriceInUSD, setTotalMarketSize, reserveAccounts]);
|
}, [marketEmitter, midPriceInUSD, setTotalMarketSize, reserveAccounts]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
Loading…
Reference in New Issue