Skip to content

Commit

Permalink
chore(suite): wip
Browse files Browse the repository at this point in the history
  • Loading branch information
enjojoy committed Feb 11, 2025
1 parent 92b1582 commit 25460db
Show file tree
Hide file tree
Showing 16 changed files with 204 additions and 38 deletions.
21 changes: 21 additions & 0 deletions packages/blockchain-link-types/src/responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,33 @@ export interface GetFiatRatesTickersList {
};
}

export interface PriorityFeeEstimationDetails {
maxFeePerGas: string;
maxPriorityFeePerGas: string;
minWaitTimeEstimate: number;
maxWaitTimeEstimate: number;
}

export type FeeTrend = 'up' | 'down';

export interface EstimateFee {
type: typeof RESPONSES.ESTIMATE_FEE;
payload: {
feePerUnit: string;
feePerTx?: string;
feeLimit?: string;
eip1559?: {
baseFeePerGas: string;
low: PriorityFeeEstimationDetails;
medium: PriorityFeeEstimationDetails;
high: PriorityFeeEstimationDetails;
networkCongestion: number;
latestPriorityFeeRange: string[];
historicalPriorityFeeRange: string[];
historicalBaseFeeRange: string[];
priorityFeeTrend: FeeTrend;
baseFeeTrend: FeeTrend;
};
}[];
}

Expand Down
50 changes: 37 additions & 13 deletions packages/connect/src/api/bitcoin/Fees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,43 @@ export class FeeLevels {
async loadMisc(blockchain: Blockchain) {
try {
const [response] = await blockchain.estimateFee({ blocks: [1] });
// misc coins should have only one FeeLevel (normal)
this.levels[0] = {
...this.levels[0],
...response,
// validate `feePerUnit` from the backend
// should be lower than `coinInfo.maxFee` and higher than `coinInfo.minFee`
// xrp sends values from 1 to very high number occasionally
// see: https://github.com/trezor/trezor-suite/blob/develop/packages/blockchain-link/src/workers/ripple/index.ts#L316
feePerUnit: Math.min(
this.coinInfo.maxFee,
Math.max(this.coinInfo.minFee, parseInt(response.feePerUnit, 10)),
).toString(),
};
if (response.eip1559) {
const eip1559LevelKeys = ['low', 'medium', 'high'] as const;

const { eip1559 } = response;
const eip1559Levels = eip1559LevelKeys.map(levelKey => {
const level = eip1559[levelKey];

return {
label: levelKey,
baseFeePerGas: eip1559.baseFeePerGas,
maxFeePerGas: level.maxFeePerGas,
maxPriorityFeePerGas: level.maxPriorityFeePerGas,
minWaitTimeEstimate: level.minWaitTimeEstimate / 1000, // Infura provides wait time in miliseconds
maxWaitTimeEstimate: level.maxWaitTimeEstimate / 1000,
feePerUnit: '0',
feeLimit: undefined,
ethFeeType: 'eip1559' as const,
blocks: -1,
};
});

this.levels = [...eip1559Levels];
} else {
//misc coins should have only one FeeLevel (normal), for ethereum depends on availability of eip1559
this.levels[0] = {
...this.levels[0],
...response,
// validate `feePerUnit` from the backend
// should be lower than `coinInfo.maxFee` and higher than `coinInfo.minFee`
// xrp sends values from 1 to very high number occasionally
// see: https://github.com/trezor/trezor-suite/blob/develop/packages/blockchain-link/src/workers/ripple/index.ts#L316
feePerUnit: Math.min(
this.coinInfo.maxFee,
Math.max(this.coinInfo.minFee, parseInt(response.feePerUnit, 10)),
).toString(),
};
}
} catch {
// silent
}
Expand Down
15 changes: 15 additions & 0 deletions packages/connect/src/types/fees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ 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.Number(),
minWaitTimeEstimate: Type.Number(),
});

