on master help sign in
bridge/src/components/L2/Dropdowns/FeeDropdown.tsx
136 lines 3.7 kB history raw
1import React, { useCallback, useEffect, useState } from 'react';
2import { Box, Row, Icon, StatelessTextInput } from '@tlon/indigo-react';
3import { DEFAULT_GAS_PRICE_GWEI } from 'lib/constants';
4import useGasPrice from 'lib/useGasPrice';
6import Dropdown from './Dropdown';
7import './FeeDropdown.scss';
8import { defaultGasValues } from 'lib/getSuggestedGasPrice';
10const PRICE_LABELS = ['Fast', 'Normal', 'Slow'];
12export interface GasPriceData {
13 price: number;
14 wait: string;
15 maxFeePerGas: string; // hex
16 maxPriorityFeePerGas: number;
17 suggestedBaseFeePerGas: number;
20export const formatDisplay = ({ price, wait }: GasPriceData) =>
21 `${price} gwei (${wait} min)`;
23export default function FeeDropdown({
24 setGasPrice,
25}: {
26 setGasPrice: (g: GasPriceData) => void;
27}) {
28 const {
29 suggestedGasPrices,
30 }: {
31 suggestedGasPrices: {
32 fast: GasPriceData;
33 average: GasPriceData;
34 low: GasPriceData;
35 };
36 } = useGasPrice(DEFAULT_GAS_PRICE_GWEI);
38 const [open, setOpen] = useState(false);
39 const [useCustom, setUseCustom] = useState(false);
40 const [custom, setCustom] = useState<string | undefined>();
41 const [selected, setSelected] = useState<GasPriceData>(
42 suggestedGasPrices.average
43 );
45 const handleCustom = (e: React.ChangeEvent<HTMLInputElement>) => {
46 // Allow digits and a single decimal point
47 const cleanedValue = e.target.value
48 .replace(/[^0-9.]/g, '')
49 .replace(/(\..*)\./g, '$1');
50 const cleanedNum = Number(cleanedValue);
51 setCustom(cleanedValue);
52 setGasPrice(defaultGasValues(cleanedNum).average);
54 let customWait = 'Unknown';
56 const { fast, average, low } = suggestedGasPrices;
58 if (cleanedNum < low.price) {
59 customWait = `> ${low.wait}`;
60 } else if (cleanedNum < average.price) {
61 customWait = `~${low.wait}`;
62 } else if (cleanedNum < fast.price) {
63 customWait = `~${average.wait}`;
64 } else {
65 customWait = `< ${fast.wait}`;
66 }
68 setSelected({
69 price: cleanedNum,
70 wait: customWait,
71 maxFeePerGas: cleanedNum,
72 maxPriorityFeePerGas: cleanedNum,
73 suggestedBaseFeePerGas: cleanedNum,
74 });
75 };
77 useEffect(() => {
78 setSelected(suggestedGasPrices.average);
79 }, [suggestedGasPrices]);
81 const selectPrice = useCallback(
82 (value: GasPriceData) => () => {
83 setSelected(value);
84 setGasPrice(value);
85 setOpen(false);
86 },
87 [setGasPrice, setSelected, setOpen]
88 );
90 if (useCustom) {
91 return (
92 <Row className="fee-dropdown custom">
93 <StatelessTextInput
94 value={custom}
95 className="custom-input"
96 placeholder="Custom gwei"
97 onChange={handleCustom}
98 autoFocus
99 />
100 <Icon
101 icon="ChevronSouth"
102 onClick={() => {
103 setOpen(true);
104 setUseCustom(false);
105 setCustom('');
106 }}
107 />
108 </Row>
109 );
110 }
112 return (
113 <Dropdown
114 className="fee-dropdown"
115 open={open}
116 value={formatDisplay(selected)}
117 toggleOpen={() => setOpen(!open)}>
118 <Box className="prices">
119 {Object.values(suggestedGasPrices).map(
120 (value: GasPriceData, ind: number) => (
121 <Row
122 className="price"
123 onClick={selectPrice(value)}
124 key={`gas-price-${ind}`}>
125 <Box className="speed">{PRICE_LABELS[ind]}</Box>
126 <Box className="time">{formatDisplay(value)}</Box>
127 </Row>
128 )
129 )}
130 <Row className="price" onClick={() => setUseCustom(true)}>
131 <Box className="time">Custom...</Box>
132 </Row>
133 </Box>
134 </Dropdown>
135 );