Skip to content

Commit

Permalink
feat(suite): add support for Solana priority fees
Browse files Browse the repository at this point in the history
(cherry picked from commit 2ab5a6e)
  • Loading branch information
gabrielKerekes authored and komret committed Mar 27, 2024
1 parent 5965324 commit a0fd548
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 23 deletions.
38 changes: 31 additions & 7 deletions packages/suite/src/actions/wallet/send/sendFormSolanaActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
buildTransferTransaction,
buildTokenTransferTransaction,
getAssociatedTokenAccountAddress,
dummyPriorityFeesForFeeEstimation,
} from 'src/utils/wallet/solanaUtils';
import { SYSTEM_PROGRAM_PUBLIC_KEY } from '@trezor/blockchain-link-utils/lib/solana';

Expand All @@ -33,7 +34,9 @@ const calculate = (
feeLevel: FeeLevel,
token?: TokenInfo,
): PrecomposedTransaction => {
const feeInLamports = feeLevel.feePerUnit;
const feeInLamports = feeLevel.feePerTx;
if (feeInLamports == null) throw new Error('Invalid fee.');

let amount: string;
let max: string | undefined;
const availableTokenBalance = token
Expand Down Expand Up @@ -61,7 +64,8 @@ const calculate = (
totalSpent: token ? amount : totalSpent.toString(),
max,
fee: feeInLamports,
feePerByte: feeInLamports,
feePerByte: feeLevel.feePerUnit,
feeLimit: feeLevel.feeLimit,
token,
bytes: 0,
inputs: [],
Expand Down Expand Up @@ -124,8 +128,6 @@ export const composeTransaction =

const { output, decimals, tokenInfo } = composeOutputs;

let fetchedFee: string | undefined;

const { blockhash, blockHeight: lastValidBlockHeight } = selectBlockchainBlockInfoBySymbol(
getState(),
account.symbol,
Expand Down Expand Up @@ -155,6 +157,7 @@ export const composeTransaction =
recipientTokenAccount,
blockhash,
lastValidBlockHeight,
dummyPriorityFeesForFeeEstimation,
)
: undefined;

Expand All @@ -171,6 +174,7 @@ export const composeTransaction =
formValues.outputs[0].amount || '0',
blockhash,
lastValidBlockHeight,
dummyPriorityFeesForFeeEstimation,
)
).compileMessage();

Expand All @@ -190,9 +194,15 @@ export const composeTransaction =
},
});

let fetchedFee: string | undefined;
let fetchedFeePerUnit: string | undefined;
let fetchedFeeLimit: string | undefined;
if (estimatedFee.success) {
// We access the array directly like this because the fee response from the solana worker always returns an array of size 1
fetchedFee = estimatedFee.payload.levels[0].feePerUnit;
const feeLevel = estimatedFee.payload.levels[0];
fetchedFee = feeLevel.feePerTx;
fetchedFeePerUnit = feeLevel.feePerUnit;
fetchedFeeLimit = feeLevel.feeLimit;
} else {
// Error fetching fee, fall back on default values defined in `/packages/connect/src/data/defaultFeeLevels.ts`
}
Expand All @@ -202,7 +212,12 @@ export const composeTransaction =
// update predefined levels with fee fetched from network
const predefinedLevels = levels
.filter(l => l.label !== 'custom')
.map(l => ({ ...l, feePerUnit: fetchedFee || l.feePerUnit }));
.map(l => ({
...l,
feePerTx: fetchedFee || l.feePerTx,
feePerUnit: fetchedFeePerUnit || l.feePerUnit,
feeLimit: fetchedFeeLimit || l.feeLimit,
}));

const wrappedResponse: PrecomposedLevels = {};
const response = predefinedLevels.map(level =>
Expand Down Expand Up @@ -241,7 +256,8 @@ export const signTransaction =
selectedAccount.status !== 'loaded' ||
!device ||
!transactionInfo ||
transactionInfo.type !== 'final'
transactionInfo.type !== 'final' ||
transactionInfo.feeLimit == null
)
return;

Expand Down Expand Up @@ -278,6 +294,10 @@ export const signTransaction =
recipientTokenAccounts,
blockhash,
lastValidBlockHeight,
{
computeUnitPrice: transactionInfo.feePerByte,
computeUnitLimit: transactionInfo.feeLimit,
},
)
: undefined;