export type FeeLevel = Static<typeof FeeLevel>;
export const FeeLevel = Type.Object({
label: Type.Union([
Expand All @@ -16,11 +24,18 @@ export const FeeLevel = Type.Object({
Type.Literal('economy'),
Type.Literal('low'),
Type.Literal('custom'),
Type.Literal('medium'),
]),
ethFeeType: Type.Optional(Type.Union([Type.Literal('legacy'), Type.Literal('eip1559')])),
feePerUnit: Type.String(),
blocks: Type.Number(),
feeLimit: Type.Optional(Type.String()), // eth gas limit
feePerTx: Type.Optional(Type.String()), // fee for BlockchainEstimateFeeParams.request.specific
baseFeePerGas: Type.Optional(Type.String()),
maxFeePerGas: Type.Optional(Type.String()),
maxPriorityFeePerGas: Type.Optional(Type.String()),
maxWaitTimeEstimate: Type.Optional(Type.Number()),
minWaitTimeEstimate: Type.Optional(Type.Number()),
});

export type SelectFeeLevel = Static<typeof SelectFeeLevel>;
Expand Down
35 changes: 35 additions & 0 deletions packages/suite/src/components/wallet/Fees/CustomFee.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,41 @@ export const CustomFee = <TFieldValues extends FormState>({
</Text>
</Row>
<Grid gap={spacings.xs} columns={useFeeLimit && !isBelowLaptop ? 2 : 1}>
{/*
{shouldUsePriorityFees && feeInfo.levels[0].ethFeeType === 'eip1559' ? (
<>
<NumberInput
label={<Translation id="TR_MAX_PRIORITY_FEE_PER_GAS" />}
locale={locale}
control={control}
inputState={getInputState(feePerUnitError)}
innerAddon={
<Text variant="tertiary" typographyStyle="label">
{feeUnits}
</Text>
}
name={MAX_PRIORITY_FEE_PER_GAS}
data-testid={MAX_PRIORITY_FEE_PER_GAS}
rules={feeRules}
bottomText={feePerUnitError?.message || null}
/>
<NumberInput
label={<Translation id="TR_MAX_FEE_PER_GAS" />}
locale={locale}
control={control}
inputState={getInputState(feePerUnitError)}
innerAddon={
<Text variant="tertiary" typographyStyle="label">
{feeUnits}
</Text>
}
name={MAX_FEE_PER_GAS}
data-testid={MAX_FEE_PER_GAS}
rules={feeRules}
bottomText={feePerUnitError?.message || null}
/>
</>
) : ( */}
{useFeeLimit ? (
<NumberInput
label={<Translation id="TR_GAS_LIMIT" />}
Expand Down
2 changes: 1 addition & 1 deletion packages/suite/src/hooks/wallet/form/useCompose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export const useCompose = <TFieldValues extends FormState>({
const switchToNearestFee = useCallback(
(composedLevels: NonNullable<UseSendFormState['composedLevels']>) => {
const { selectedFee, setMaxOutputId } = getValues();
let composed = composedLevels[selectedFee || 'normal'];
let composed = composedLevels[selectedFee || 'normal'] ?? composedLevels['medium'];

// selectedFee was not set yet (no interaction with Fees) and default (normal) fee tx is not valid
// OR setMax option was used
Expand Down
23 changes: 21 additions & 2 deletions packages/suite/src/hooks/wallet/useSendFormCompose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ export const useSendFormCompose = ({
const [composeField, setComposeField] = useState<FieldPath<FormState> | undefined>(undefined);
const [draftSaveRequest, setDraftSaveRequest] = useState(false);

const shouldUsePriorityFees = state.network.features.includes('eip1559');

const dispatch = useDispatch();

const { translationString } = useTranslation();
Expand All @@ -79,6 +81,7 @@ export const useSendFormCompose = ({
feeInfo: state.feeInfo,
excludedUtxos,
prison,
shouldUsePriorityFees,
},
}),
);
Expand All @@ -90,7 +93,16 @@ export const useSendFormCompose = ({
setLoading(false);
}
},
[account, dispatch, prison, excludedUtxos, setLoading, state.network, state.feeInfo],
[
account,
dispatch,
prison,
excludedUtxos,
setLoading,
state.network,
state.feeInfo,
shouldUsePriorityFees,
],
);

// Create a compose request
Expand Down Expand Up @@ -130,6 +142,7 @@ export const useSendFormCompose = ({
feeInfo: state.feeInfo,
excludedUtxos,
prison,
shouldUsePriorityFees,
},
}),
);
Expand Down Expand Up @@ -162,6 +175,7 @@ export const useSendFormCompose = ({
state.feeInfo,
excludedUtxos,
prison,
shouldUsePriorityFees,
],
);

Expand Down Expand Up @@ -244,7 +258,12 @@ export const useSendFormCompose = ({

const values = getValues();
const { selectedFee, setMaxOutputId } = values;
let composed = composedLevels[selectedFee || 'normal'];

let composed =
selectedFee && composedLevels[selectedFee]
? composedLevels[selectedFee]
: composedLevels.medium || composedLevels.normal;
console.log('composed', composed, composedLevels, selectedFee);

// selectedFee was not set yet (no interaction with Fees) and default (normal) fee tx is not valid
// OR setMax option was used
Expand Down
12 changes: 12 additions & 0 deletions packages/suite/src/support/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5630,6 +5630,18 @@ export default defineMessages({
defaultMessage: 'Low',
id: 'FEE_LEVEL_LOW',
},
FEE_LEVEL_MEDIUM: {
defaultMessage: 'Medium',
id: 'FEE_LEVEL_MEDIUM',
},
TR_MAX_PRIORITY_FEE_PER_GAS: {
defaultMessage: 'Max priority fee per gas',
id: 'TR_MAX_PRIORITY_FEE_PER_GAS',
},
TR_MAX_FEE_PER_GAS: {
defaultMessage: 'Max fee per gas',
id: 'TR_MAX_FEE_PER_GAS',
},
CUSTOM_FEE_IS_NOT_SET: {
defaultMessage:
'Enter the fee rate you want to spend in order to complete this transaction.',
Expand Down
8 changes: 7 additions & 1 deletion suite-common/wallet-config/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ export type NetworkSymbol =
| 'etc'
| 'xrp'
| 'bch'
| 'btg'
| 'dash'
| 'dgb'
| 'doge'
| 'nmc'
| 'vtc'
| 'zec'
| 'ada'
| 'sol'
Expand Down Expand Up @@ -57,7 +62,8 @@ export type NetworkFeature =
| 'tokens'
| 'staking'
| 'coin-definitions'
| 'nft-definitions';
| 'nft-definitions'
| 'eip1559';

type Level = `/${number}'`;
type MaybeApostrophe = `'` | '';
Expand Down
9 changes: 4 additions & 5 deletions suite-common/wallet-core/src/blockchain/blockchainThunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const getAccountSyncInterval = (symbol: NetworkSymbol) =>

// sort FeeLevels in reversed order (Low > High)
// TODO: consider to use same order in @trezor/connect to avoid double sorting
const order: FeeLevel['label'][] = ['low', 'economy', 'normal', 'high'];
const order: FeeLevel['label'][] = ['low', 'economy', 'medium', 'normal', 'high'];
const sortLevels = (levels: FeeLevel[]) =>
levels.sort((levelA, levelB) => order.indexOf(levelA.label) - order.indexOf(levelB.label));

Expand Down Expand Up @@ -127,26 +127,25 @@ export const updateFeeInfoThunk = createThunk(
let newFeeInfo;

if (network.networkType === 'ethereum') {
// NOTE: ethereum smart fees are not implemented properly in @trezor/connect Issue: https://github.com/trezor/trezor-suite/issues/5340
// create raw call to @trezor/blockchain-link, receive data and create FeeLevel.normal from it

const result = await TrezorConnect.blockchainEstimateFee({
coin: network.symbol,
request: {
blocks: [2],
feeLevels: 'smart',
specific: {
from: '0x0000000000000000000000000000000000000000',
to: '0x0000000000000000000000000000000000000000',
},
},
});

if (result.success) {
newFeeInfo = {
...result.payload,
levels: result.payload.levels.map(l => ({
...l,
blocks: -1, // NOTE: @trezor/connect returns -1 for ethereum default
label: 'normal' as const,
label: l.label || ('normal' as const),
})),
};
}
Expand Down
5 changes: 4 additions & 1 deletion suite-common/wallet-core/src/send/sendFormEthereumThunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ const calculate = (
): PrecomposedTransaction => {
let amount: string;
let max: string | undefined;
const feeInGwei = calculateEthFee(toWei(feeLevel.feePerUnit, 'gwei'), feeLevel.feeLimit || '0');
const feeInGwei = calculateEthFee(
toWei(feeLevel.feePerUnit || '0', 'gwei'),
feeLevel.feeLimit || '0',
);

const availableTokenBalance = token
? amountToSmallestUnit(token.balance!, token.decimals)
Expand Down
5 changes: 4 additions & 1 deletion suite-common/wallet-core/src/send/sendFormThunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,10 @@ export const composeSendFormTransactionFeeLevelsThunk = createThunk<
);
} else if (networkType === 'ethereum') {
response = await dispatch(
composeEthereumTransactionFeeLevelsThunk({ formState, composeContext }),
composeEthereumTransactionFeeLevelsThunk({
formState,
composeContext,
}),
);
} else if (networkType === 'ripple') {
response = await dispatch(
Expand Down
1 change: 1 addition & 0 deletions suite-common/wallet-core/src/send/sendFormTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface ComposeActionContext {
feeInfo: FeeInfo;
excludedUtxos?: ExcludedUtxos;
prison?: Record<string, unknown>;
shouldUsePriorityFees: boolean;
}

export type EthTransactionData = {
Expand Down
4 changes: 3 additions & 1 deletion suite-common/wallet-types/src/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,10 @@ export type EthTransactionData = {
amount: string;
data?: string;
gasLimit: string;
gasPrice: string;
nonce: string;
gasPrice?: string; // this field is not used for EIP1559 transactions (See EthereumTransaction and EthereumTransactionEIP1559 types in connect)
maxFeePerGas?: string;
maxPriorityFeePerGas?: string;
};

export type ExternalOutput = Exclude<ComposeOutput, { type: 'opreturn' } | { address_n: number[] }>;
Expand Down
Loading

0 comments on commit 25460db

Please sign in to comment.