Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Priority fees on EVMs (EIP-1559) #16342

Draft
wants to merge 4 commits into
base: develop
Choose a base branch
from
Draft
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
20 changes: 1 addition & 19 deletions packages/connect/src/api/ethereum/EthereumFees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { BigNumber } from '@trezor/utils/src/bigNumber';

import { Blockchain } from '../../backend/BlockchainLink';
import type { EthereumNetworkInfo, FeeLevel } from '../../types';
import { Blocks, MiscFeeLevels, findBlocksForFee } from '../common/MiscFees';
import { Blocks, MiscFeeLevels } from '../common/MiscFees';

type EipResponse1559Level = 'low' | 'medium' | 'high';
type Eip1559Level = 'low' | 'normal' | 'high';
Expand Down Expand Up @@ -72,22 +72,4 @@ export class EthereumFeeLevels extends MiscFeeLevels {

return this.levels;
}

updateEthereumCustomFee(
feePerUnit: string,
effectiveGasPrice?: string,
maxPriorityFeePerGas?: string,
) {
// remove "custom" level from list
this.levels = this.levels.filter(l => l.label !== 'custom');
// recreate "custom" level
const blocks = findBlocksForFee(feePerUnit, this.blocks);
this.levels.push({
label: 'custom',
feePerUnit,
blocks,
maxPriorityFeePerGas,
effectiveGasPrice,
});
}
}
10 changes: 2 additions & 8 deletions packages/connect/src/types/fees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,6 @@ export const FeeInfo = Type.Object({
dustLimit: Type.Number(),
});

export type PriorityFeeEstimationDetails = Static<typeof PriorityFeeEstimationDetails>;
export const PriorityFeeEstimationDetails = Type.Object({
maxFeePerGas: Type.String(),
maxPriorityFeePerGas: Type.String(),
maxWaitTimeEstimate: Type.Optional(Type.Number()),
minWaitTimeEstimate: Type.Optional(Type.Number()),
});