Expand All @@ -291,6 +311,10 @@ export const signTransaction =
formValues.outputs[0].amount,
blockhash,
lastValidBlockHeight,
{
computeUnitPrice: transactionInfo.feePerByte,
computeUnitLimit: transactionInfo.feeLimit,
},
);

const serializedTx = tx.serializeMessage().toString('hex');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import styled, { useTheme } from 'styled-components';
import BigNumber from 'bignumber.js';
import { getFeeUnits, formatNetworkAmount, formatAmount } from '@suite-common/wallet-utils';
import { getFeeUnits, formatNetworkAmount, formatAmount, getFee } from '@suite-common/wallet-utils';
import { Icon, CoinLogo, variables } from '@trezor/components';
import { formatDuration, isFeatureFlagEnabled } from '@suite-common/suite-utils';
import { borders, spacingsPx, typography } from '@trezor/theme';
Expand Down Expand Up @@ -220,7 +220,7 @@ export const TransactionReviewSummary = ({
const theme = useTheme();

const { symbol, accountType, index } = account;
const { feePerByte } = tx;
const fee = getFee(network.networkType, tx);

const spentWithoutFee = !tx.token ? new BigNumber(tx.totalSpent).minus(tx.fee).toString() : '';
const amount = !tx.token
Expand All @@ -229,7 +229,7 @@ export const TransactionReviewSummary = ({

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

return (
<Wrapper>
Expand Down Expand Up @@ -278,7 +278,7 @@ export const TransactionReviewSummary = ({
</ReviewRbfLeftDetailsLineRight>
</LeftDetailsRow>
)}
{!!tx.feeLimit && (
{!!tx.feeLimit && network.networkType !== 'solana' && (
<LeftDetailsRow>
<ReviewRbfLeftDetailsLineLeft>
<Icon size={12} color={theme.iconSubdued} icon="GAS" />
Expand All @@ -300,7 +300,7 @@ export const TransactionReviewSummary = ({
</ReviewRbfLeftDetailsLineLeft>

<ReviewRbfLeftDetailsLineRight color={theme.textSubdued}>
{feePerByte} {getFeeUnits(network.networkType)}
{fee} {getFeeUnits(network.networkType)}
</ReviewRbfLeftDetailsLineRight>
</LeftDetailsRow>

Expand Down
22 changes: 15 additions & 7 deletions packages/suite/src/hooks/wallet/__fixtures__/useSendForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,19 @@ const DEFAULT_FEES = {
levels: [{ label: 'normal', feePerUnit: '12', blocks: -1 }],
},
sol: {
minFee: 5000,
maxFee: 5000,
blockHeight: 1,
blockTime: 1,
levels: [{ label: 'normal', feePerUnit: '5000', blocks: -1 }],
minFee: -1,
maxFee: -1,
blockHeight: -1,
blockTime: -1,
levels: [
{
label: 'normal',
feePerUnit: '100000',
feeLimit: '50000',
feePerTx: '10000',
blocks: -1,
},
],
},
};

Expand Down Expand Up @@ -1204,13 +1212,13 @@ export const setMax = [
composedLevels: {
normal: {
type: 'final',
fee: '5000',
fee: '10000',
totalSpent: '10000000000',
},
custom: undefined,
},
formValues: {
outputs: [{ amount: '9.999995' }],
outputs: [{ amount: '9.99999' }],
},
},
},
Expand Down
18 changes: 15 additions & 3 deletions packages/suite/src/utils/wallet/__fixtures__/solanaUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,13 @@ export const fixtures = {
toTokenAccount: undefined,
blockhash: '7xpT7BDE7q1ZWhe6Pg8PHRYbqgDwNK3L2v97rEfsjMkn',
lastValidBlockHeight: 50,
priorityFees: {
computeUnitPrice: '100000',
computeUnitLimit: '50000',
},
},
expectedOutput:
'01000508c80f8b50107e9f3e3c16a661b8c806df454a6deb293d5e8730a9d28f2f4998c6a99c9c4d0c7def9dd60a3a40dc5266faf41996310aa62ad6cbd9b64e1e2cca78ebaa24826cef9644c1ecf0dfcf955775b8438528e97820efc2b20ed46be1dc580000000000000000000000000000000000000000000000000000000000000000527706a12f3f7c3c852582f0f79b515c03c6ffbe6e3100044ba7c982eb5cf9f28c97258f4e2489f1bb3d1029148e0d830b5a1399daff1084048e7bd8dbe9f859d27c181cb023db6239e22e49e4b67f7dd9ed13f3d7ed319f9e91b3bc64cec0a906ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a96772b7d36a2e66e52c817f385d7e94d3d4b6d47d7171c9f2dd86c6f1be1a93eb020506000206040307000704010402000a0c00e1f5050000000009',
'01000609c80f8b50107e9f3e3c16a661b8c806df454a6deb293d5e8730a9d28f2f4998c6a99c9c4d0c7def9dd60a3a40dc5266faf41996310aa62ad6cbd9b64e1e2cca78ebaa24826cef9644c1ecf0dfcf955775b8438528e97820efc2b20ed46be1dc580000000000000000000000000000000000000000000000000000000000000000527706a12f3f7c3c852582f0f79b515c03c6ffbe6e3100044ba7c982eb5cf9f28c97258f4e2489f1bb3d1029148e0d830b5a1399daff1084048e7bd8dbe9f8590306466fe5211732ffecadba72c39be7bc8ce5bbc5f7126b2c439b3a40000000d27c181cb023db6239e22e49e4b67f7dd9ed13f3d7ed319f9e91b3bc64cec0a906ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a96772b7d36a2e66e52c817f385d7e94d3d4b6d47d7171c9f2dd86c6f1be1a93eb040600050250c3000006000903a0860100000000000506000207040308000804010402000a0c00e1f5050000000009',
},
{
description:
Expand All @@ -218,9 +222,13 @@ export const fixtures = {
},
blockhash: '7xpT7BDE7q1ZWhe6Pg8PHRYbqgDwNK3L2v97rEfsjMkn',
lastValidBlockHeight: 50,
priorityFees: {
computeUnitPrice: '100000',
computeUnitLimit: '50000',
},
},
expectedOutput:
'01000205c80f8b50107e9f3e3c16a661b8c806df454a6deb293d5e8730a9d28f2f4998c6a99c9c4d0c7def9dd60a3a40dc5266faf41996310aa62ad6cbd9b64e1e2cca78ebaa24826cef9644c1ecf0dfcf955775b8438528e97820efc2b20ed46be1dc58527706a12f3f7c3c852582f0f79b515c03c6ffbe6e3100044ba7c982eb5cf9f206ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a96772b7d36a2e66e52c817f385d7e94d3d4b6d47d7171c9f2dd86c6f1be1a93eb010404010302000a0c00e1f5050000000009',
'01000306c80f8b50107e9f3e3c16a661b8c806df454a6deb293d5e8730a9d28f2f4998c6a99c9c4d0c7def9dd60a3a40dc5266faf41996310aa62ad6cbd9b64e1e2cca78ebaa24826cef9644c1ecf0dfcf955775b8438528e97820efc2b20ed46be1dc58527706a12f3f7c3c852582f0f79b515c03c6ffbe6e3100044ba7c982eb5cf9f20306466fe5211732ffecadba72c39be7bc8ce5bbc5f7126b2c439b3a4000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a96772b7d36a2e66e52c817f385d7e94d3d4b6d47d7171c9f2dd86c6f1be1a93eb030400050250c3000004000903a0860100000000000504010302000a0c00e1f5050000000009',
},
{
description:
Expand All @@ -241,9 +249,13 @@ export const fixtures = {
toTokenAccount: undefined,
blockhash: '7xpT7BDE7q1ZWhe6Pg8PHRYbqgDwNK3L2v97rEfsjMkn',
lastValidBlockHeight: 50,
priorityFees: {
computeUnitPrice: '100000',
computeUnitLimit: '50000',
},
},
expectedOutput:
'01000205c80f8b50107e9f3e3c16a661b8c806df454a6deb293d5e8730a9d28f2f4998c6a99c9c4d0c7def9dd60a3a40dc5266faf41996310aa62ad6cbd9b64e1e2cca78ebaa24826cef9644c1ecf0dfcf955775b8438528e97820efc2b20ed46be1dc58527706a12f3f7c3c852582f0f79b515c03c6ffbe6e3100044ba7c982eb5cf9f206ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a96772b7d36a2e66e52c817f385d7e94d3d4b6d47d7171c9f2dd86c6f1be1a93eb010404010302000a0c00e1f5050000000009',
'01000306c80f8b50107e9f3e3c16a661b8c806df454a6deb293d5e8730a9d28f2f4998c6a99c9c4d0c7def9dd60a3a40dc5266faf41996310aa62ad6cbd9b64e1e2cca78ebaa24826cef9644c1ecf0dfcf955775b8438528e97820efc2b20ed46be1dc58527706a12f3f7c3c852582f0f79b515c03c6ffbe6e3100044ba7c982eb5cf9f20306466fe5211732ffecadba72c39be7bc8ce5bbc5f7126b2c439b3a4000000006ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a96772b7d36a2e66e52c817f385d7e94d3d4b6d47d7171c9f2dd86c6f1be1a93eb030400050250c3000004000903a0860100000000000504010302000a0c00e1f5050000000009',
},
],
};
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ describe('solana utils', () => {
input.toTokenAccount,
input.blockhash,
input.lastValidBlockHeight,
input.priorityFees,
);
const message = tx.transaction.compileMessage().serialize().toString('hex');

Expand Down
29 changes: 28 additions & 1 deletion packages/suite/src/utils/wallet/solanaUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,43 @@ const encodeTokenTransferInstructionData = (instruction: {
return b.subarray(0, span);
};

type PriorityFees = { computeUnitPrice: string; computeUnitLimit: string };

export const dummyPriorityFeesForFeeEstimation: PriorityFees = {
computeUnitPrice: '100000',
computeUnitLimit: '200000',
};

const addPriorityFees = async (transaction: Transaction, priorityFees: PriorityFees) => {
const { ComputeBudgetProgram } = await loadSolanaLib();
transaction.add(
ComputeBudgetProgram.setComputeUnitLimit({
units: parseInt(priorityFees.computeUnitLimit, 10),
}),
ComputeBudgetProgram.setComputeUnitPrice({
microLamports: parseInt(priorityFees.computeUnitPrice, 10),
}),
);
};

export const buildTransferTransaction = async (
fromAddress: string,
toAddress: string,
amountInSol: string,
blockhash: string,
lastValidBlockHeight: number,
priorityFees: PriorityFees,
) => {
const { Transaction, SystemProgram, PublicKey } = await loadSolanaLib();
const transaction = new Transaction({
blockhash,
lastValidBlockHeight,
feePayer: new PublicKey(fromAddress),
}).add(
});

await addPriorityFees(transaction, priorityFees);

transaction.add(
SystemProgram.transfer({
fromPubkey: new PublicKey(fromAddress),
toPubkey: new PublicKey(toAddress),
Expand Down Expand Up @@ -210,6 +234,7 @@ export const buildTokenTransferTransaction = async (
toTokenAccount: TokenAccount | undefined,
blockhash: string,
lastValidBlockHeight: number,
priorityFees: PriorityFees,
): Promise<TokenTransferTxWithDestinationAddress> => {
const { Transaction, PublicKey } = await loadSolanaLib();

Expand All @@ -219,6 +244,8 @@ export const buildTokenTransferTransaction = async (
feePayer: new PublicKey(fromAddress),
});

await addPriorityFees(transaction, priorityFees);

// Token transaction building logic

const tokenAmount = new BigNumber(tokenUiAmount).times(10 ** tokenDecimals);
Expand Down
11 changes: 11 additions & 0 deletions suite-common/wallet-utils/src/sendFormUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import type {
Account,
CurrencyOption,
ExcludedUtxos,
PrecomposedTransactionFinal,
TxFinalCardano,
} from '@suite-common/wallet-types';

import { amountToSatoshi, getUtxoOutpoint, networkAmountToSatoshi } from './accountUtils';
Expand Down Expand Up @@ -201,6 +203,15 @@ export const getFeeUnits = (networkType: NetworkType) => {
return 'sat/B';
};

export const getFee = (
networkType: NetworkType,
tx: PrecomposedTransactionFinal | TxFinalCardano,
) => {
if (networkType === 'solana') return tx.fee;

return tx.feePerByte;
};

// Find all validation errors set while composing a transaction
export const findComposeErrors = <T extends FieldValues>(
errors: FieldErrors<T>,
Expand Down

0 comments on commit a0fd548

Please sign in to comment.