Skip to content
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
6 changes: 6 additions & 0 deletions .yarn/versions/eip5792-gas-editor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
releases:
browser-extension: patch
helios-popup: patch

declined:
- helios
209 changes: 209 additions & 0 deletions packages/popup/src/components/AdvancedGasEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import {useState} from 'react'
import PropTypes from 'prop-types'
import {useTranslation} from 'react-i18next'
import Button from '@fluent-wallet/component-button'
import {
convertDecimal,
convertValueToData,
formatDecimalToHex,
formatHexToDecimal,
GWEI_DECIMALS,
} from '@fluent-wallet/data-format'
import {NETWORK_TYPE} from '../constants'
import {useTransactionEstimate} from '../hooks/useTransactionEstimate'
import {useGasFeeValidation} from '../hooks/useGasFeeValidation'
import GasCost from './GasCost'
import GasPriceFields from './GasPriceFields'
import GasAndNonceFields from './GasAndNonceFields'

// initialValues and onSave use base-unit decimal strings.
function AdvancedGasEditor({
transaction,
network,
initialValues,
uses1559Fees,
suggestedNonce,
maxGasLimit,
canEditNonce = true,
onSave,
}) {
const {t} = useTranslation()
const networkTypeIsCfx = network.type === NETWORK_TYPE.CFX
const isCfxChain =
networkTypeIsCfx ||
network.ticker.symbol?.toLowerCase() === NETWORK_TYPE.CFX

const [values, setValues] = useState(() => ({
gasPrice: convertDecimal(
initialValues.gasPrice || '',
'divide',
GWEI_DECIMALS,
),
maxFeePerGas: convertDecimal(
initialValues.maxFeePerGas || '',
'divide',
GWEI_DECIMALS,
),
maxPriorityFeePerGas: convertDecimal(
initialValues.maxPriorityFeePerGas || '',
'divide',
GWEI_DECIMALS,
),
gasLimit: '',
nonce: canEditNonce ? initialValues.nonce || '' : '',
}))

const gasLimit = values.gasLimit || initialValues.gasLimit

const transactionForEstimate = {
...transaction,
gas: formatDecimalToHex(gasLimit),
storageLimit: networkTypeIsCfx
? formatDecimalToHex(initialValues.storageLimit)
: undefined,
nonce: canEditNonce ? formatDecimalToHex(values.nonce) : transaction.nonce,
gasPrice: uses1559Fees
? undefined
: convertValueToData(values.gasPrice, GWEI_DECIMALS),
maxFeePerGas: uses1559Fees
? convertValueToData(values.maxFeePerGas, GWEI_DECIMALS)
: undefined,
maxPriorityFeePerGas: uses1559Fees
? convertValueToData(values.maxPriorityFeePerGas, GWEI_DECIMALS)
: undefined,
}

for (const field of [
'gas',
'storageLimit',
'nonce',
'gasPrice',
'maxFeePerGas',
'maxPriorityFeePerGas',
]) {
if (!transactionForEstimate[field]) {
delete transactionForEstimate[field]
}
}

const {data: estimate, error} = useTransactionEstimate({
transaction: transactionForEstimate,
network,
})

const resolvedGasLimit = gasLimit || formatHexToDecimal(estimate?.gasLimit)
const storageLimit =
initialValues.storageLimit ||
formatHexToDecimal(estimate?.storageCollateralized)

const errors = useGasFeeValidation({
values: {
...values,
gasLimit: resolvedGasLimit,
},
uses1559Fees,
isCfxChain,
minGasLimit: formatHexToDecimal(estimate?.gasUsed || '21000'),
maxGasLimit,
canEditNonce,
})

const updateField = (field, value) => {
setValues(current => ({...current, [field]: value}))
}

const handleSave = () => {
onSave({
gasPrice: uses1559Fees
? ''
: convertDecimal(values.gasPrice, 'multiply', GWEI_DECIMALS),
maxFeePerGas: uses1559Fees
? convertDecimal(values.maxFeePerGas, 'multiply', GWEI_DECIMALS)
: '',
maxPriorityFeePerGas: uses1559Fees
? convertDecimal(values.maxPriorityFeePerGas, 'multiply', GWEI_DECIMALS)
: '',
gasLimit: resolvedGasLimit,
storageLimit,
nonce: canEditNonce
? values.nonce
: formatHexToDecimal(transaction.nonce),
})
}

return (
<div className="flex min-h-0 flex-1 flex-col">
<main className="min-h-0 flex-1 overflow-y-auto px-4 pt-3">
<GasCost estimate={estimate || {error}} network={network} />

<GasPriceFields
isCfxChain={isCfxChain}
uses1559Fees={uses1559Fees}
inputGasPrice={values.gasPrice}
gasPriceErr={errors.gasPrice}
onChangeGasPrice={value => updateField('gasPrice', value)}
inputMaxFeePerGas={values.maxFeePerGas}
onChangeMaxFeePerGas={value => updateField('maxFeePerGas', value)}
inputMaxPriorityFeePerGas={values.maxPriorityFeePerGas}
maxPriorityFeePerGasErr={errors.maxPriorityFeePerGas}
onChangeMaxPriorityFeePerGas={value =>
updateField('maxPriorityFeePerGas', value)
}
/>

<GasAndNonceFields
showStorageLimit={networkTypeIsCfx}
canEditNonce={canEditNonce}
inputGasLimit={values.gasLimit}
gasLimitErr={errors.gasLimit}
onChangeGasLimit={value => updateField('gasLimit', value)}
inputNonce={values.nonce}
nonceErr={errors.nonce}
onChangeNonce={value => updateField('nonce', value)}
storageLimit={storageLimit}
nonce={
values.nonce ||
formatHexToDecimal(estimate?.nonce) ||
suggestedNonce
}
gasLimit={resolvedGasLimit}
/>
</main>

<footer className="shrink-0 px-4 pb-6">
<Button
className="w-full"
id="saveAdvancedGasFeeBtn"
onClick={handleSave}
disabled={
!resolvedGasLimit ||
(networkTypeIsCfx && !storageLimit) ||
Object.values(errors).some(Boolean)
}
>
{t('save')}
</Button>
</footer>
</div>
)
}

