Merge pull request #46 from yamijuan/swap_input

Swap input style with corresponding calculations
This commit is contained in:
Bartosz Lipinski 2021-01-24 17:03:37 -06:00 committed by GitHub
commit dcb1e6c9a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 233 additions and 103 deletions

View File

@ -1,27 +1,23 @@
import React, { useCallback, useMemo, useState } from "react"; import React, { useCallback, useEffect, useMemo, useState } from "react";
import { import { useUserBalance, useUserObligationByReserve } from "../../hooks";
useTokenName,
useUserBalance,
useUserObligationByReserve,
} from "../../hooks";
import { import {
BorrowAmountType, BorrowAmountType,
LendingReserve, LendingReserve,
LendingReserveParser, LendingReserveParser,
} from "../../models"; } from "../../models";
import { TokenIcon } from "../TokenIcon";
import { Card } from "antd"; import { Card } from "antd";
import { cache, ParsedAccount } from "../../contexts/accounts"; import { cache, ParsedAccount } from "../../contexts/accounts";
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 { borrow } from "../../actions"; import { borrow } from "../../actions";
import { CollateralSelector } from "./../CollateralSelector";
import "./style.less"; import "./style.less";
import { LABELS } from "../../constants"; import { LABELS } from "../../constants";
import { ActionConfirmation } from "./../ActionConfirmation"; import { ActionConfirmation } from "./../ActionConfirmation";
import { BackButton } from "./../BackButton"; import { BackButton } from "./../BackButton";
import { ConnectButton } from "../ConnectButton"; import { ConnectButton } from "../ConnectButton";
import CollateralInput from "../CollateralInput";
import { ArrowDownOutlined } from "@ant-design/icons";
import { useMidPriceInUSD } from "../../contexts/market";
export const BorrowInput = (props: { export const BorrowInput = (props: {
className?: string; className?: string;
@ -30,6 +26,8 @@ export const BorrowInput = (props: {
const connection = useConnection(); const connection = useConnection();
const { wallet } = useWallet(); const { wallet } = useWallet();
const [value, setValue] = useState(""); const [value, setValue] = useState("");
const [collateralValue, setCollateralValue] = useState("");
const [lastTyped, setLastTyped] = useState("collateral");
const [pendingTx, setPendingTx] = useState(false); const [pendingTx, setPendingTx] = useState(false);
const [showConfirmation, setShowConfirmation] = useState(false); const [showConfirmation, setShowConfirmation] = useState(false);
@ -46,7 +44,57 @@ export const BorrowInput = (props: {
return cache.get(id) as ParsedAccount<LendingReserve>; return cache.get(id) as ParsedAccount<LendingReserve>;
}, [collateralReserveKey]); }, [collateralReserveKey]);
const name = useTokenName(borrowReserve?.info.liquidityMint); const borrowPrice = useMidPriceInUSD(
borrowReserve.info.liquidityMint.toBase58()
).price;
const collateralPrice = useMidPriceInUSD(
collateralReserve?.info.liquidityMint.toBase58()
)?.price;
useEffect(() => {
if (collateralReserve && lastTyped === "collateral") {
const ltv = borrowReserve.info.config.loanToValueRatio / 100;
if (collateralValue) {
const nCollateralValue = parseFloat(collateralValue);
const borrowInUSD = nCollateralValue * collateralPrice * ltv;
const borrowAmount = borrowInUSD / borrowPrice;
setValue(borrowAmount.toString());
} else {
setValue("");
}
}
}, [
lastTyped,
collateralReserve,
collateralPrice,
borrowPrice,
borrowReserve,
collateralValue,
]);
useEffect(() => {
if (collateralReserve && lastTyped === "borrow") {
const ltv = borrowReserve.info.config.loanToValueRatio / 100;
if (value) {
const nValue = parseFloat(value);
const borrowInUSD = nValue * borrowPrice;
const collateralAmount = borrowInUSD / ltv / collateralPrice;
setCollateralValue(collateralAmount.toString());
} else {
setCollateralValue("");
}
}
}, [
lastTyped,
collateralReserve,
collateralPrice,
borrowPrice,
borrowReserve,
value,
]);
const { accounts: fromAccounts } = useUserBalance( const { accounts: fromAccounts } = useUserBalance(
collateralReserve?.info.collateralMint collateralReserve?.info.collateralMint
); );
@ -87,6 +135,7 @@ export const BorrowInput = (props: {
); );
setValue(""); setValue("");
setCollateralValue("");
setShowConfirmation(true); setShowConfirmation(true);
} catch { } catch {
// TODO: // TODO:
@ -126,32 +175,50 @@ export const BorrowInput = (props: {
justifyContent: "space-around", justifyContent: "space-around",
}} }}
> >
<div className="borrow-input-title">{LABELS.SELECT_COLLATERAL}</div> <div
<CollateralSelector style={{
reserve={borrowReserve.info} display: "flex",
collateralReserve={collateralReserveKey} flexDirection: "row",
onCollateralReserve={setCollateralReserveKey} justifyContent: "space-evenly",
/> alignItems: "center",
}}
<div className="borrow-input-title">{LABELS.BORROW_QUESTION}</div> >
<div className="token-input"> <CollateralInput
<TokenIcon mintAddress={borrowReserve?.info.liquidityMint} /> title="Collateral (estimated)"
<NumericInput reserve={borrowReserve.info}
value={value} amount={parseFloat(collateralValue) || 0}
onChange={(val: any) => { onInputChange={(val: number | null) => {
setValue(val); setCollateralValue(val?.toString() || "");
setLastTyped("collateral");
}} }}
style={{ onCollateralReserve={(key) => {
fontSize: 20, setCollateralReserveKey(key);
boxShadow: "none",
borderColor: "transparent",
outline: "transparent",
}} }}
placeholder="0.00"
/> />
<div>{name}</div> </div>
<ArrowDownOutlined />
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-evenly",
alignItems: "center",
}}
>
<CollateralInput
title="Borrow Amount"
reserve={borrowReserve.info}
amount={parseFloat(value) || 0}
onInputChange={(val: number | null) => {
setValue(val?.toString() || "");
setLastTyped("borrow");
}}
disabled={true}
hideBalance={true}
/>
</div> </div>
<ConnectButton <ConnectButton
size="large"
type="primary" type="primary"
onClick={onBorrow} onClick={onBorrow}
loading={pendingTx} loading={pendingTx}

