mirror of
https://github.com/zhigang1992/wallet.git
synced 2026-04-28 20:55:30 +08:00
Merge pull request #3626 from hirosystems/release/monday-release
Release/monday release
This commit is contained in:
49
.github/workflows/ordinals-checker.yml
vendored
Normal file
49
.github/workflows/ordinals-checker.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
name: Ordinals API test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
schedule:
|
||||
# https://crontab.guru
|
||||
- cron: '0 * * * *'
|
||||
env:
|
||||
CI: true
|
||||
|
||||
jobs:
|
||||
test-ordinals:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Get installed Playwright version
|
||||
id: playwright-version
|
||||
run: echo "PLAYWRIGHT_VERSION=$(node -e "console.log(require('./package.json').devDependencies['@playwright/test'])")" >> $GITHUB_ENV
|
||||
|
||||
- name: Cache playwright binaries
|
||||
uses: actions/cache@v3
|
||||
id: playwright-cache
|
||||
with:
|
||||
path: ~/.cache/ms-playwright
|
||||
key: ${{ runner.os }}-playwright-${{ env.PLAYWRIGHT_VERSION }}
|
||||
|
||||
- name: Install Playwright deps
|
||||
run: yarn playwright install chrome
|
||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
||||
|
||||
- name: Run Playwright tests
|
||||
run: yarn playwright test tests/specs/ordinals/ordinals.spec.ts
|
||||
|
||||
- name: Discord notification
|
||||
if: failure()
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_ALERT_CHANNEL }}
|
||||
uses: Ilshidur/action-discord@master
|
||||
with:
|
||||
args: "Something funky's up with the OrdAPI.xyz. Wallet team engineer to investigate. See `ordinals-checker.yml Github Action."
|
||||
@@ -1,5 +1,5 @@
|
||||
import { fibonacciGenerator } from '../math/fibonacci';
|
||||
import { createCounter } from '../utils/counter';
|
||||
import { fibonacciGenerator } from '../utils/fibonacci';
|
||||
|
||||
const numOfEmptyAccountsToCheck = 20;
|
||||
|
||||
|
||||
26
src/app/common/math/helpers.spec.ts
Normal file
26
src/app/common/math/helpers.spec.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { sumNumbers } from './helpers';
|
||||
|
||||
const cases = [
|
||||
{
|
||||
sums: [1, 2, 3],
|
||||
expectedResult: new BigNumber(6),
|
||||
},
|
||||
{
|
||||
sums: [0.1, 0.2],
|
||||
expectedResult: new BigNumber(0.3),
|
||||
},
|
||||
{
|
||||
sums: [Number.MAX_SAFE_INTEGER, 1],
|
||||
expectedResult: new BigNumber('9007199254740992'),
|
||||
},
|
||||
];
|
||||
|
||||
describe(sumNumbers.name, () => {
|
||||
describe.each(cases)('Sum example', ({ sums, expectedResult }) => {
|
||||
test('sum of ' + sums.toString(), () => {
|
||||
expect(sumNumbers(sums).toString()).toEqual(expectedResult.toString());
|
||||
});
|
||||
});
|
||||
});
|
||||
25
src/app/common/math/helpers.ts
Normal file
25
src/app/common/math/helpers.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
export function initBigNumber(num: string | number | BigNumber) {
|
||||
return BigNumber.isBigNumber(num) ? num : new BigNumber(num);
|
||||
}
|
||||
|
||||
export function sumNumbers(nums: number[]) {
|
||||
return nums.reduce((acc, num) => acc.plus(num), new BigNumber(0));
|
||||
}
|
||||
|
||||
function isMultipleOf(multiple: number) {
|
||||
return (num: number) => num % multiple === 0;
|
||||
}
|
||||
|
||||
export function isEven(num: number) {
|
||||
return isMultipleOf(2)(num);
|
||||
}
|
||||
|
||||
export function countDecimals(num: string | number | BigNumber) {
|
||||
const LARGE_NUMBER_OF_DECIMALS = 100;
|
||||
BigNumber.config({ DECIMAL_PLACES: LARGE_NUMBER_OF_DECIMALS });
|
||||
const amount = initBigNumber(num);
|
||||
const decimals = amount.toString(10).split('.')[1];
|
||||
return decimals ? decimals.length : 0;
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import BigNumber from 'bignumber.js';
|
||||
import { BTC_DECIMALS, STX_DECIMALS } from '@shared/constants';
|
||||
import { Money } from '@shared/models/money.model';
|
||||
|
||||
import { initBigNumber } from '../utils';
|
||||
import { initBigNumber } from '../math/helpers';
|
||||
|
||||
function fractionalUnitToUnit(decimals: number) {
|
||||
return (unit: number | string | BigNumber) => {
|
||||
|
||||
@@ -4,8 +4,9 @@ import { c32addressDecode } from 'c32check';
|
||||
|
||||
import { NetworkConfiguration, STX_DECIMALS } from '@shared/constants';
|
||||
|
||||
import { abbreviateNumber, initBigNumber } from '@app/common/utils';
|
||||
import { abbreviateNumber } from '@app/common/utils';
|
||||
|
||||
import { initBigNumber } from './math/helpers';
|
||||
import { microStxToStx } from './money/unit-conversion';
|
||||
|
||||
export const stacksValue = ({
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createNullArrayOfLength, sumNumbers } from '@app/common/utils';
|
||||
import { sumNumbers } from '@app/common/math/helpers';
|
||||
import { createNullArrayOfLength } from '@app/common/utils';
|
||||
|
||||
import { determineUtxosForSpend } from './local-coin-selection';
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { getTicker, sumNumbers } from '@app/common/utils';
|
||||
import { getTicker } from '@app/common/utils';
|
||||
import { extractPhraseFromString } from '@app/common/utils';
|
||||
|
||||
import { countDecimals } from './utils';
|
||||
import { countDecimals } from './math/helpers';
|
||||
|
||||
describe(countDecimals.name, () => {
|
||||
test('that it returns 0 when given an integer', () => expect(countDecimals(100)).toEqual(0));
|
||||
@@ -107,26 +105,3 @@ describe(extractPhraseFromString.name, () => {
|
||||
expect(key).toEqual(SECRET_KEY_COPIED_FROM_V3_FORMATTED);
|
||||
});
|
||||
});
|
||||
|
||||
const cases = [
|
||||
{
|
||||
sums: [1, 2, 3],
|
||||
expectedResult: new BigNumber(6),
|
||||
},
|
||||
{
|
||||
sums: [0.1, 0.2],
|
||||
expectedResult: new BigNumber(0.3),
|
||||
},
|
||||
{
|
||||
sums: [Number.MAX_SAFE_INTEGER, 1],
|
||||
expectedResult: new BigNumber('9007199254740992'),
|
||||
},
|
||||
];
|
||||
|
||||
describe(sumNumbers.name, () => {
|
||||
describe.each(cases)('Sum example', ({ sums, expectedResult }) => {
|
||||
test('sum of ' + sums.toString(), () => {
|
||||
expect(sumNumbers(sums).toString()).toEqual(expectedResult.toString());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
PostCondition,
|
||||
deserializePostCondition,
|
||||
} from '@stacks/transactions';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { toUnicode } from 'punycode';
|
||||
|
||||
import { BitcoinNetworkModes, KEBAB_REGEX, NetworkModes } from '@shared/constants';
|
||||
@@ -88,14 +87,6 @@ export function truncateString(str: string, maxLength: number) {
|
||||
return str.slice(0, maxLength) + '…';
|
||||
}
|
||||
|
||||
function isMultipleOf(multiple: number) {
|
||||
return (num: number) => num % multiple === 0;
|
||||
}
|
||||
|
||||
export function isEven(num: number) {
|
||||
return isMultipleOf(2)(num);
|
||||
}
|
||||
|
||||
function getLetters(string: string, offset = 1) {
|
||||
return string.slice(0, offset);
|
||||
}
|
||||
@@ -251,18 +242,6 @@ export function with0x(value: string): string {
|
||||
return !value.startsWith('0x') ? `0x${value}` : value;
|
||||
}
|
||||
|
||||
export function initBigNumber(num: string | number | BigNumber) {
|
||||
return BigNumber.isBigNumber(num) ? num : new BigNumber(num);
|
||||
}
|
||||
|
||||
export function countDecimals(num: string | number | BigNumber) {
|
||||
const LARGE_NUMBER_OF_DECIMALS = 100;
|
||||
BigNumber.config({ DECIMAL_PLACES: LARGE_NUMBER_OF_DECIMALS });
|
||||
const amount = initBigNumber(num);
|
||||
const decimals = amount.toString(10).split('.')[1];
|
||||
return decimals ? decimals.length : 0;
|
||||
}
|
||||
|
||||
export function pullContractIdFromIdentity(identifier: string) {
|
||||
return identifier.split('::')[0];
|
||||
}
|
||||
@@ -314,10 +293,6 @@ export function bitcoinNetworkModeToCoreNetworkMode(mode: BitcoinNetworkModes) {
|
||||
return bitcoinNetworkToCoreNetworkMap[mode];
|
||||
}
|
||||
|
||||
export function sumNumbers(nums: number[]) {
|
||||
return nums.reduce((acc, num) => acc.plus(num), new BigNumber(0));
|
||||
}
|
||||
|
||||
export function logAndThrow(msg: string, args: any[] = []) {
|
||||
logger.error(msg, ...args);
|
||||
throw new Error(msg);
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as yup from 'yup';
|
||||
import { Money } from '@shared/models/money.model';
|
||||
import { isNumber } from '@shared/utils';
|
||||
|
||||
import { countDecimals } from '@app/common/math/helpers';
|
||||
import { subtractMoney } from '@app/common/money/format-money';
|
||||
import {
|
||||
btcToSat,
|
||||
@@ -18,7 +19,6 @@ import {
|
||||
formatPrecisionError,
|
||||
} from '../../error-formatters';
|
||||
import { FormErrorMessages } from '../../error-messages';
|
||||
import { countDecimals } from '../../utils';
|
||||
import { currencyAmountValidator, stxAmountPrecisionValidator } from './currency-validators';
|
||||
|
||||
const minSpendAmountInSats = 6000;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { isNumber } from '@shared/utils';
|
||||
|
||||
import { formatErrorWithSymbol } from '@app/common/error-formatters';
|
||||
import { FormErrorMessages } from '@app/common/error-messages';
|
||||
import { countDecimals } from '@app/common/utils';
|
||||
import { countDecimals } from '@app/common/math/helpers';
|
||||
|
||||
export function currencyAmountValidator() {
|
||||
return yup
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { isEven } from '@app/common/utils';
|
||||
import { isEven } from '@app/common/math/helpers';
|
||||
|
||||
import { AddressDisplayerLayout } from './address-displayer.layout';
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Stack, Text } from '@stacks/ui';
|
||||
|
||||
import { LoadingSpinner } from '../loading-spinner';
|
||||
import { FeesCard } from './components/fees-card';
|
||||
import { useBitcoinFeesList } from './use-bitcoin-fees-list';
|
||||
|
||||
@@ -9,8 +10,19 @@ interface BitcoinFeesListProps {
|
||||
recipient: string;
|
||||
}
|
||||
export function BitcoinFeesList({ amount, onChooseFee, recipient }: BitcoinFeesListProps) {
|
||||
const { feesList } = useBitcoinFeesList({ amount, recipient });
|
||||
const { feesList, isLoading } = useBitcoinFeesList({ amount, recipient });
|
||||
|
||||
if (isLoading) return <LoadingSpinner />;
|
||||
|
||||
if (!feesList.length) {
|
||||
return (
|
||||
<Stack alignItems="center" spacing="extra-loose" width="100%">
|
||||
<Text color="#74777D" fontSize="14px" textAlign="center">
|
||||
Unable to fetch fees. Please try again later.
|
||||
</Text>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Stack alignItems="center" spacing="extra-loose" width="100%">
|
||||
<Stack spacing="base" width="100%">
|
||||
|
||||
@@ -8,7 +8,7 @@ import { btcToSat } from '@app/common/money/unit-conversion';
|
||||
import { determineUtxosForSpend } from '@app/common/transactions/bitcoin/coinselect/local-coin-selection';
|
||||
import { useGetUtxosByAddressQuery } from '@app/query/bitcoin/address/utxos-by-address.query';
|
||||
import { BtcFeeType, btcTxTimeMap } from '@app/query/bitcoin/bitcoin-client';
|
||||
import { useBitcoinFeeRate } from '@app/query/bitcoin/fees/fee-estimates.hooks';
|
||||
import { useAverageBitcoinFeeRate } from '@app/query/bitcoin/fees/fee-estimates.hooks';
|
||||
import { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks';
|
||||
import { useCurrentBtcNativeSegwitAccountAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
|
||||
|
||||
@@ -21,7 +21,7 @@ export function useBitcoinFeesList({ amount, recipient }: UseBitcoinFeesListArgs
|
||||
const { data: utxos } = useGetUtxosByAddressQuery(currentAccountBtcAddress);
|
||||
|
||||
const btcMarketData = useCryptoCurrencyMarketData('BTC');
|
||||
const { data: feeRate } = useBitcoinFeeRate();
|
||||
const { avgApiFeeRates: feeRate, isLoading } = useAverageBitcoinFeeRate();
|
||||
|
||||
const feesList = useMemo(() => {
|
||||
function getFiatFeeValue(fee: number) {
|
||||
@@ -37,21 +37,21 @@ export function useBitcoinFeesList({ amount, recipient }: UseBitcoinFeesListArgs
|
||||
utxos,
|
||||
recipient,
|
||||
amount: satAmount,
|
||||
feeRate: feeRate.fastestFee,
|
||||
feeRate: feeRate.fastestFee.toNumber(),
|
||||
});
|
||||
|
||||
const { fee: standartFeeValue } = determineUtxosForSpend({
|
||||
utxos,
|
||||
recipient,
|
||||
amount: satAmount,
|
||||
feeRate: feeRate.halfHourFee,
|
||||
feeRate: feeRate.halfHourFee.toNumber(),
|
||||
});
|
||||
|
||||
const { fee: lowFeeValue } = determineUtxosForSpend({
|
||||
utxos,
|
||||
recipient,
|
||||
amount: satAmount,
|
||||
feeRate: feeRate.economyFee,
|
||||
feeRate: feeRate.hourFee.toNumber(),
|
||||
});
|
||||
|
||||
return [
|
||||
@@ -61,7 +61,7 @@ export function useBitcoinFeesList({ amount, recipient }: UseBitcoinFeesListArgs
|
||||
btcValue: formatMoneyPadded(createMoney(highFeeValue, 'BTC')),
|
||||
time: btcTxTimeMap.fastestFee,
|
||||
fiatValue: getFiatFeeValue(highFeeValue),
|
||||
feeRate: feeRate.fastestFee,
|
||||
feeRate: feeRate.fastestFee.toNumber(),
|
||||
},
|
||||
{
|
||||
label: BtcFeeType.Standard,
|
||||
@@ -69,7 +69,7 @@ export function useBitcoinFeesList({ amount, recipient }: UseBitcoinFeesListArgs
|
||||
btcValue: formatMoneyPadded(createMoney(standartFeeValue, 'BTC')),
|
||||
time: btcTxTimeMap.halfHourFee,
|
||||
fiatValue: getFiatFeeValue(standartFeeValue),
|
||||
feeRate: feeRate.halfHourFee,
|
||||
feeRate: feeRate.halfHourFee.toNumber(),
|
||||
},
|
||||
{
|
||||
label: BtcFeeType.Low,
|
||||
@@ -77,12 +77,13 @@ export function useBitcoinFeesList({ amount, recipient }: UseBitcoinFeesListArgs
|
||||
btcValue: formatMoneyPadded(createMoney(lowFeeValue, 'BTC')),
|
||||
time: btcTxTimeMap.economyFee,
|
||||
fiatValue: getFiatFeeValue(lowFeeValue),
|
||||
feeRate: feeRate.economyFee,
|
||||
feeRate: feeRate.hourFee.toNumber(),
|
||||
},
|
||||
];
|
||||
}, [feeRate, btcMarketData, utxos, recipient, amount]);
|
||||
|
||||
return {
|
||||
feesList,
|
||||
isLoading,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ import { truncateMiddle } from '@stacks/ui-utils';
|
||||
|
||||
import { BitcoinTransaction } from '@shared/models/transactions/bitcoin-transaction.model';
|
||||
|
||||
import { sumNumbers } from '@app/common/math/helpers';
|
||||
import { satToBtc } from '@app/common/money/unit-conversion';
|
||||
import { sumNumbers } from '@app/common/utils';
|
||||
|
||||
export const getBitcoinTxCaption = (transaction?: BitcoinTransaction) =>
|
||||
transaction ? truncateMiddle(transaction.txid, 4) : '';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useIsFetching } from '@tanstack/react-query';
|
||||
|
||||
import { sumNumbers } from '@app/common/utils';
|
||||
import { sumNumbers } from '@app/common/math/helpers';
|
||||
import { QueryPrefixes } from '@app/query/query-prefixes';
|
||||
|
||||
function areAnyQueriesFetching(...args: number[]) {
|
||||
|
||||
@@ -4,8 +4,8 @@ import * as btc from '@scure/btc-signer';
|
||||
|
||||
import { Money, createMoney } from '@shared/models/money.model';
|
||||
|
||||
import { sumNumbers } from '@app/common/math/helpers';
|
||||
import { BtcSizeFeeEstimator } from '@app/common/transactions/bitcoin/fees/btc-size-fee-estimator';
|
||||
import { sumNumbers } from '@app/common/utils';
|
||||
import { useCurrentTaprootAccountUninscribedUtxos } from '@app/query/bitcoin/balance/bitcoin-balances.query';
|
||||
import { useBitcoinFeeRate } from '@app/query/bitcoin/fees/fee-estimates.hooks';
|
||||
import { getNumberOfInscriptionOnUtxo } from '@app/query/bitcoin/ordinals/ordinals-aware-utxo.query';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { sumNumbers } from '@app/common/utils';
|
||||
import { sumNumbers } from '@app/common/math/helpers';
|
||||
|
||||
import { selectInscriptionTransferCoins } from './select-inscription-coins';
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { BTC_P2WPKH_DUST_AMOUNT } from '@shared/constants';
|
||||
import { isDefined } from '@shared/utils';
|
||||
|
||||
import { sumNumbers } from '@app/common/math/helpers';
|
||||
import { BtcSizeFeeEstimator } from '@app/common/transactions/bitcoin/fees/btc-size-fee-estimator';
|
||||
import { sumNumbers } from '@app/common/utils';
|
||||
import { createCounter } from '@app/common/utils/counter';
|
||||
import { UtxoResponseItem } from '@app/query/bitcoin/bitcoin-client';
|
||||
import { TaprootUtxo } from '@app/query/bitcoin/ordinals/use-taproot-address-utxos.query';
|
||||
|
||||
@@ -51,7 +51,7 @@ export function SendInscriptionForm() {
|
||||
|
||||
try {
|
||||
const numInscriptionsOnUtxo = await getNumberOfInscriptionOnUtxo(utxo.txid, utxo.vout);
|
||||
if (numInscriptionsOnUtxo !== 1) {
|
||||
if (numInscriptionsOnUtxo > 1) {
|
||||
setShowError('Sending inscription from utxo with multiple inscriptions is unsupported');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useMemo } from 'react';
|
||||
|
||||
import { createMoney } from '@shared/models/money.model';
|
||||
|
||||
import { sumNumbers } from '@app/common/utils';
|
||||
import { sumNumbers } from '@app/common/math/helpers';
|
||||
import { useCurrentBtcNativeSegwitAccountAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
|
||||
|
||||
import { useGetBitcoinTransactionsByAddressQuery } from './transactions-by-address.query';
|
||||
|
||||
@@ -5,7 +5,7 @@ import BigNumber from 'bignumber.js';
|
||||
import { createMoney } from '@shared/models/money.model';
|
||||
import { isDefined } from '@shared/utils';
|
||||
|
||||
import { sumNumbers } from '@app/common/utils';
|
||||
import { sumNumbers } from '@app/common/math/helpers';
|
||||
import { useCurrentBtcNativeSegwitAccountAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';
|
||||
|
||||
import { createBitcoinCryptoCurrencyAssetTypeWrapper } from '../address/address.utils';
|
||||
|
||||
@@ -1,9 +1,61 @@
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { logger } from '@shared/logger';
|
||||
|
||||
import { useGetBitcoinFeeEstimatesQuery } from './fee-estimates.query';
|
||||
import { calculateMeanAverage } from '@app/common/math/calculate-averages';
|
||||
|
||||
import {
|
||||
useGetBitcoinAllFeeEstimatesQuery,
|
||||
useGetBitcoinMempoolApiFeeEstimatesQuery,
|
||||
} from './fee-estimates.query';
|
||||
|
||||
export function useBitcoinFeeRate() {
|
||||
return useGetBitcoinFeeEstimatesQuery({
|
||||
return useGetBitcoinMempoolApiFeeEstimatesQuery({
|
||||
onError: err => logger.error('Error getting bitcoin fee estimates', { err }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useAverageBitcoinFeeRate() {
|
||||
const { data: avgApiFeeRates, isLoading } = useGetBitcoinAllFeeEstimatesQuery({
|
||||
onError: err => logger.error('Error getting all apis bitcoin fee estimates', { err }),
|
||||
select: resp => {
|
||||
if (resp[0].status === 'rejected' && resp[1].status === 'rejected') {
|
||||
return null;
|
||||
}
|
||||
|
||||
let mempoolApiFeeRates = null;
|
||||
if (resp[0].status === 'fulfilled') {
|
||||
mempoolApiFeeRates = resp[0].value;
|
||||
}
|
||||
|
||||
let earnApiFeeRates = null;
|
||||
if (resp[1].status === 'fulfilled') {
|
||||
earnApiFeeRates = resp[1].value;
|
||||
}
|
||||
|
||||
const fastestFees = [
|
||||
new BigNumber(mempoolApiFeeRates?.fastestFee ?? 0),
|
||||
new BigNumber(earnApiFeeRates?.fastestFee ?? 0),
|
||||
].filter(fee => fee.isGreaterThan(0));
|
||||
|
||||
const halfHourFees = [
|
||||
new BigNumber(mempoolApiFeeRates?.halfHourFee ?? 0),
|
||||
new BigNumber(earnApiFeeRates?.halfHourFee ?? 0),
|
||||
].filter(fee => fee.isGreaterThan(0));
|
||||
|
||||
const hourFees = [
|
||||
new BigNumber(mempoolApiFeeRates?.hourFee ?? 0),
|
||||
new BigNumber(earnApiFeeRates?.hourFee ?? 0),
|
||||
].filter(fee => fee.isGreaterThan(0));
|
||||
|
||||
// zero values for cases when one api is down
|
||||
return {
|
||||
fastestFee: calculateMeanAverage(fastestFees),
|
||||
halfHourFee: calculateMeanAverage(halfHourFees),
|
||||
hourFee: calculateMeanAverage(hourFees),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
return { isLoading, avgApiFeeRates };
|
||||
}
|
||||
|
||||
@@ -5,25 +5,54 @@ import { useBitcoinClient } from '@app/store/common/api-clients.hooks';
|
||||
|
||||
import { BitcoinClient } from '../bitcoin-client';
|
||||
|
||||
function fetchBitcoinFeeEstimates(client: BitcoinClient) {
|
||||
// Mempool api
|
||||
function fetchMempoolApiBitcoinFeeEstimates(client: BitcoinClient) {
|
||||
return async () => {
|
||||
return client.feeEstimatesApi.getFeeEstimatesFromMempoolSpaceApi();
|
||||
};
|
||||
}
|
||||
|
||||
type FetchBitcoinFeeEstimatesResp = Awaited<
|
||||
ReturnType<ReturnType<typeof fetchBitcoinFeeEstimates>>
|
||||
type FetchMempoolApiBitcoinFeeEstimatesResp = Awaited<
|
||||
ReturnType<ReturnType<typeof fetchMempoolApiBitcoinFeeEstimates>>
|
||||
>;
|
||||
|
||||
// https://github.com/bitcoinbook/bitcoinbook/blob/develop/ch06.asciidoc#transaction-fees
|
||||
// Possible alt api if needed: https://bitcoinfees.earn.com/api
|
||||
export function useGetBitcoinFeeEstimatesQuery<T extends unknown = FetchBitcoinFeeEstimatesResp>(
|
||||
options?: AppUseQueryConfig<FetchBitcoinFeeEstimatesResp, T>
|
||||
) {
|
||||
export function useGetBitcoinMempoolApiFeeEstimatesQuery<
|
||||
T extends unknown = FetchMempoolApiBitcoinFeeEstimatesResp
|
||||
>(options?: AppUseQueryConfig<FetchMempoolApiBitcoinFeeEstimatesResp, T>) {
|
||||
const client = useBitcoinClient();
|
||||
return useQuery({
|
||||
queryKey: ['bitcoin-fee-estimates'],
|
||||
queryFn: fetchBitcoinFeeEstimates(client),
|
||||
queryKey: ['mempool-api-bitcoin-fee-estimates'],
|
||||
queryFn: fetchMempoolApiBitcoinFeeEstimates(client),
|
||||
staleTime: 1000 * 60,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
// Earn api
|
||||
function fetchAllBitcoinFeeEstimates(client: BitcoinClient) {
|
||||
return async () => {
|
||||
return Promise.allSettled([
|
||||
client.feeEstimatesApi.getFeeEstimatesFromMempoolSpaceApi(),
|
||||
client.feeEstimatesApi.getFeeEstimatesFromEarnApi(),
|
||||
]);
|
||||
};
|
||||
}
|
||||
|
||||
type FetchAllBitcoinFeeEstimatesResp = Awaited<
|
||||
ReturnType<ReturnType<typeof fetchAllBitcoinFeeEstimates>>
|
||||
>;
|
||||
|
||||
export function useGetBitcoinAllFeeEstimatesQuery<
|
||||
T extends unknown = FetchAllBitcoinFeeEstimatesResp
|
||||
>(options?: AppUseQueryConfig<FetchAllBitcoinFeeEstimatesResp, T>) {
|
||||
const client = useBitcoinClient();
|
||||
return useQuery({
|
||||
queryKey: ['all-bitcoin-fee-estimates'],
|
||||
queryFn: fetchAllBitcoinFeeEstimates(client),
|
||||
staleTime: 1000 * 60,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: false,
|
||||
|
||||
@@ -30,7 +30,9 @@ export type OrdApiInscriptionTxOutput = Prettify<yup.InferType<typeof ordApiGetT
|
||||
|
||||
export async function getNumberOfInscriptionOnUtxo(id: string, index: number) {
|
||||
const resp = await fetchOrdinalsAwareUtxo(id, index);
|
||||
return resp.all_inscriptions?.length ?? 0;
|
||||
if (resp.all_inscriptions) return resp.all_inscriptions.length;
|
||||
if (resp.inscriptions) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
async function fetchOrdinalsAwareUtxo(
|
||||
|
||||
@@ -6,7 +6,7 @@ import { CryptoCurrencies } from '@shared/models/currencies.model';
|
||||
import { MarketData, createMarketData, createMarketPair } from '@shared/models/market.model';
|
||||
import { createMoney, currencyDecimalsMap } from '@shared/models/money.model';
|
||||
|
||||
import { calculateMeanAverage } from '@app/common/calculate-averages';
|
||||
import { calculateMeanAverage } from '@app/common/math/calculate-averages';
|
||||
import { convertAmountToFractionalUnit } from '@app/common/money/calculate-money';
|
||||
|
||||
import {
|
||||
|
||||
21
tests/specs/ordinals/ordinals.spec.ts
Normal file
21
tests/specs/ordinals/ordinals.spec.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { test } from '@playwright/test';
|
||||
|
||||
import { getNumberOfInscriptionOnUtxo } from '@app/query/bitcoin/ordinals/ordinals-aware-utxo.query';
|
||||
|
||||
test.describe(getNumberOfInscriptionOnUtxo.name, () => {
|
||||
test('should return 3 in case of 3 inscriptions', async () => {
|
||||
const resp = await getNumberOfInscriptionOnUtxo(
|
||||
'aa24aecb0e60afa43b646c5a61fee76aebdbbf85b8f85a4aa429f9d0c52c9623',
|
||||
0
|
||||
);
|
||||
test.expect(resp).toBe(3);
|
||||
});
|
||||
|
||||
test('should return 0 in case of this random address', async () => {
|
||||
const resp = await getNumberOfInscriptionOnUtxo(
|
||||
'75d11f43163ca0c3a1656e124a63bc08da267c0f8454aa5244ef7346839dc5d5',
|
||||
0
|
||||
);
|
||||
test.expect(resp).toBe(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user