AdvancedGasEditor.propTypes = {
transaction: PropTypes.object.isRequired,
network: PropTypes.object.isRequired,
initialValues: PropTypes.shape({
gasPrice: PropTypes.string,
maxFeePerGas: PropTypes.string,
maxPriorityFeePerGas: PropTypes.string,
gasLimit: PropTypes.string,
storageLimit: PropTypes.string,
nonce: PropTypes.string,
}).isRequired,
uses1559Fees: PropTypes.bool.isRequired,
suggestedNonce: PropTypes.string,
maxGasLimit: PropTypes.string,
canEditNonce: PropTypes.bool,
onSave: PropTypes.func.isRequired,
}

export default AdvancedGasEditor
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import PropTypes from 'prop-types'
import {useTranslation} from 'react-i18next'
import {toThousands} from '@fluent-wallet/data-format'
import {EditOutlined} from '@fluent-wallet/component-icons'
import {WrapIcon, NumberInput} from '../../../components'
import WrapIcon from './WrapIcon'
import NumberInput from './NumberInput'

function CustomOptional({
networkTypeIsCfx,
isHistoryTx,
function GasAndNonceFields({
showStorageLimit,
canEditNonce,
inputGasLimit,
gasLimitErr,
onChangeGasLimit,
Expand All @@ -22,15 +23,21 @@ function CustomOptional({
const [showGasLimitInput, setShowGasLimitInput] = useState(false)
const [showNonceInput, setShowNonceInput] = useState(false)

const isGasLimitInputVisible = showGasLimitInput || Boolean(gasLimitErr)

return (
<div className="flex flex-col">
<div className="flex flex-col mb-3">
<div
className={`flex justify-between ${showGasLimitInput ? 'mb-2' : ''}`}
className={`flex justify-between ${
isGasLimitInputVisible ? 'mb-2' : ''
}`}
>
<span>{t('gasLimit')}</span>
<span
className={`flex items-center ${showGasLimitInput ? 'hidden' : ''}`}
className={`flex items-center ${
isGasLimitInputVisible ? 'hidden' : ''
}`}
>
<span id="gasLimit">{toThousands(gasLimit || '21000')}</span>
<WrapIcon
Expand All @@ -44,7 +51,7 @@ function CustomOptional({
</span>{' '}
</div>
<NumberInput
containerClassName={`${showGasLimitInput ? '' : 'hidden'}`}
containerClassName={isGasLimitInputVisible ? '' : 'hidden'}
width="w-full"
id="gasLimitInput"
value={inputGasLimit}
Expand All @@ -53,13 +60,13 @@ function CustomOptional({
onChange={value => onChangeGasLimit(value)}
/>
</div>
{networkTypeIsCfx && (
{showStorageLimit && (
<div className={`flex justify-between mb-3`}>
<span>{t('storageLimit')}</span>
<span id="storageLimit">{toThousands(storageLimit || '0')}</span>
</div>
)}
{!isHistoryTx && (
{canEditNonce && (
<div className="flex flex-col mb-3">
<div
className={`flex justify-between ${showNonceInput ? 'mb-2' : ''}`}
Expand Down Expand Up @@ -94,9 +101,9 @@ function CustomOptional({
)
}

CustomOptional.propTypes = {
networkTypeIsCfx: PropTypes.bool,
isHistoryTx: PropTypes.bool,
GasAndNonceFields.propTypes = {
showStorageLimit: PropTypes.bool,
canEditNonce: PropTypes.bool,
inputGasLimit: PropTypes.string,
gasLimitErr: PropTypes.string,
onChangeGasLimit: PropTypes.func,
Expand All @@ -108,4 +115,4 @@ CustomOptional.propTypes = {
gasLimit: PropTypes.string,
}

export default CustomOptional
export default GasAndNonceFields
49 changes: 30 additions & 19 deletions packages/popup/src/components/GasCost.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import PropTypes from 'prop-types'
import {useTranslation} from 'react-i18next'
import {DisplayBalance} from '../components'
import {useCurrentTicker} from '../hooks/useApi'
import {useEstimateTx} from '../hooks'
import {NETWORK_TYPE} from '../constants'
import useDebouncedValue from '../hooks/useDebouncedValue'
import DisplayBalance from './DisplayBalance'

function GasCost({sendParams, networkTypeIsCfx}) {
function GasCost({estimate, network}) {
const {t} = useTranslation()
const {symbol, decimals} = useCurrentTicker()
const estimateRst = useEstimateTx(sendParams) || {}
const {symbol, decimals} = network.ticker
const networkTypeIsCfx = network.type === NETWORK_TYPE.CFX

const {
error,
willPayCollateral,
willPayTxFee,
storageFeeDrip,
gasFeeDrip,
txFeeDrip,
} = estimateRst
} = estimate
const isBePayed = willPayCollateral === false || willPayTxFee === false
const isBeAllPayed = willPayCollateral === false && willPayTxFee === false
const partPayedFeeDrip =
Expand All @@ -34,17 +35,21 @@ function GasCost({sendParams, networkTypeIsCfx}) {
<div className="flex flex-col border-b border-gray-20 mb-4">
<div className="flex justify-between items-center mb-4">
<span className="text-gray-40 font-medium">{t('gasCost')}</span>
<DisplayBalance
id="realPayedFee"
balance={realPayedFeeDrip}
maxWidth={230}
maxWidthStyle="max-w-[230px]"
className="!font-bold"
symbol={symbol}
decimals={decimals}
/>
{error ? (
<span className="text-gray-40">—</span>
) : (
<DisplayBalance
id="realPayedFee"
balance={realPayedFeeDrip}
maxWidth={230}
maxWidthStyle="max-w-[230px]"
className="!font-bold"
symbol={symbol}
decimals={decimals}
/>
)}
</div>
{networkTypeIsCfx && (
{networkTypeIsCfx && !error && (
<>
<div className="flex justify-between items-center mb-3">
<span className="text-gray-40 font-medium">{t('sponsored')}</span>
Expand Down Expand Up @@ -92,8 +97,14 @@ function GasCost({sendParams, networkTypeIsCfx}) {
}

GasCost.propTypes = {
sendParams: PropTypes.object,
networkTypeIsCfx: PropTypes.bool,
estimate: PropTypes.object.isRequired,
network: PropTypes.shape({
type: PropTypes.string,
ticker: PropTypes.shape({
symbol: PropTypes.string,
decimals: PropTypes.number,
}).isRequired,
}).isRequired,
}

export default GasCost
Loading
Loading