View File

@ -1,14 +1,7 @@
import React, { useCallback, useState } from "react"; import React, { useCallback, useState } from "react";
import { import { InputType, useSliderInput, useUserBalance } from "../../hooks";
InputType,
useSliderInput,
useTokenName,
useUserBalance,
} from "../../hooks";
import { LendingReserve } from "../../models/lending"; import { LendingReserve } from "../../models/lending";
import { TokenIcon } from "../TokenIcon";
import { Card, Slider } from "antd"; import { Card, Slider } from "antd";
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";
@ -17,6 +10,7 @@ import "./style.less";
import { ActionConfirmation } from "./../ActionConfirmation"; import { ActionConfirmation } from "./../ActionConfirmation";
import { LABELS, marks } from "../../constants"; import { LABELS, marks } from "../../constants";
import { ConnectButton } from "../ConnectButton"; import { ConnectButton } from "../ConnectButton";
import CollateralInput from "../CollateralInput";
export const DepositInput = (props: { export const DepositInput = (props: {
className?: string; className?: string;
@ -31,7 +25,6 @@ export const DepositInput = (props: {
const reserve = props.reserve; const reserve = props.reserve;
const address = props.address; const address = props.address;
const name = useTokenName(reserve?.liquidityMint);
const { accounts: fromAccounts, balance, balanceLamports } = useUserBalance( const { accounts: fromAccounts, balance, balanceLamports } = useUserBalance(
reserve?.liquidityMint reserve?.liquidityMint
); );
@ -41,7 +34,7 @@ export const DepositInput = (props: {
if (typeof val === "string") { if (typeof val === "string") {
return (parseFloat(val) / balance) * 100; return (parseFloat(val) / balance) * 100;
} else { } else {
return ((val * balance) / 100).toFixed(2); return (val * balance) / 100;
} }
}, },
[balance] [balance]
@ -108,26 +101,30 @@ export const DepositInput = (props: {
}} }}
> >
<div className="deposit-input-title">{LABELS.DEPOSIT_QUESTION}</div> <div className="deposit-input-title">{LABELS.DEPOSIT_QUESTION}</div>
<div className="token-input"> <div
<TokenIcon mintAddress={reserve?.liquidityMint} /> style={{
<NumericInput display: "flex",
value={value} flexDirection: "row",
onChange={setValue} justifyContent: "space-evenly",
autoFocus={true} alignItems: "center",
style={{ }}
fontSize: 20, >
boxShadow: "none", <CollateralInput
borderColor: "transparent", title="Amount"
outline: "transparent", reserve={reserve}
amount={parseFloat(value) || 0}
onInputChange={(val: number | null) => {
setValue(val?.toString() || "");
}} }}
placeholder="0.00" disabled={true}
hideBalance={true}
/> />
<div>{name}</div>
</div> </div>
<Slider marks={marks} value={pct} onChange={setPct} /> <Slider marks={marks} value={pct} onChange={setPct} />
<ConnectButton <ConnectButton
size="large"
type="primary" type="primary"
onClick={onDeposit} onClick={onDeposit}
loading={pendingTx} loading={pendingTx}

View File

@ -1,38 +1,38 @@
import React, { useCallback, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
import { import {
EnrichedLendingObligation, EnrichedLendingObligation,
InputType, InputType,
useAccountByMint, useAccountByMint,
useSliderInput, useSliderInput,
useTokenName,
useUserBalance, useUserBalance,
} from "../../hooks"; } from "../../hooks";
import { LendingReserve } from "../../models"; import { LendingReserve } from "../../models";
import { TokenIcon } from "../TokenIcon";
import { Card, Slider } from "antd"; import { Card, Slider } from "antd";
import { ParsedAccount, useMint } from "../../contexts/accounts"; import { ParsedAccount, useMint } from "../../contexts/accounts";
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 { repay } from "../../actions"; import { repay } from "../../actions";
import { CollateralSelector } from "./../CollateralSelector";
import "./style.less"; import "./style.less";
import { LABELS, marks } from "../../constants"; import { LABELS, marks } from "../../constants";
import { ActionConfirmation } from "./../ActionConfirmation"; import { ActionConfirmation } from "./../ActionConfirmation";
import { fromLamports, wadToLamports } from "../../utils/utils"; import { fromLamports, wadToLamports } from "../../utils/utils";
import { notify } from "../../utils/notifications"; import { notify } from "../../utils/notifications";
import { ConnectButton } from "../ConnectButton"; import { ConnectButton } from "../ConnectButton";
import CollateralInput from "../CollateralInput";
import { useMidPriceInUSD } from "../../contexts/market";
export const RepayInput = (props: { export const RepayInput = (props: {
className?: string; className?: string;
borrowReserve: ParsedAccount<LendingReserve>; borrowReserve: ParsedAccount<LendingReserve>;
collateralReserve?: ParsedAccount<LendingReserve>; collateralReserve: ParsedAccount<LendingReserve>;
obligation: EnrichedLendingObligation; obligation: EnrichedLendingObligation;
}) => { }) => {
const connection = useConnection(); const connection = useConnection();
const { wallet } = useWallet(); const { wallet } = useWallet();
const [lastTyped, setLastTyped] = useState("repay");
const [pendingTx, setPendingTx] = useState(false); const [pendingTx, setPendingTx] = useState(false);
const [showConfirmation, setShowConfirmation] = useState(false); const [showConfirmation, setShowConfirmation] = useState(false);
const [collateralValue, setCollateralValue] = useState("");
const repayReserve = props.borrowReserve; const repayReserve = props.borrowReserve;
const obligation = props.obligation; const obligation = props.obligation;
@ -45,7 +45,6 @@ export const RepayInput = (props: {
const borrowAmount = fromLamports(borrowAmountLamports, liquidityMint); const borrowAmount = fromLamports(borrowAmountLamports, liquidityMint);
const collateralReserve = props.collateralReserve; const collateralReserve = props.collateralReserve;
const name = useTokenName(repayReserve?.info.liquidityMint);
const { accounts: fromAccounts } = useUserBalance( const { accounts: fromAccounts } = useUserBalance(
repayReserve.info.liquidityMint repayReserve.info.liquidityMint
); );
@ -54,10 +53,11 @@ export const RepayInput = (props: {
const convert = useCallback( const convert = useCallback(
(val: string | number) => { (val: string | number) => {
setLastTyped("repay");
if (typeof val === "string") { if (typeof val === "string") {
return (parseFloat(val) / borrowAmount) * 100; return (parseFloat(val) / borrowAmount) * 100;
} else { } else {
return ((val * borrowAmount) / 100).toFixed(2); return (val * borrowAmount) / 100;
} }
}, },
[borrowAmount] [borrowAmount]
@ -85,7 +85,6 @@ export const RepayInput = (props: {
: Math.ceil( : Math.ceil(
borrowAmountLamports * (parseFloat(value) / borrowAmount) borrowAmountLamports * (parseFloat(value) / borrowAmount)
); );
await repay( await repay(
fromAccounts[0], fromAccounts[0],
toRepayLamports, toRepayLamports,
@ -98,6 +97,7 @@ export const RepayInput = (props: {
); );
setValue(""); setValue("");
setCollateralValue("");
setShowConfirmation(true); setShowConfirmation(true);
} catch (error) { } catch (error) {
notify({ notify({
@ -125,6 +125,53 @@ export const RepayInput = (props: {
setValue, setValue,
]); ]);
const collateralPrice = useMidPriceInUSD(
collateralReserve?.info.liquidityMint.toBase58()
)?.price;
useEffect(() => {
if (collateralReserve && lastTyped === "repay") {
const collateralInQuote = obligation.info.collateralInQuote;
const collateral = collateralInQuote * collateralPrice;
if (value) {
const borrowRatio = (parseFloat(value) / borrowAmount) * 100;
const collateralAmount = (borrowRatio * collateral) / 100;
setCollateralValue(collateralAmount.toString());
} else {
setCollateralValue("");
}
}
}, [
lastTyped,
collateralReserve,
obligation.info.collateralInQuote,
collateralPrice,
borrowAmount,
value,
]);
useEffect(() => {
if (collateralReserve && lastTyped === "collateral") {
const collateralInQuote = obligation.info.collateralInQuote;
const collateral = collateralInQuote * collateralPrice;
if (collateralValue) {
const collateralRatio =
(parseFloat(collateralValue) / collateral) * 100;
const borrowValue = (collateralRatio * borrowAmount) / 100;
setValue(borrowValue.toString());
} else {
setValue("");
}
}
}, [
lastTyped,
collateralReserve,
obligation.info.collateralInQuote,
collateralPrice,
borrowAmount,
collateralValue,
]);
const bodyStyle: React.CSSProperties = { const bodyStyle: React.CSSProperties = {
display: "flex", display: "flex",
flex: 1, flex: 1,
@ -145,33 +192,50 @@ export const RepayInput = (props: {
justifyContent: "space-around", justifyContent: "space-around",
}} }}
> >
<div className="repay-input-title">{LABELS.REPAY_QUESTION}</div> <div
<div className="token-input"> style={{
<TokenIcon mintAddress={repayReserve?.info.liquidityMint} /> display: "flex",
<NumericInput flexDirection: "row",
value={value} justifyContent: "space-evenly",
onChange={setValue} alignItems: "center",
autoFocus={true} }}
style={{ >
fontSize: 20, <CollateralInput
boxShadow: "none", title="Repay Amount"
borderColor: "transparent", reserve={repayReserve.info}
outline: "transparent", amount={parseFloat(value) || 0}
onInputChange={(val: number | null) => {
setValue(val?.toString() || "");
setLastTyped("repay");
}} }}
placeholder="0.00" disabled={true}
hideBalance={true}
/> />
<div>{name}</div>
</div> </div>
<Slider marks={marks} value={pct} onChange={setPct} /> <Slider marks={marks} value={pct} onChange={setPct} />
<div className="repay-input-title">{LABELS.COLLATERAL}</div> <div
<CollateralSelector style={{
reserve={repayReserve.info} display: "flex",
collateralReserve={collateralReserve?.pubkey.toBase58()} flexDirection: "row",
disabled={true} justifyContent: "space-evenly",
/> alignItems: "center",
}}
>
<CollateralInput
title="Collateral Amount (estimated)"
reserve={collateralReserve?.info}
amount={parseFloat(collateralValue) || 0}
onInputChange={(val: number | null) => {
setCollateralValue(val?.toString() || "");
setLastTyped("collateral");
}}
disabled={true}
hideBalance={true}
/>
</div>
<ConnectButton <ConnectButton
type="primary" type="primary"
size="large"
onClick={onRepay} onClick={onRepay}
loading={pendingTx} loading={pendingTx}
disabled={fromAccounts.length === 0} disabled={fromAccounts.length === 0}

View File

@ -3,13 +3,10 @@ import {
InputType, InputType,
useUserCollateralBalance, useUserCollateralBalance,
useSliderInput, useSliderInput,
useTokenName,
useUserBalance, useUserBalance,
} from "../../hooks"; } from "../../hooks";
import { LendingReserve } from "../../models/lending"; import { LendingReserve } from "../../models/lending";
import { TokenIcon } from "../TokenIcon";
import { Card, Slider } from "antd"; import { Card, Slider } from "antd";
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";
@ -18,6 +15,7 @@ import "./style.less";
import { LABELS, marks } from "../../constants"; import { LABELS, marks } from "../../constants";
import { ActionConfirmation } from "./../ActionConfirmation"; import { ActionConfirmation } from "./../ActionConfirmation";
import { ConnectButton } from "../ConnectButton"; import { ConnectButton } from "../ConnectButton";
import CollateralInput from "../CollateralInput";
export const WithdrawInput = (props: { export const WithdrawInput = (props: {
className?: string; className?: string;
@ -32,7 +30,6 @@ export const WithdrawInput = (props: {
const reserve = props.reserve; const reserve = props.reserve;
const address = props.address; const address = props.address;
const name = useTokenName(reserve?.liquidityMint);
const { const {
balanceLamports: collateralBalanceLamports, balanceLamports: collateralBalanceLamports,
accounts: fromAccounts, accounts: fromAccounts,
@ -46,7 +43,7 @@ export const WithdrawInput = (props: {
if (typeof val === "string") { if (typeof val === "string") {
return (parseFloat(val) / collateralBalanceInLiquidity) * 100; return (parseFloat(val) / collateralBalanceInLiquidity) * 100;
} else { } else {
return ((val * collateralBalanceInLiquidity) / 100).toFixed(2); return (val * collateralBalanceInLiquidity) / 100;
} }
}, },
[collateralBalanceInLiquidity] [collateralBalanceInLiquidity]
@ -120,26 +117,30 @@ export const WithdrawInput = (props: {
}} }}
> >
<div className="withdraw-input-title">{LABELS.WITHDRAW_QUESTION}</div> <div className="withdraw-input-title">{LABELS.WITHDRAW_QUESTION}</div>
<div className="token-input"> <div
<TokenIcon mintAddress={reserve?.liquidityMint} /> style={{
<NumericInput display: "flex",
value={value} flexDirection: "row",
onChange={setValue} justifyContent: "space-evenly",
autoFocus={true} alignItems: "center",
style={{ }}
fontSize: 20, >
boxShadow: "none", <CollateralInput
borderColor: "transparent", title="Amount"
outline: "transparent", reserve={reserve}
amount={parseFloat(value) || 0}
onInputChange={(val: number | null) => {
setValue(val?.toString() || "");
}} }}
placeholder="0.00" disabled={true}
hideBalance={true}
/> />
<div>{name}</div>
</div> </div>
<Slider marks={marks} value={pct} onChange={setPct} /> <Slider marks={marks} value={pct} onChange={setPct} />
<ConnectButton <ConnectButton
size="large"
type="primary" type="primary"
onClick={onWithdraw} onClick={onWithdraw}
loading={pendingTx} loading={pendingTx}

View File

@ -275,6 +275,7 @@ export const useEnrichedPools = (pools: PoolInfo[]) => {
}; };
// Do not add pools here, causes a really bad infinite rendering loop. Use poolKeys instead. // Do not add pools here, causes a really bad infinite rendering loop. Use poolKeys instead.
}, [ }, [
pools,
tokenMap, tokenMap,
dailyVolume, dailyVolume,
poolKeys, poolKeys,

View File

@ -37,7 +37,7 @@ export const RepayReserveView = () => {
const reserve = lendingReserve?.info; const reserve = lendingReserve?.info;
if (!reserve || !lendingReserve || !lendingObligation) { if (!reserve || !lendingReserve || !lendingObligation || !repayReserve) {
return null; return null;
} }