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

[ENG-3617] Update the rbf logic #354

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e609d51
[ENG-3617] Update the rbf logic
dhriaznov Jan 19, 2024
04a5eed
Add getLatestNonce and getRawTransaction methods for stacks txs
dhriaznov Jan 19, 2024
12e472b
Merge branch 'develop' into denys/eng-3617-speed-up-stacks-transactio…
dhriaznov Jan 23, 2024
861d531
Merge branch 'develop' into denys/eng-3617-speed-up-stacks-transactio…
dhriaznov Jan 24, 2024
574470e
Add the useRbfTransactionData hook
dhriaznov Jan 24, 2024
807945d
Export the useRbfTransactionData hook
dhriaznov Jan 24, 2024
3c82079
Update the useRbfTransactionData hook params
dhriaznov Jan 24, 2024
c203b32
Update the useRbfTransactionData hook params
dhriaznov Jan 24, 2024
920d935
Update the useRbfTransactionData hook logic
dhriaznov Jan 24, 2024
4ae3e3a
Merge branch 'develop' into denys/eng-3617-speed-up-stacks-transactio…
dhriaznov Jan 30, 2024
60e6d1c
Move some logic to the separate functions, add unit tests
dhriaznov Jan 31, 2024
3c807fd
Merge branch 'denys/eng-3617-speed-up-stacks-transactions-on-mobile' …
dhriaznov Jan 31, 2024
85f497d
Merge branch 'develop' into denys/eng-3617-speed-up-stacks-transactio…
dhriaznov Jan 31, 2024
7f02475
Fix the imports
dhriaznov Jan 31, 2024
30f5f88
Merge branch 'denys/eng-3617-speed-up-stacks-transactions-on-mobile' …
dhriaznov Jan 31, 2024
43fba6b
Move the rbf tests to the tests folder
dhriaznov Feb 1, 2024
7f9d3c8
Update the rbf logic syntax
dhriaznov Feb 1, 2024
2df8ba7
Remove the unnecessary prop
dhriaznov Feb 1, 2024
0a1dd77
Update the stx rbf unit tests
dhriaznov Feb 1, 2024
9354e55
Add react-query package, add separate react-query functions for the r…
dhriaznov Feb 1, 2024
b544424
Export the new hooks
dhriaznov Feb 1, 2024
945e9fa
Remove the useRbfTransactionData hook in favor of the separate react-…
dhriaznov Feb 1, 2024
fb5e1cf
Merge branch 'develop' into denys/eng-3617-speed-up-stacks-transactio…
dhriaznov Feb 5, 2024
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
1 change: 1 addition & 0 deletions hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './brc20';
export * from './inscriptions';
export * from './transactions';
120 changes: 120 additions & 0 deletions hooks/transactions/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { deserializeTransaction, estimateTransaction } from '@stacks/transactions';
import BigNumber from 'bignumber.js';
import { RbfRecommendedFees, getRawTransaction, rbf } from '../../transactions';
import {
AppInfo,
RecommendedFeeResponse,
SettingsNetwork,
BtcTransactionData,
StacksNetwork,
StacksTransaction,
StxTransactionData,
} from '../../types';
import { microstacksToStx } from '../../currency';

export type RbfData = {
rbfTransaction?: InstanceType<typeof rbf.RbfTransaction>;
rbfTxSummary?: {
currentFee: number;
currentFeeRate: number;
minimumRbfFee: number;
minimumRbfFeeRate: number;
};
rbfRecommendedFees?: RbfRecommendedFees;
mempoolFees?: RecommendedFeeResponse;
isLoading?: boolean;
errorCode?: 'SOMETHING_WENT_WRONG';
};

export const isBtcTransaction = (
transaction: BtcTransactionData | StxTransactionData,
): transaction is BtcTransactionData => transaction?.txType === 'bitcoin';

export const constructRecommendedFees = (
lowerName: keyof RbfRecommendedFees,
lowerFeeRate: number,
higherName: keyof RbfRecommendedFees,
higherFeeRate: number,
stxAvailableBalance: string,
): RbfRecommendedFees => {
const bigNumLowerFee = BigNumber(lowerFeeRate);
const bigNumHigherFee = BigNumber(higherFeeRate);

return {
[lowerName]: {
enoughFunds: bigNumLowerFee.lte(BigNumber(stxAvailableBalance)),
feeRate: microstacksToStx(bigNumLowerFee).toNumber(),
fee: microstacksToStx(bigNumLowerFee).toNumber(),
},
[higherName]: {
enoughFunds: bigNumHigherFee.lte(BigNumber(stxAvailableBalance)),
feeRate: microstacksToStx(bigNumHigherFee).toNumber(),
fee: microstacksToStx(bigNumHigherFee).toNumber(),
},
};
};

export const sortFees = (fees: RbfRecommendedFees) =>
Object.fromEntries(
Object.entries(fees).sort((a, b) => {
const priorityOrder = ['highest', 'higher', 'high', 'medium'];
return priorityOrder.indexOf(a[0]) - priorityOrder.indexOf(b[0]);
}),
);