export type FeeLevel = Static<typeof FeeLevel>;
export const FeeLevel = Type.Object({
label: Type.Union([
Expand All @@ -32,6 +24,8 @@ export const FeeLevel = Type.Object({
baseFeePerGas: Type.Optional(Type.String()),
maxFeePerGas: Type.Optional(Type.String()),
effectiveGasPrice: Type.Optional(Type.String()),
customMaxBaseFeePerGas: Type.Optional(Type.String()),
customMaxPriorityFeePerGas: Type.Optional(Type.String()),
maxPriorityFeePerGas: Type.Optional(Type.String()),
maxWaitTimeEstimate: Type.Optional(Type.Number()),
minWaitTimeEstimate: Type.Optional(Type.Number()),
Expand Down
5 changes: 5 additions & 0 deletions packages/suite/src/actions/wallet/stake/stakeFormActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ export const calculate = (
max,
fee: feeInBaseUnits,
feePerByte: feeLevel.feePerUnit,
maxFeePerGas: feeLevel.effectiveGasPrice || undefined,
maxPriorityFeePerGas: feeLevel.maxPriorityFeePerGas || undefined,
feeLimit: feeLevel.feeLimit,
bytes: 0,
inputs: [],
Expand Down Expand Up @@ -123,6 +125,7 @@ export const composeStakingTransaction = (
) => {
const { account, network } = formState;
const composeOutputs = getExternalComposeOutput(formValues, account, network);
console.log('composeOutputs', formValues, composeOutputs);
if (!composeOutputs) return; // no valid Output

const { output, decimals } = composeOutputs;
Expand All @@ -131,6 +134,8 @@ export const composeStakingTransaction = (
// wrap response into PrecomposedLevels object where key is a FeeLevel label
const wrappedResponse: PrecomposedLevels = {};
const compareWithAmount = formValues.stakeType === 'stake';

console.log('predefinedLevels', predefinedLevels);
const response = predefinedLevels.map(level =>
calculateTransaction(
availableBalance,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import {
UNSTAKE_INTERCHANGES,
} from '@suite-common/wallet-constants';
import { ComposeActionContext, selectSelectedDevice } from '@suite-common/wallet-core';
import { calculateEffectiveGasPrice } from '@suite-common/wallet-core/src/send/sendFormEthereumUtils';
import {
AddressDisplayOptions,
ExternalOutput,
PrecomposedTransaction,
PrecomposedTransactionFinal,
StakeFormState,
} from '@suite-common/wallet-types';
import { calculateEthFee, getAccountIdentity, isPending } from '@suite-common/wallet-utils';
import { calculateMaxEthFee, getAccountIdentity, isPending } from '@suite-common/wallet-utils';
import TrezorConnect, { FeeLevel } from '@trezor/connect';
import { BigNumber } from '@trezor/utils/src/bigNumber';

Expand All @@ -31,14 +32,18 @@ import {

import { calculate, composeStakingTransaction } from './stakeFormActions';

const calculateTransaction = (
const calculateStakingTransaction = (
availableBalance: string,
output: ExternalOutput,
feeLevel: FeeLevel,
compareWithAmount = true,
symbol: NetworkSymbol,
): PrecomposedTransaction => {
const feeInWei = calculateEthFee(toWei(feeLevel.feePerUnit, 'gwei'), feeLevel.feeLimit || '0');
const isEip1559 = feeLevel.maxPriorityFeePerGas !== undefined;

const feeInWei = isEip1559
? calculateMaxEthFee(feeLevel.effectiveGasPrice, feeLevel.feeLimit)
: calculateMaxEthFee(toWei(feeLevel.feePerUnit, 'gwei'), feeLevel.feeLimit);

const stakingParams = {
feeInBaseUnits: feeInWei,
Expand All @@ -47,6 +52,8 @@ const calculateTransaction = (
minAmountForWithdrawalInBaseUnits: toWei(MIN_ETH_FOR_WITHDRAWALS.toString(), 'ether'),
};

console.log('feeLevel', feeLevel);

return calculate(availableBalance, output, feeLevel, compareWithAmount, symbol, stakingParams);
};

Expand Down Expand Up @@ -81,11 +88,20 @@ export const composeTransaction =
predefinedLevels.forEach(l => (l.feeLimit = customFeeLimit));
}
// in case when selectedFee is set to 'custom' construct this FeeLevel from values
//TODO: calculate effective gas price here?
if (formValues.selectedFee === 'custom') {
const calculatedEffectiveGasPrice = calculateEffectiveGasPrice(
formValues.customMaxPriorityFeePerGas,
formValues.customMaxBaseFeePerGas,
);
predefinedLevels.push({
label: 'custom',
feePerUnit: formValues.feePerUnit,
feeLimit: formValues.feeLimit,
customMaxBaseFeePerGas: formValues.customMaxBaseFeePerGas,
customMaxPriorityFeePerGas: formValues.customMaxPriorityFeePerGas,
effectiveGasPrice: calculatedEffectiveGasPrice,
maxPriorityFeePerGas: toWei(Number(formValues.customMaxPriorityFeePerGas), 'gwei'),
blocks: -1,
});
}
Expand All @@ -94,7 +110,7 @@ export const composeTransaction =
formValues,
formState,
predefinedLevels,
calculateTransaction,
calculateStakingTransaction,
undefined,
customFeeLimit,
);
Expand Down Expand Up @@ -150,6 +166,8 @@ export const signTransaction =
identity,
amount: formValues.outputs[0].amount,
gasPrice: transactionInfo.feePerByte,
maxFeePerGas: transactionInfo.maxFeePerGas,
maxPriorityFeePerGas: transactionInfo.maxPriorityFeePerGas,
nonce,
chainId: network.chainId,
});
Expand All @@ -161,6 +179,8 @@ export const signTransaction =
identity,
amount: formValues.outputs[0].amount,
gasPrice: transactionInfo.feePerByte,
maxFeePerGas: transactionInfo.maxFeePerGas,
maxPriorityFeePerGas: transactionInfo.maxPriorityFeePerGas,
nonce,
chainId: network.chainId,
interchanges: UNSTAKE_INTERCHANGES,
Expand All @@ -172,6 +192,8 @@ export const signTransaction =
from: account.descriptor,
identity,
gasPrice: transactionInfo.feePerByte,
maxFeePerGas: transactionInfo.maxFeePerGas,
maxPriorityFeePerGas: transactionInfo.maxPriorityFeePerGas,
nonce,
chainId: network.chainId,
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { formatDurationStrict } from '@suite-common/suite-utils';
import { NetworkType, networks } from '@suite-common/wallet-config';
import { NetworkType, getNetworkFeatures, networks } from '@suite-common/wallet-config';
import { FeeInfo, GeneralPrecomposedTransactionFinal, StakeType } from '@suite-common/wallet-types';
import { getFee } from '@suite-common/wallet-utils';
import { Box, IconButton, Note, Row, Text } from '@trezor/components';
Expand Down Expand Up @@ -47,14 +47,22 @@ export const TransactionReviewSummary = ({
const fees = useSelector(state => state.wallet.fees);
const locale = useLocales();
const { symbol, accountType, index, networkType } = account;

const network = networks[symbol];
const fee = getFee(networkType, tx);

const baseFee = fees[symbol].levels[0].baseFeePerGas;
const hasEip1559Feature = getNetworkFeatures(symbol).includes('eip1559');
const shouldUsePriorityFees = !!tx.fee && hasEip1559Feature && !!baseFee;
const fee = getFee({ account, tx, shouldUsePriorityFees });

const estimateTime = getEstimatedTime(networkType, fees[account.symbol], tx);

const formFeeRate = drafts[currentAccountKey]?.feePerUnit;
const isFeeCustom = drafts[currentAccountKey]?.selectedFee === 'custom';
const isComposedFeeRateDifferent = isFeeCustom && formFeeRate !== fee;

const isEthereumNetworkType = networkType === 'ethereum';

return (
<Row columnGap={spacings.md} rowGap={spacings.xxs} flexWrap="wrap">
<Row gap={spacings.xxs}>
Expand All @@ -74,25 +82,23 @@ export const TransactionReviewSummary = ({
</Note>
)}

{!!tx.feeLimit && network.networkType !== 'solana' && (
{!!tx.feeLimit && network.networkType !== 'solana' && !hasEip1559Feature && (
<Note iconName="gasPump">
<Translation id="TR_GAS_LIMIT" />
{': '}
{tx.feeLimit}
</Note>
)}

{networkType === 'ethereum' ? (
<Note iconName="gasPump">
<Translation id="TR_GAS_PRICE" />
{': '}
<FeeRate feeRate={fee} networkType={network.networkType} symbol={symbol} />
</Note>
) : (
<Note iconName="receipt">
<FeeRate feeRate={fee} networkType={network.networkType} symbol={symbol} />
</Note>
)}
<Note iconName={isEthereumNetworkType ? 'gasPump' : 'receipt'}>
{isEthereumNetworkType && (
<>
<Translation id="TR_GAS_PRICE" />
{': '}
</>
)}
<FeeRate feeRate={fee} networkType={network.networkType} symbol={symbol} />
</Note>

{isComposedFeeRateDifferent && network.networkType === 'bitcoin' && (
<Translation id="TR_FEE_RATE_CHANGED" />
Expand Down
12 changes: 10 additions & 2 deletions packages/suite/src/components/wallet/Fees/CustomFee/CurrentFee.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,20 @@ type CurrentFeeProps = {
feeIconName: IconName;
currentFee: string;
symbol: NetworkSymbol;
isEip1559?: boolean;
};

export const CurrentFee = ({ networkType, feeIconName, currentFee, symbol }: CurrentFeeProps) => (
// For priority fees it should show current base fee
export const CurrentFee = ({
networkType,
feeIconName,
currentFee,
symbol,
isEip1559 = false,
}: CurrentFeeProps) => (
<Row justifyContent="space-between">
<Text variant="tertiary" typographyStyle="hint">
<Translation id="TR_CURRENT_FEE_CUSTOM_FEES" />
<Translation id={isEip1559 ? 'TR_CURRENT_BASE_FEE' : 'TR_CURRENT_FEE_CUSTOM_FEES'} />
</Text>
<Text variant="default" typographyStyle="hint">
<Row alignItems="center" gap={spacings.xxs}>
Expand Down
15 changes: 15 additions & 0 deletions packages/suite/src/components/wallet/Fees/CustomFee/CustomFee.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
UseFormSetValue,
} from 'react-hook-form';

import { fromWei } from 'web3-utils';

import { NetworkSymbol, NetworkType } from '@suite-common/wallet-config';
import { FeeInfo, FormState } from '@suite-common/wallet-types';
import { getFeeUnits, isInteger } from '@suite-common/wallet-utils';
Expand Down Expand Up @@ -75,7 +77,17 @@ export const CustomFee = <TFieldValues extends FormState>({

const locale = useSelector(selectLanguage);

const normalLevel = feeInfo.levels.filter(level => level.label === 'normal')[0];

const isEip1559 = normalLevel.maxPriorityFeePerGas !== undefined;

const currentBaseFee = fromWei(Number(normalLevel.baseFeePerGas), 'Gwei');

const getCurrentFee = () => {
if (isEip1559) {
return `${currentBaseFee}`;
}

const { levels } = feeInfo;
const middleIndex = Math.floor((levels.length - 1) / 2);

Expand All @@ -92,11 +104,14 @@ export const CustomFee = <TFieldValues extends FormState>({
feeIconName={feeIconName}
currentFee={getCurrentFee()}
symbol={symbol}
isEip1559={isEip1559}
/>
{networkType === 'ethereum' ? (
<CustomFeeEthereum
{...props}
networkType={networkType}
isEip1559={isEip1559}
currentBaseFee={currentBaseFee}
feeInfo={feeInfo}
register={register}
control={control}
Expand Down
Loading
Loading