Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

feat(PoolPage): When set input value, calculate the other input value. Validate if pool ratio is right #185

Merged
merged 5 commits into from
May 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions packages/app/src/components/CoinInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,27 +95,29 @@ export function useCoinInput({
}

function getInputProps() {
const handleInputPropsChange = (val: string) => {
if (isReadOnly) return;
const next = val !== "" ? parseValueBigInt(val) : null;
if (typeof onChange === "function") {
onChange(next);
} else {
setAmount(next);
}
};

return {
...params,
coin,
isReadOnly,
value: formatValue(amount),
displayType: (isReadOnly ? "text" : "input") as DisplayType,
onInput,
onChange: (val: string) => {
if (isReadOnly) return;
const next = val !== "" ? parseValueBigInt(val) : null;
if (typeof onChange === "function") {
onChange(next);
} else {
setAmount(next);
}
},
onChange: handleInputPropsChange,
isAllowed: ({ value }: NumberFormatValues) =>
parseValueBigInt(value) <= MAX_U64_VALUE,
setMaxBalance: () => {
onInput?.();
setAmount(getSafeMaxBalance());
handleInputPropsChange(formatValue(getSafeMaxBalance()));
},
balance: formatValue(coinBalance?.amount || BigInt(0)),
showBalance,
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const MINT_AMOUNT = parseUnits('2000', DECIMAL_UNITS).toBigInt();
export const ONE_ASSET = parseUnits('1', DECIMAL_UNITS).toBigInt();
export const RECAPTCHA_SITE_KEY = import.meta.env.VITE_RECAPTCHA_SITE_KEY!;
export const ENABLE_FAUCET_API = import.meta.env.VITE_ENABLE_FAUCET_API === 'true';
export const SLIPPAGE_TOLERANCE = 0.005;

// Max value supported
// eslint-disable-next-line @typescript-eslint/no-loss-of-precision
Expand Down
15 changes: 15 additions & 0 deletions packages/app/src/lib/asset.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { toNumber } from "fuels"
import { ONE_ASSET } from "~/config"

export const calculateRatio = (fromAmount?: bigint | null, toAmount?: bigint | null) => {
const _fromAmount = fromAmount || BigInt(0);
luizstacio marked this conversation as resolved.
Show resolved Hide resolved
const _toAmount = toAmount || BigInt(0);

const ratio = (
toNumber(ONE_ASSET * _fromAmount) /
toNumber(_toAmount) /
toNumber(ONE_ASSET)
);

return (isNaN(ratio) || !isFinite(ratio)) ? 0 : ratio;
}
89 changes: 53 additions & 36 deletions packages/app/src/pages/PoolPage/AddLiquidity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import { useMutation, useQuery } from "react-query";
import { Button } from "~/components/Button";
import { CoinInput, useCoinInput } from "~/components/CoinInput";
import { Spinner } from "~/components/Spinner";
import { DECIMAL_UNITS, ONE_ASSET } from "~/config";
import { DECIMAL_UNITS, ONE_ASSET, SLIPPAGE_TOLERANCE } from "~/config";
import { useContract } from "~/context/AppContext";
import { calculateRatio } from "~/lib/asset";
import assets from "~/lib/CoinsMetadata";
import type { Coin } from "~/types";
import { Pages } from "~/types/pages";
Expand Down Expand Up @@ -75,17 +76,42 @@ export default function AddLiquidity() {
contract.callStatic.get_info()
);

const handleChangeFromValue = (val: bigint | null) => {
fromInput.setAmount(val);

if (reservesFromToRatio) {
const _val = val || BigInt(0);
const newToValue = Math.round(toNumber(_val) / reservesFromToRatio);
toInput.setAmount(BigInt(newToValue));
}
}
const handleChangeToValue = (val: bigint | null) => {
toInput.setAmount(val);

if (reservesFromToRatio) {
const _val = val || BigInt(0);
const newFromValue = Math.round(toNumber(_val) * reservesFromToRatio);
fromInput.setAmount(BigInt(newFromValue));
}
}

const fromInput = useCoinInput({
coin: coinFrom,
onChangeCoin: (coin: Coin) => setCoins([coin, coinTo]),
gasFee: BigInt(1),
onChange: handleChangeFromValue
});

const toInput = useCoinInput({
coin: coinTo,
onChangeCoin: (coin: Coin) => setCoins([coin, coinTo]),
onChange: handleChangeToValue
});

const reservesFromToRatio = calculateRatio(poolInfo?.eth_reserve, poolInfo?.token_reserve);
const reservesToFromRatio = calculateRatio(poolInfo?.token_reserve, poolInfo?.eth_reserve);
const addLiquidityRatio = calculateRatio(fromInput.amount, toInput.amount);

const addLiquidityMutation = useMutation(
async () => {
const fromAmount = fromInput.amount;
Expand Down Expand Up @@ -143,26 +169,35 @@ export default function AddLiquidity() {
setToInitialAmount(toInput.amount);
}, [fromInput.amount, toInput.amount]);

const handleCreatePool = () => {
const fromAmount = fromInput.amount;
const toAmount = toInput.amount;
const validateCreatePool = () => {
const errors = [];

if (!fromAmount) {
throw new Error('"fromAmount" is required');
if (!fromInput.amount) {
errors.push(`Enter ${coinFrom.name} amount`);
}
if (!toAmount) {
throw new Error('"toAmount" is required');
if (!toInput.amount) {
errors.push(`Enter ${coinTo.name} amount`);
}

if (!fromInput.hasEnoughBalance) {
throw new Error(`Insufficient ${coinFrom.name} balance`);
errors.push(`Insufficient ${coinFrom.name} balance`);
}
if (!toInput.hasEnoughBalance) {
throw new Error(`Insufficient ${coinTo.name} balance`);
errors.push(`Insufficient ${coinTo.name} balance`);
}

addLiquidityMutation.mutate();
};
if (reservesFromToRatio) {
const minRatio = reservesFromToRatio * (1 - SLIPPAGE_TOLERANCE);
const maxRatio = reservesFromToRatio * (1 + SLIPPAGE_TOLERANCE);

if ( addLiquidityRatio < minRatio || addLiquidityRatio > maxRatio ) {
luizstacio marked this conversation as resolved.
Show resolved Hide resolved
errors.push(`Entered ratio doesn't match pool`);
}
}

return errors;
}

const errorsCreatePull = validateCreatePool();

return addLiquidityMutation.isLoading ? (
<div className="mt-6 mb-8 flex justify-center">
Expand Down Expand Up @@ -209,26 +244,12 @@ export default function AddLiquidity() {
<div className="flex flex-col">
<span>
<>
ETH/DAI:{" "}
{
+(
toNumber(ONE_ASSET * poolInfo.eth_reserve) /
toNumber(poolInfo.token_reserve) /
toNumber(ONE_ASSET)
).toFixed(6)
}
ETH/DAI: {reservesFromToRatio.toFixed(6)}
</>
</span>
<span>
<>
DAI/ETH:{" "}
{
+(
toNumber(ONE_ASSET * poolInfo.token_reserve) /
toNumber(poolInfo.eth_reserve) /
toNumber(ONE_ASSET)
).toFixed(6)
}
DAI/ETH: {reservesToFromRatio.toFixed(6)}
</>
</span>
</div>
Expand All @@ -237,17 +258,13 @@ export default function AddLiquidity() {
</div>
) : null}
<Button
isDisabled={!fromInput.hasEnoughBalance || !toInput.hasEnoughBalance}
isDisabled={!!errorsCreatePull.length}
isFull
size="lg"
variant="primary"
onPress={handleCreatePool}
onPress={errorsCreatePull.length ? undefined : () => addLiquidityMutation.mutate()}
>
{!fromInput.hasEnoughBalance
? `Insufficient ${coinFrom.name} balance`
: !toInput.hasEnoughBalance
? `Insufficient ${coinTo.name} balance`
: "Confirm"}
{errorsCreatePull.length ? errorsCreatePull[0] : 'Confirm'}
</Button>
</>
);
Expand Down