Skip to content

Commit

Permalink
refactor: gas gas gas [FRE-446]
Browse files Browse the repository at this point in the history
  • Loading branch information
grikomsn committed Jan 19, 2024
1 parent 2a84d51 commit 72a2308
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 51 deletions.
33 changes: 6 additions & 27 deletions src/components/AssetInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { BigNumber } from "bignumber.js";
import { clsx } from "clsx";
import { formatUnits } from "ethers";
import { MouseEventHandler, useMemo } from "react";
import toast from "react-hot-toast";

import { AssetWithMetadata, useAssets } from "@/context/assets";
import { useSettingsStore } from "@/context/settings";
Expand Down Expand Up @@ -46,7 +45,7 @@ function AssetInput({
context,
isLoading,
}: Props) {
const { assetsByChainID, getNativeAssets, getFeeDenom } = useAssets();
const { assetsByChainID, getNativeAssets } = useAssets();

const assets = useMemo(() => {
if (!chain) {
Expand Down Expand Up @@ -85,37 +84,17 @@ function AssetInput({
const handleMax: MouseEventHandler<HTMLButtonElement> = (event) => {
if (!selectedAssetBalance || !chain || !asset) return;

let amount = new BigNumber(selectedAssetBalance);
let balance = new BigNumber(selectedAssetBalance);

if (event.shiftKey) {
onAmountChange?.(amount.toString());
onAmountChange?.(balance.toString());
return;
}

const feeDenom = getFeeDenom(chain.chainID);
const { gasComputed } = useSettingsStore.getState();
gasComputed && (balance = balance.minus(gasComputed));

// if selected asset is the fee denom, subtract the fee
if (feeDenom && feeDenom.denom === asset.denom) {
const { gas } = useSettingsStore.getState();

const { gasPrice } = chain.feeAssets.find((a) => a.denom === feeDenom.denom)!;

const fee = new BigNumber(gasPrice.average).multipliedBy(gas).shiftedBy(-(feeDenom.decimals ?? 6)); // denom decimals

amount = amount.minus(fee);
if (amount.isNegative()) {
amount = new BigNumber(0);
toast.error(
<p>
<strong>Insufficient Balance</strong>
<br />
You need to have at least ≈{fee.toString()} to accommodate gas fees.
</p>,
);
}
}

onAmountChange?.(amount.toString());
onAmountChange?.(balance.toString());
};

return (
Expand Down
26 changes: 14 additions & 12 deletions src/components/SettingsDialog/GasSetting.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
import { clsx } from "clsx";

import { useSettingsStore } from "@/context/settings";
import { formatNumberWithCommas, formatNumberWithoutCommas } from "@/utils/number";

export const GasSetting = () => {
const currentValue = useSettingsStore((state) => state.gas);
const currentValue = useSettingsStore((state) => state.gasMultiplier);

return (
<div className="flex items-center space-x-2 p-2">
<h3>Gas</h3>
<h3>Gas Multiplier</h3>
<div className="flex-grow" />
<div className="flex w-full max-w-32 flex-col items-stretch gap-1">
<div className="relative text-sm">
<input
className={clsx(
"rounded-lg border px-2 py-1 text-end tabular-nums transition",
"w-full number-input-arrows-hide",
)}
type="number"
value={currentValue}
className="w-full rounded-lg border px-2 py-1 text-end tabular-nums transition"
type="text"
inputMode="numeric"
value={formatNumberWithCommas(currentValue)}
min={0}
onChange={(event) => {
const value = Math.max(0, +event.target.value);
useSettingsStore.setState({ gas: value.toString() });
let latest = formatNumberWithoutCommas(event.target.value);
latest = latest.replace(/^[0]{2,}/, "0"); // Remove leading zeros
latest = latest.replace(/[^\d,]/g, ""); // Remove non-numeric and non-decimal characters
latest = latest.replace(/[,]{2,}/g, ","); // Remove multiple commas

const value = Math.max(0, +latest);
useSettingsStore.setState({ gasMultiplier: value.toString() });
}}
/>
</div>
Expand Down
7 changes: 5 additions & 2 deletions src/components/SwapWidget/SwapDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const SwapDetails = ({
}: Props) => {
const [open, control] = useDisclosureKey("swapDetailsCollapsible");

const { gas, slippage } = useSettingsStore();
const { gasComputed, slippage } = useSettingsStore();

const axelarTransferOperation = useMemo(() => {
for (const op of route.operations) {
Expand Down Expand Up @@ -172,7 +172,10 @@ export const SwapDetails = ({
<PencilSquareIcon className="h-3 w-3" />
</button>
</SimpleTooltip>
{parseFloat(gas).toLocaleString()}
{gasComputed &&
parseFloat(gasComputed).toLocaleString("en-US", {
maximumFractionDigits: 8,
})}
</dd>
<dt>Bridging Fee</dt>
<dd>
Expand Down
8 changes: 7 additions & 1 deletion src/components/SwapWidget/SwapWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,13 @@ export function SwapWidget() {
routeWarningMessage={routeWarningMessage}
/>
{insufficientBalance && (
<p className="text-center text-sm font-semibold text-red-500">Insufficient Balance</p>
<p className="animate-slide-up-and-fade text-center text-sm font-semibold text-red-500">
{typeof insufficientBalance === "string" ? (
<>Insufficient Balance: {insufficientBalance}</>
) : (
<>Insufficient Balance</>
)}
</p>
)}
</div>
)}
Expand Down
43 changes: 39 additions & 4 deletions src/components/SwapWidget/useSwapWidget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { createWithEqualityFn as create } from "zustand/traditional";

import { AssetWithMetadata, useAssets } from "@/context/assets";
import { useAnyDisclosureOpen } from "@/context/disclosures";
import { useSettingsStore } from "@/context/settings";
import { trackWallet } from "@/context/track-wallet";
import { useAccount } from "@/hooks/useAccount";
import { useBalancesByChain } from "@/hooks/useBalancesByChain";
Expand Down Expand Up @@ -90,6 +91,9 @@ export function useSwapWidget() {

const { data: balances } = useBalancesByChain(srcAccount?.address, srcChain, srcAssets);

const gasComputed = useSettingsStore((state) => state.gasComputed);
const gasMultiplier = useSettingsStore((state) => state.gasMultiplier);

// #endregion

/////////////////////////////////////////////////////////////////////////////
Expand All @@ -116,8 +120,12 @@ export function useSwapWidget() {
const balanceStr = balances[asset.denom] ?? "0";
const balance = parseFloat(formatUnits(balanceStr, asset.decimals));

if (gasComputed && parsedAmount + +gasComputed > balance) {
return `You need to have at least more than ≈${gasComputed} to accommodate gas fees.`;
}

return parsedAmount > balance;
}, [amountIn, balances, srcAsset]);
}, [amountIn, balances, gasComputed, srcAsset]);

const swapPriceImpactPercent = useMemo(() => {
if (!route?.swapPriceImpactPercent) return undefined;
Expand Down Expand Up @@ -277,6 +285,33 @@ export function useSwapWidget() {

// #region -- side effects

/**
* compute gas amount on source chain change
*/
useEffect(() => {
return useSwapFormStore.subscribe(
(state) => state.sourceChain,
(srcChain) => {
if (!srcChain) return;
const feeDenom = getFeeDenom(srcChain.chainID);
if (!feeDenom) return;
const { gasPrice } = srcChain.feeAssets.find(({ denom }) => {
return denom === feeDenom.denom;
})!;
useSettingsStore.setState({
gasComputed: new BigNumber(gasPrice.average)
.multipliedBy(gasMultiplier)
.shiftedBy(-(feeDenom.decimals ?? 6))
.toString(),
});
},
{
equalityFn: shallow,
fireImmediately: true,
},
);
}, [gasMultiplier, getFeeDenom]);

/**
* sync either amount in or out depending on {@link direction}
*/
Expand Down Expand Up @@ -374,7 +409,7 @@ export function useSwapWidget() {
}
if (wallet) {
try {
await wallet.client.addChain?.({
await wallet.client?.addChain?.({
chain: {
bech32_prefix: wallet.chain.bech32_prefix,
chain_id: wallet.chain.chain_id,
Expand Down Expand Up @@ -454,7 +489,7 @@ export function useSwapWidget() {
}
if (wallet) {
try {
await wallet.client.addChain?.({
await wallet.client?.addChain?.({
chain: {
bech32_prefix: wallet.chain.bech32_prefix,
chain_id: wallet.chain.chain_id,
Expand Down Expand Up @@ -528,7 +563,7 @@ export function useSwapWidget() {
});
if (wallet) {
try {
await wallet.client.addChain?.({
await wallet.client?.addChain?.({
chain: {
bech32_prefix: wallet.chain.bech32_prefix,
chain_id: wallet.chain.chain_id,
Expand Down
4 changes: 2 additions & 2 deletions src/components/TransactionDialog/TransactionDialogContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export interface RouteTransaction {
interface Props {
route: RouteResponse;
transactionCount: number;
insufficentBalance?: boolean;
insufficentBalance?: boolean | string;
onClose: () => void;
}

Expand Down Expand Up @@ -403,7 +403,7 @@ function TransactionDialogContent({ route, onClose, insufficentBalance, transact
"disabled:cursor-not-allowed disabled:opacity-75",
)}
onClick={onSubmit}
disabled={transacting || insufficentBalance}
disabled={transacting || !!insufficentBalance}
>
Submit
</button>
Expand Down
2 changes: 1 addition & 1 deletion src/components/TransactionDialog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface Props {
isLoading?: boolean;
route?: RouteResponse;
transactionCount: number;
insufficientBalance?: boolean;
insufficientBalance?: boolean | string;
shouldShowPriceImpactWarning?: boolean;
routeWarningMessage?: string;
routeWarningTitle?: string;
Expand Down
7 changes: 5 additions & 2 deletions src/context/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@ import { create } from "zustand";
import { persist } from "zustand/middleware";

interface SettingsStore {
gas: string;
gasComputed?: string;
gasMultiplier: string;
slippage: string;
}

export const defaultValues: SettingsStore = {
gas: (150_000).toString(),
gasComputed: undefined,
gasMultiplier: (150_000).toString(),
slippage: (3).toString(),
};

export const useSettingsStore = create<SettingsStore>()(
persist(() => defaultValues, {
name: "SettingsState",
version: 1,
}),
);

0 comments on commit 72a2308

Please sign in to comment.