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

feat(suite): display rent in fees summary #17244

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
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
3 changes: 1 addition & 2 deletions packages/blockchain-link-types/src/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ export interface EstimateFeeParams {
to?: string; // eth to
data?: string; // eth tx data, sol tx message
value?: string; // eth tx amount
isCreatingAccount?: boolean; // sol account creation
newTokenAccountProgramName?: 'spl-token' | 'spl-token-2022'; // program name of the Solana Token account that is being created, ignored if isCreatingAccount is false, default: 'spl-token'
newAccountProgramName?: 'staking' | 'spl-token' | 'spl-token-2022'; // program name of the Solana account that is being created, default: 'spl-token'
};
}

Expand Down
24 changes: 15 additions & 9 deletions packages/blockchain-link/src/workers/solana/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { STAKE_ACCOUNT_V2_SIZE } from '@everstake/wallet-sdk-solana';
import {
AccountInfoBase,
ClusterUrl,
Expand Down Expand Up @@ -459,17 +460,19 @@ const getInfo = async (request: Request<MessageTypes.GetInfo>, isTestnet: boolea
} as const;
};

const getTokenSize = (programName: TokenProgramName) =>
({ 'spl-token': _getTokenSize(), 'spl-token-2022': _getToken2022Size() })[programName];
export type AccountProgramName = TokenProgramName | 'staking';

const getAccountProgramSize = (programName: AccountProgramName) =>
({
staking: STAKE_ACCOUNT_V2_SIZE,
'spl-token': _getTokenSize(),
'spl-token-2022': _getToken2022Size(),
})[programName];

