Skip to content

Commit

Permalink
feat: add btc set fee choice
Browse files Browse the repository at this point in the history
  • Loading branch information
alter-eggo committed Apr 13, 2023
1 parent afe15f4 commit 485dbcd
Show file tree
Hide file tree
Showing 30 changed files with 504 additions and 141 deletions.
87 changes: 60 additions & 27 deletions src/app/components/info-card/info-card.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Box, Button, Flex, FlexProps, Stack, Text } from '@stacks/ui';
import { Box, Button, Flex, FlexProps, Stack, StackProps, Text } from '@stacks/ui';
import { SharedComponentsSelectors } from '@tests/selectors/shared-component.selectors';

import { isString } from '@shared/utils';

import { whenPageMode } from '@app/common/utils';
import { figmaTheme } from '@app/common/utils/figma-theme';

import { SpaceBetween } from '../layout/space-between';
Expand Down Expand Up @@ -54,7 +55,7 @@ export function InfoCardSeparator() {
}

// InfoCardAssetValue
interface InfoCardAssetValueProps {
interface InfoCardAssetValueProps extends StackProps {
value: number;
fiatValue?: string;
fiatSymbol?: string;
Expand All @@ -68,35 +69,37 @@ export function InfoCardAssetValue({
fiatSymbol,
symbol,
icon,
...props
}: InfoCardAssetValueProps) {
return (
<Stack
mb="44px"
width="100%"
alignItems="center"
backgroundColor="#F9F9FA"
py="24px"
border="1px solid #EFEFF2"
borderRadius="10px"
>
{icon && <Box as={icon} size="32px" />}
<Box width="100%" {...props}>
<Stack
width="100%"
alignItems="center"
backgroundColor="#F9F9FA"
py="24px"
border="1px solid #EFEFF2"
borderRadius="10px"
>
{icon && <Box as={icon} size="32px" />}

<Flex flexDirection="column" alignItems="center">
<Text
fontSize="24px"
fontWeight="500"
lineHeight="36px"
data-testid={SharedComponentsSelectors.InfoCardAssetValue}
>
{value} {symbol}
</Text>
{fiatValue && (
<Text fontSize="12px" mt="4px">
~ {fiatValue} {fiatSymbol}
<Flex flexDirection="column" alignItems="center">
<Text
fontSize="24px"
fontWeight="500"
lineHeight="36px"
data-testid={SharedComponentsSelectors.InfoCardAssetValue}
>
{value} {symbol}
</Text>
)}
</Flex>
</Stack>
{fiatValue && (
<Text fontSize="12px" mt="4px">
~ {fiatValue} {fiatSymbol}
</Text>
)}
</Flex>
</Stack>
</Box>
);
}

Expand Down Expand Up @@ -124,3 +127,33 @@ export function InfoCardBtn({ icon, label, onClick }: InfoCardBtnProps) {
</Button>
);
}

// InfoCardFooter
interface InfoCardFooterProps {
children: React.ReactNode;
}

export function InfoCardFooter({ children }: InfoCardFooterProps) {
return (
<Flex
bottom="0"
width="100%"
bg={whenPageMode({
full: '',
popup: '#fff',
})}
borderTop="1px solid #EFEFF2"
alignItems="center"
justifyContent="center"
zIndex="999"
py="loose"
px="extra-loose"
position={whenPageMode({
full: 'unset',
popup: 'fixed',
})}
>
{children}
</Flex>
);
}
8 changes: 1 addition & 7 deletions src/app/components/modal-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,7 @@ export function ModalHeader({
</Title>
</Flex>

<Flex
alignItems="center"
flexBasis="20%"
isInline
justifyContent="flex-end"
position="relative"
>
<Flex alignItems="center" flexBasis="20%" justifyContent="flex-end" position="relative">
<NetworkModeBadge position="absolute" right="35px" />
{(onClose || defaultClose) && (
<IconButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { InfoCard, InfoCardRow, InfoCardSeparator } from '@app/components/info-c
import { InscriptionPreview } from '@app/components/inscription-preview-card/components/inscription-preview';
import { PrimaryButton } from '@app/components/primary-button';
import { useCurrentNativeSegwitUtxos } from '@app/query/bitcoin/address/address.hooks';
import { useBitcoinFeeRate } from '@app/query/bitcoin/fees/fee-estimates.hooks';
import { btcTxTimeMap } from '@app/query/bitcoin/bitcoin-client';

import { InscriptionPreviewCard } from '../../../components/inscription-preview-card/inscription-preview-card';
import { useBitcoinBroadcastTransaction } from '../../../query/bitcoin/transaction/use-bitcoin-broadcast-transaction';
Expand All @@ -25,7 +25,7 @@ function useSendInscriptionReviewState() {
return {
signedTx: get(location.state, 'tx') as string,
recipient: get(location.state, 'recipient', '') as string,
fee: get(location.state, 'fee'),
fee: get(location.state, 'fee') as number,
};
}

Expand All @@ -39,9 +39,7 @@ export function SendInscriptionReview() {
const { refetch } = useCurrentNativeSegwitUtxos();
const { broadcastTx, isBroadcasting } = useBitcoinBroadcastTransaction();

const { data: feeRate } = useBitcoinFeeRate();

const arrivesIn = feeRate ? `~${feeRate?.fastestFee} min` : '~10 – 20 min';
const arrivesIn = btcTxTimeMap.hourFee;
const summaryFee = formatMoney(createMoney(Number(fee), 'BTC'));

async function sendInscription() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useNavigate } from 'react-router-dom';

// import get from 'lodash.get';
// import { RouteUrls } from '@shared/route-urls';
import { BaseDrawer } from '@app/components/drawer/base-drawer';

// import { BitcoinSetFee } from '../send-crypto-asset-form/family/bitcoin/components/bitcoin-set-fee';
// import { useInscriptionSendState } from './send-inscription-container';

// function useSendInscriptionSetFeeState() {
// const location = useLocation();
// return {
// tx: get(location.state, 'tx') as string,
// recipient: get(location.state, 'recipient', '') as string,
// };
// }

export function SendInscriptionSetFee() {
const navigate = useNavigate();
// const { tx, recipient } = useSendInscriptionSetFeeState();
// const { inscription, utxo } = useInscriptionSendState();

// function previewTransaction(feeRate: number, feeValue: number, time: string) {
// feeRate;
// navigate(RouteUrls.SendOrdinalInscriptionReview, {
// state: { fee: feeValue, inscription, utxo, recipient, tx, arrivesIn: time },
// });
// }

return (
<BaseDrawer title="Choose fee" isShowing enableGoBack onClose={() => navigate(-1)}>
{/* <BitcoinSetFee onChooseFee={previewTransaction} recipient={recipient} amount={}/>; */}
</BaseDrawer>
);
}
36 changes: 36 additions & 0 deletions src/app/pages/send/send-crypto-asset-form/components/fees-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Box, Flex, Text, color, transition } from '@stacks/ui';
import { SharedComponentsSelectors } from '@tests/selectors/shared-component.selectors';

interface FeesCardProps {
feeType: string;
feeAmount: string;
feeFiatValue: string;
arrivesIn: string;
onClick: () => void;
}
export function FeesCard({ feeType, feeAmount, feeFiatValue, arrivesIn, ...props }: FeesCardProps) {
return (
<Box
as="button"
border="1px solid"
borderColor={color('border')}
borderRadius="16px"
boxShadow="0px 1px 2px rgba(0, 0, 0, 0.04)"
transition={transition}
padding="extra-loose"
width="100%"
_hover={{ background: '#F9F9FA' }}
data-testid={SharedComponentsSelectors.FeeCard}
{...props}
>
<Flex justifyContent="space-between" mb="tight" fontWeight={500}>
<Text>{feeType}</Text>
<Text data-testid={SharedComponentsSelectors.FeeCardFeeValue}>{feeAmount}</Text>
</Flex>
<Flex justifyContent="space-between" color="#74777D">
<Text>{arrivesIn}</Text>
<Text>{feeFiatValue}</Text>
</Flex>
</Box>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export function FormFooter(props: { balance: Money }) {
bg={color('bg')}
borderTop="1px solid #DCDDE2"
bottom="0px"
height={['106px', '116px']}
height={['96px', '116px']}
position={whenPageMode({
full: 'unset',
popup: 'absolute',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function PreviewButton(props: ButtonProps) {
width="100%"
{...props}
>
Preview
Continue
</Button>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Stack, Text } from '@stacks/ui';

import { FeesCard } from '../../../components/fees-card';
import { useBitcoinSetFees } from './use-bitcoin-set-fees';

interface BitcoinSetFeeProps {
onChooseFee(feeRate: number, feeValue: number, time: string): Promise<void> | void;
recipient: string;
amount: number;
}

export function BitcoinSetFee({ onChooseFee, recipient, amount }: BitcoinSetFeeProps) {
const { feesList } = useBitcoinSetFees({ recipient, amount });

return (
<Stack p="extra-loose" width="100%" spacing="extra-loose" alignItems="center">
<Stack width="100%" spacing="base">
{feesList.map(({ label, value, btcValue, fiatValue, time, feeRate }) => (
<FeesCard
key={label}
feeType={label}
feeAmount={btcValue}
feeFiatValue={fiatValue}
arrivesIn={time}
onClick={() => onChooseFee(feeRate, value, time)}
/>
))}
</Stack>

<Text fontSize="14px" color="#74777D" textAlign="center">
Fees are deducted from your balance,
<br /> it won't affect your sending amount.
</Text>
</Stack>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { useMemo } from 'react';

import { createMoney } from '@shared/models/money.model';

import { baseCurrencyAmountInQuote } from '@app/common/money/calculate-money';
import { formatMoneyPadded, i18nFormatCurrency } from '@app/common/money/format-money';
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 { useCryptoCurrencyMarketData } from '@app/query/common/market-data/market-data.hooks';
import { useCurrentBtcNativeSegwitAccountAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks';

interface UseBitcoinSetFeesArgs {
recipient: string;
amount: number;
}

export function useBitcoinSetFees({ recipient, amount }: UseBitcoinSetFeesArgs) {
const currentAccountBtcAddress = useCurrentBtcNativeSegwitAccountAddressIndexZero();
const { data: utxos } = useGetUtxosByAddressQuery(currentAccountBtcAddress);

const btcMarketData = useCryptoCurrencyMarketData('BTC');
const { data: feeRate } = useBitcoinFeeRate();

const feesList = useMemo(() => {
function getFiatFeeValue(fee: number) {
return `~ ${i18nFormatCurrency(
baseCurrencyAmountInQuote(createMoney(Math.ceil(fee), 'BTC'), btcMarketData)
)}`;
}

if (!feeRate || !utxos) return [];

const satAmount = btcToSat(amount).toNumber();
const { fee: highFeeValue } = determineUtxosForSpend({
utxos,
recipient,
amount: satAmount,
feeRate: feeRate.fastestFee,
});

const { fee: standartFeeValue } = determineUtxosForSpend({
utxos,
recipient,
amount: satAmount,
feeRate: feeRate.halfHourFee,
});

const { fee: lowFeeValue } = determineUtxosForSpend({
utxos,
recipient,
amount: satAmount,
feeRate: feeRate.economyFee,
});

return [
{
label: BtcFeeType.High,
value: highFeeValue,
btcValue: formatMoneyPadded(createMoney(highFeeValue, 'BTC')),
time: btcTxTimeMap.fastestFee,
fiatValue: getFiatFeeValue(highFeeValue),
feeRate: feeRate.fastestFee,
},

{
label: BtcFeeType.Standard,
value: standartFeeValue,
btcValue: formatMoneyPadded(createMoney(standartFeeValue, 'BTC')),
time: btcTxTimeMap.halfHourFee,
fiatValue: getFiatFeeValue(standartFeeValue),
feeRate: feeRate.halfHourFee,
},
{
label: BtcFeeType.Low,
value: lowFeeValue,
btcValue: formatMoneyPadded(createMoney(lowFeeValue, 'BTC')),
time: btcTxTimeMap.economyFee,
fiatValue: getFiatFeeValue(lowFeeValue),
feeRate: feeRate.economyFee,
},
];
}, [feeRate, btcMarketData, utxos, recipient, amount]);

return {
feesList,
};
}
Loading

0 comments on commit 485dbcd

Please sign in to comment.