export const calculateStxRbfData = async (
fee: BigNumber,
feeEstimations: {
fee: number;
fee_rate: number;
}[],
appInfo: AppInfo | null,
stxAvailableBalance: string,
): Promise<RbfData> => {
const [slow, medium, high] = feeEstimations;
const shouldCapFee = appInfo?.thresholdHighStacksFee && high.fee > appInfo.thresholdHighStacksFee;

const mediumFee = shouldCapFee ? appInfo.thresholdHighStacksFee : medium.fee;
const highFee = shouldCapFee ? appInfo.thresholdHighStacksFee * 1.5 : high.fee;
const higherFee = fee.multipliedBy(1.25).toNumber();
const highestFee = fee.multipliedBy(1.5).toNumber();

const defaultMinimumFee = fee.multipliedBy(1.25).toNumber();
const minimumFee = !Number.isSafeInteger(defaultMinimumFee) ? Math.ceil(defaultMinimumFee) : defaultMinimumFee;

const feePresets: RbfRecommendedFees = fee.lt(BigNumber(mediumFee))
? constructRecommendedFees('medium', mediumFee, 'high', highFee, stxAvailableBalance)
: constructRecommendedFees('higher', higherFee, 'highest', highestFee, stxAvailableBalance);

return {
rbfTxSummary: {
currentFee: microstacksToStx(fee).toNumber(),
currentFeeRate: microstacksToStx(fee).toNumber(),
minimumRbfFee: microstacksToStx(BigNumber(minimumFee)).toNumber(),
minimumRbfFeeRate: microstacksToStx(BigNumber(minimumFee)).toNumber(),
},
rbfRecommendedFees: sortFees(feePresets),
mempoolFees: {
fastestFee: microstacksToStx(BigNumber(high.fee)).toNumber(),
halfHourFee: microstacksToStx(BigNumber(medium.fee)).toNumber(),
hourFee: microstacksToStx(BigNumber(slow.fee)).toNumber(),
economyFee: microstacksToStx(BigNumber(slow.fee)).toNumber(),
minimumFee: microstacksToStx(BigNumber(slow.fee)).toNumber(),
},
};
};

export const fetchStxRbfData = async (
transaction: StxTransactionData,
btcNetwork: SettingsNetwork,
stacksNetwork: StacksNetwork,
appInfo: AppInfo | null,
stxAvailableBalance: string,
): Promise<RbfData> => {
const { fee } = transaction;
const txRaw: string = await getRawTransaction(transaction.txid, btcNetwork);
const unsignedTx: StacksTransaction = deserializeTransaction(txRaw);
const feeEstimations = await estimateTransaction(unsignedTx.payload, undefined, stacksNetwork);

return calculateStxRbfData(fee, feeEstimations, appInfo, stxAvailableBalance);
};
2 changes: 2 additions & 0 deletions hooks/transactions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as useBtcRbfTransactionData } from './useBtcRbfTransactionData';
export { default as useStxRbfTransactionData } from './useStxRbfTransactionData';
48 changes: 48 additions & 0 deletions hooks/transactions/useBtcRbfTransactionData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useQuery } from '@tanstack/react-query';
import { rbf } from '../../transactions';
import { Account, BtcTransactionData, SettingsNetwork } from '../../types';
import { BitcoinEsploraApiProvider, mempoolApi } from '../../api';
import { RbfData, sortFees } from './helpers';

type Props = {
account: Account | null;
transaction?: BtcTransactionData;
btcNetwork: SettingsNetwork;
esploraProvider: BitcoinEsploraApiProvider;
isLedgerAccount: boolean;
};

const useBtcRbfTransactionData = ({ account, transaction, btcNetwork, esploraProvider, isLedgerAccount }: Props) => {
const fetchRbfData = async (): Promise<RbfData | undefined> => {
if (!account || !transaction) {
return;
}

const rbfTx = new rbf.RbfTransaction(transaction, {
...account,
accountType: account.accountType || 'software',
accountId: isLedgerAccount && account.deviceAccountIndex ? account.deviceAccountIndex : account.id,
network: btcNetwork.type,
esploraProvider,
});

const mempoolFees = await mempoolApi.getRecommendedFees(btcNetwork.type);
const rbfRecommendedFees = await rbfTx.getRbfRecommendedFees(mempoolFees);
const rbfTransactionSummary = await rbf.getRbfTransactionSummary(esploraProvider, transaction.txid);

return {
rbfTransaction: rbfTx,
rbfTxSummary: rbfTransactionSummary,
rbfRecommendedFees: sortFees(rbfRecommendedFees),
mempoolFees,
};
};

return useQuery({
fedeerbes marked this conversation as resolved.
Show resolved Hide resolved
queryKey: ['btc-rbf-transaction-data', transaction?.txid],
queryFn: fetchRbfData,
enabled: !!transaction && !!account,
});
};

export default useBtcRbfTransactionData;
30 changes: 30 additions & 0 deletions hooks/transactions/useStxRbfTransactionData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useQuery } from '@tanstack/react-query';
import { StxTransactionData, SettingsNetwork, StacksNetwork, AppInfo } from '../../types';
import { fetchStxRbfData } from './helpers';

type Props = {
transaction?: StxTransactionData;
btcNetwork: SettingsNetwork;
stacksNetwork: StacksNetwork;
appInfo: AppInfo | null;
stxAvailableBalance: string;
};

const useStxRbfTransactionData = ({ transaction, btcNetwork, stacksNetwork, appInfo, stxAvailableBalance }: Props) => {
const fetchRbfData = async () => {
if (!transaction) {
return;
}

const stxRbfData = await fetchStxRbfData(transaction, btcNetwork, stacksNetwork, appInfo, stxAvailableBalance);
return stxRbfData;
};

return useQuery({
queryKey: ['stx-rbf-transaction-data', transaction?.txid],
queryFn: fetchRbfData,
enabled: !!transaction,
});
};
teebszet marked this conversation as resolved.
Show resolved Hide resolved

export default useStxRbfTransactionData;
44 changes: 44 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@stacks/storage": "^6.9.0",
"@stacks/transactions": "6.9.0",
"@stacks/wallet-sdk": "^6.9.0",
"@tanstack/react-query": "^4.29.3",
"@zondax/ledger-stacks": "^1.0.4",
"async-mutex": "^0.4.0",
"axios": "1.6.2",
Expand Down
Loading
Loading