const estimateFee = async (request: Request<MessageTypes.EstimateFee>) => {
const api = await request.connect();

const {
data: messageHex,
isCreatingAccount,
newTokenAccountProgramName = 'spl-token',
} = request.payload.specific ?? {};
const { data: messageHex, newAccountProgramName } = request.payload.specific ?? {};

if (messageHex == null) {
throw new Error('Could not estimate fee for transaction.');
Expand All @@ -479,9 +482,12 @@ const estimateFee = async (request: Request<MessageTypes.EstimateFee>) => {

const priorityFee = await getPriorityFee(api.rpc, message, transaction.signatures);
const baseFee = await getBaseFee(api.rpc, message);
const accountCreationFee = isCreatingAccount

const accountCreationFee = newAccountProgramName
? await api.rpc
.getMinimumBalanceForRentExemption(BigInt(getTokenSize(newTokenAccountProgramName)))
.getMinimumBalanceForRentExemption(
BigInt(getAccountProgramSize(newAccountProgramName)),
)
.send()
: BigInt(0);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,8 @@ TrezorConnect.solanaComposeTransaction({
payload: {
serializedTx: string,
additionalInfo: {
isCreatingAccount: boolean,
// in case of token transfer:
newTokenAccountProgramName: "spl-token" | "spl-token-2022",
newAccountProgramName: "staking" | "spl-token" | "spl-token-2022",
tokenAccountInfo: {
baseAddress: string,
tokenProgram: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ export default {
serializedTx:
'0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010006098b42f51ed008e27e643818f0503bdfeb6535dc815e3a9fd41a7ce42344f888dc297187b6006964cafcb0a71cdf7d16cf80563debd388a20ed2beae6de8f0e23d4dcf19bed853ae158e8c5ad250530e09f8fb4b82bee35ec1a3c89d30b2b183f500000000000000000000000000000000000000000000000000000000000000008f7329c364a8e1d0376058cc672d81ce5ff8585a46c0adcb918ca7e3f0dc82a08c97258f4e2489f1bb3d1029148e0d830b5a1399daff1084048e7bd8dbe9f8590306466fe5211732ffecadba72c39be7bc8ce5bbc5f7126b2c439b3a40000000f07f39f63a83ade085f851ca297f39b6993fd7b0fccba5e3aff7511fad40da3906ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a9dd762ba278d68a101267dbdcd6b7e6e1a84e080acdf553ba8caf0d072fbb6d200406000502400d030006000903a0860100000000000506000104070308000804020701000a0c0a0000000000000001',
additionalInfo: {
isCreatingAccount: true,
newTokenAccountProgramName: 'spl-token',
newAccountProgramName: 'spl-token',
tokenAccountInfo: {
baseAddress: 'Aey9o8JXzTcQdjJVrV4Y56xzt5qHkLPWLgAQmaodUojm',
tokenProgram: 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
Expand Down
3 changes: 1 addition & 2 deletions packages/connect/src/api/blockchainEstimateFee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,7 @@ export default class BlockchainEstimateFee extends AbstractMethod<'blockchainEst
{ name: 'from', type: 'string' },
{ name: 'to', type: 'string' },
{ name: 'txsize', type: 'number' },
{ name: 'isCreatingAccount', type: 'boolean' },
{ name: 'newTokenAccountProgramName', type: 'string' },
{ name: 'newAccountProgramName', type: 'string' },
]);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,12 @@ export default class SolanaComposeTransaction extends AbstractMethod<
recipientTokenAccounts === undefined &&
// if the recipient account has no owner, it means it's a new account and needs the token account to be created
(recipientAccountOwner === SYSTEM_PROGRAM_PUBLIC_KEY || recipientAccountOwner == null);
const newTokenAccountProgramName = isCreatingAccount
? this.params.token?.program
: undefined;
const newAccountProgramName = isCreatingAccount ? this.params.token?.program : undefined;

return {
serializedTx: tx.serialize(),
additionalInfo: {
isCreatingAccount: !!isCreatingAccount,
newTokenAccountProgramName,
newAccountProgramName,
tokenAccountInfo: tokenTransferTxAndDestinationAddress?.tokenAccountInfo,
},
};
Expand Down
3 changes: 1 addition & 2 deletions packages/connect/src/types/api/solana/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ export type SolanaComposedTransaction = Static<typeof SolanaComposedTransaction>
export const SolanaComposedTransaction = Type.Object({
serializedTx: Type.String(),
additionalInfo: Type.Object({
isCreatingAccount: Type.Boolean(),
newTokenAccountProgramName: Type.Optional(SolanaProgramName),
newAccountProgramName: Type.Optional(SolanaProgramName),
tokenAccountInfo: Type.Optional(SolanaTxTokenAccountInfo),
}),
});
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,7 @@ async function estimateFee(
request: {
specific: {
data: txData.tx.txShim.serialize(),
isCreatingAccount: false,
newTokenAccountProgramName: undefined,
newAccountProgramName: 'staking',
},
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ const getLines = (
const isEthereum = networkType === 'ethereum';
const isSolana = networkType === 'solana';
const showAmountWithoutFee = isEthereum || isSolana;
const feeLabel = ((network: NetworkType) => {
const feeLabelId = ((network: NetworkType) => {
switch (network) {
case 'ethereum':
return 'MAX_FEE';
case 'solana':
return 'TR_TX_FEE';
return 'TR_TX_FEE_INCLUDING_RENT';
default:
return 'TR_INCLUDING_FEE';
}
Expand Down Expand Up @@ -76,7 +76,7 @@ const getLines = (
},
{
id: 'fee',
label: <Translation id={feeLabel} />,
label: <Translation id={feeLabelId} />,
value: precomposedTx.fee,
type: 'amount',
},
Expand Down
38 changes: 25 additions & 13 deletions packages/suite/src/components/wallet/Fees/Fees.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useMemo } from 'react';
import {
Control,
FieldErrors,
Expand Down Expand Up @@ -155,6 +156,28 @@ export const Fees = <TFieldValues extends FormState>({

const supportsCustomFee = networkType !== 'solana';

const feeLabelId = useMemo(() => {
switch (networkType) {
case 'ethereum':
return 'MAX_FEE';
case 'solana':
return 'TR_TX_FEE_INCLUDING_RENT';
default:
return 'FEE';
}
}, [networkType]);

const feeTooltipTextId = useMemo(() => {
switch (networkType) {
case 'ethereum':
return 'TR_EVM_MAX_FEE_DESC';
case 'solana':
return 'TR_SOL_FEE_DESC';
default:
return 'TR_TRANSACTION_FEE_DESC';
}
}, [networkType]);

return (
<Column gap={spacings.md}>
<Row flexWrap="wrap">
Expand All @@ -168,21 +191,10 @@ export const Fees = <TFieldValues extends FormState>({
<Tooltip
dashed
maxWidth={328}
content={
networkType === 'ethereum' ? (
<Translation id="TR_EVM_MAX_FEE_DESC" />
) : (
<Translation id="TR_TRANSACTION_FEE_DESC" />
)
}
content={<Translation id={feeTooltipTextId} />}
>
<Text variant="default">
<Translation
id={
label ??
(networkType === 'ethereum' ? 'MAX_FEE' : 'FEE')
}
/>
<Translation id={label ?? feeLabelId} />
</Text>
</Tooltip>
</Row>
Expand Down
3 changes: 1 addition & 2 deletions packages/suite/src/hooks/wallet/__fixtures__/useSendForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1275,8 +1275,7 @@ export const setMax = [
payload: {
serializedTx: 'serializedABCD',
additionalInfo: {
isCreatingAccount: false,
newTokenAccountProgramName: undefined,
newAccountProgramName: undefined,
},
},
},
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 @@ -3254,6 +3254,10 @@ export default defineMessages({
defaultMessage: 'Fee',
id: 'TR_TX_FEE',
},
TR_TX_FEE_INCLUDING_RENT: {
defaultMessage: 'Fee (incl. rent)',
id: 'TR_TX_FEE_INCLUDING_RENT',
},
TR_UNCONFIRMED_TX: {
defaultMessage: 'Unconfirmed',
id: 'TR_UNCONFIRMED_TX',
Expand Down Expand Up @@ -5533,6 +5537,10 @@ export default defineMessages({
id: 'INCLUDING_FEE',
defaultMessage: 'Incl. fee',
},
INCLUDING_FEE_AND_RENT: {
id: 'INCLUDING_FEE_AND_RENT',
defaultMessage: 'Incl. fee and rent',
},
SEND_TRANSACTION: {
id: 'SEND_TRANSACTION',
description: 'Sign and send button used in Review modal',
Expand Down Expand Up @@ -8896,6 +8904,10 @@ export default defineMessages({
defaultMessage:
"The maximum fee you're willing to pay network validators to process your transaction. A higher fee may speed up confirmation times, though you'll only pay what's needed if the actual fee is lower.",
},
TR_SOL_FEE_DESC: {
id: 'TR_SOL_FEE_DESC',
defaultMessage: 'XXXXXX TODO',
},
TR_TRANSACTION_FEE_DESC: {
id: 'TR_TRANSACTION_FEE_DESC',
defaultMessage:
Expand Down
20 changes: 16 additions & 4 deletions packages/suite/src/views/wallet/send/TotalSent/TotalSent.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useMemo } from 'react';

import styled from 'styled-components';

import { formatAmount, formatNetworkAmount } from '@suite-common/wallet-utils';
Expand Down Expand Up @@ -26,6 +28,19 @@ export const TotalSent = () => {
const isTokenTransfer = networkType === 'ethereum' && !!getValues('outputs.0.token');
const hasTransactionInfo = transactionInfo !== undefined && transactionInfo.type !== 'error';
const tokenInfo = hasTransactionInfo ? transactionInfo.token : undefined;
const includingRent = networkType === 'solana';

const feeLabelId = useMemo(() => {
if (isTokenTransfer) {
return 'FEE';
}

if (includingRent) {
return 'INCLUDING_FEE_AND_RENT';
}

return 'INCLUDING_FEE';
}, [isTokenTransfer, includingRent]);

return (
<Container>
Expand Down Expand Up @@ -54,10 +69,7 @@ export const TotalSent = () => {
)}
</InfoItem>

<InfoItem
label={<Translation id={isTokenTransfer ? 'FEE' : 'INCLUDING_FEE'} />}
direction="row"
>
<InfoItem label={<Translation id={feeLabelId} />} direction="row">
{hasTransactionInfo &&
(tokenInfo ? (
<FormattedCryptoAmount
Expand Down
4 changes: 1 addition & 3 deletions suite-common/wallet-core/src/send/sendFormSolanaThunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,7 @@ export const composeSolanaTransactionFeeLevelsThunk = createThunk<
request: {
specific: {
data: transaction.payload.serializedTx,
isCreatingAccount: transaction.payload.additionalInfo.isCreatingAccount,
newTokenAccountProgramName:
transaction.payload.additionalInfo.newTokenAccountProgramName,
newAccountProgramName: transaction.payload.additionalInfo.newAccountProgramName,
},
},
});
Expand Down
Loading