From 43f157a34d456c3cc60d96482f2c85e812371d4a Mon Sep 17 00:00:00 2001 From: Jordan K <65149726+jordankzf@users.noreply.github.com> Date: Thu, 18 Jan 2024 10:48:09 +0800 Subject: [PATCH 01/20] ENG-3498: Control STX display via Manage Tokens Screen (#757) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add CoinItem map key * ManageTokens drawer round icons * ManageTokens drawer round text icons * Make TickerImage always round * Standalone TickerImage roundness * Add hideStx to wallet store * Why use the whole Coin 😎when you can use just what's needed * Comment typo * Add special Stacks to manageTokens * Deprecate rounded square STX icon * Move STX icon from ledger to dashboard folder * Sync hideStx to localStorage * Remove manual localStorage sync * v2 layout * flex magic for selective scrollable areas * Don't count STX as part of total balance if hidden * ENG-3509: Include SIP-10 token balances --- .../postCondition/stxPostConditionCard.tsx | 2 +- src/app/components/tokenImage/index.tsx | 8 +- src/app/components/tokenTile/index.tsx | 3 +- src/app/components/topRow/index.tsx | 2 +- src/app/hooks/useWalletReducer.ts | 7 + .../screens/confirmStxTransaction/index.tsx | 2 +- src/app/screens/home/balanceCard/index.tsx | 30 +++- .../screens/home/coinSelectModal/index.tsx | 6 +- src/app/screens/home/index.tsx | 9 +- .../screens/ledger/addStxAddress/index.tsx | 2 +- .../importLedgerAccount/steps/index.tsx | 4 +- .../verifyLedgerAccountAddress/index.tsx | 2 +- .../screens/manageTokens/coinItem/index.tsx | 13 +- src/app/screens/manageTokens/index.tsx | 132 +++++++++++++++--- .../stores/wallet/actions/actionCreators.ts | 7 + src/app/stores/wallet/actions/types.ts | 11 +- src/app/stores/wallet/reducer.ts | 7 + src/assets/img/dashboard/stack_icon.svg | 5 - src/assets/img/dashboard/stacks_token.svg | 12 -- .../img/{ledger => dashboard}/stx_icon.svg | 0 src/locales/en.json | 3 +- 21 files changed, 204 insertions(+), 63 deletions(-) delete mode 100644 src/assets/img/dashboard/stack_icon.svg delete mode 100644 src/assets/img/dashboard/stacks_token.svg rename src/assets/img/{ledger => dashboard}/stx_icon.svg (100%) diff --git a/src/app/components/postCondition/stxPostConditionCard.tsx b/src/app/components/postCondition/stxPostConditionCard.tsx index 61e18710f..5d278bda6 100644 --- a/src/app/components/postCondition/stxPostConditionCard.tsx +++ b/src/app/components/postCondition/stxPostConditionCard.tsx @@ -1,5 +1,5 @@ +import IconStacks from '@assets/img/dashboard/stx_icon.svg'; import { PostCondition } from '@stacks/transactions'; -import IconStacks from '@assets/img/dashboard/stack_icon.svg'; import PostConditionsView from './postConditionView'; import { getAmountFromPostCondition } from './postConditionView/helper'; diff --git a/src/app/components/tokenImage/index.tsx b/src/app/components/tokenImage/index.tsx index fc6fdf5fb..edf602795 100644 --- a/src/app/components/tokenImage/index.tsx +++ b/src/app/components/tokenImage/index.tsx @@ -1,5 +1,5 @@ import IconBitcoin from '@assets/img/dashboard/bitcoin_icon.svg'; -import IconStacks from '@assets/img/dashboard/stack_icon.svg'; +import IconStacks from '@assets/img/dashboard/stx_icon.svg'; import BarLoader from '@components/barLoader'; import { FungibleToken } from '@secretkeylabs/xverse-core'; import { LoaderSize } from '@utils/constants'; @@ -17,10 +17,10 @@ export interface TokenImageProps { round?: boolean; } -const TickerImage = styled.img<{ size?: number; round?: boolean }>((props) => ({ +const TickerImage = styled.img<{ size?: number }>((props) => ({ height: props.size ?? 44, width: props.size ?? 44, - borderRadius: props.round ? '50%' : 'none', + borderRadius: '50%', })); const LoaderImageContainer = styled.div({ @@ -85,5 +85,5 @@ export default function TokenImage({ ); } - return ; + return ; } diff --git a/src/app/components/tokenTile/index.tsx b/src/app/components/tokenTile/index.tsx index 3b605a588..acdb7883a 100644 --- a/src/app/components/tokenTile/index.tsx +++ b/src/app/components/tokenTile/index.tsx @@ -38,6 +38,7 @@ const TickerImage = styled.img((props) => ({ transform: 'all', height: props.enlargeTicker ? 40 : 32, width: props.enlargeTicker ? 40 : 32, + borderRadius: '50%', })); const TickerIconContainer = styled.div((props) => ({ @@ -47,7 +48,7 @@ const TickerIconContainer = styled.div((props) => ({ height: props.enlargeTicker ? 40 : 32, width: props.enlargeTicker ? 40 : 32, marginRight: props.theme.spacing(3), - borderRadius: props.theme.radius(2), + borderRadius: '50%', backgroundColor: props.color, })); diff --git a/src/app/components/topRow/index.tsx b/src/app/components/topRow/index.tsx index 38e44039c..eb37ba91b 100644 --- a/src/app/components/topRow/index.tsx +++ b/src/app/components/topRow/index.tsx @@ -39,7 +39,7 @@ const AnimatedBackButton = styled(BackButton)` `; interface Props { - title: string; + title?: string; onClick: (e: React.MouseEvent) => void; showBackButton?: boolean; className?: string; diff --git a/src/app/hooks/useWalletReducer.ts b/src/app/hooks/useWalletReducer.ts index a0a1ad269..119743fd6 100644 --- a/src/app/hooks/useWalletReducer.ts +++ b/src/app/hooks/useWalletReducer.ts @@ -24,6 +24,7 @@ import { resetWalletAction, selectAccount, setWalletAction, + setWalletHideStxAction, setWalletUnlockedAction, storeEncryptedSeedAction, updateLedgerAccountsAction, @@ -54,6 +55,7 @@ const useWalletReducer = () => { const { setSessionStartTime, clearSessionTime, setSessionStartTimeAndMigrate } = useWalletSession(); const queryClient = useQueryClient(); + const { hideStx } = useWalletSelector(); const loadActiveAccounts = async ( secretKey: string, @@ -166,6 +168,10 @@ const useWalletReducer = () => { dispatch(setWalletUnlockedAction(false)); }; + const toggleStxVisibility = async () => { + dispatch(setWalletHideStxAction(!hideStx)); + }; + const resetWallet = async () => { resetMixPanel(); dispatch(resetWalletAction()); @@ -379,6 +385,7 @@ const useWalletReducer = () => { addLedgerAccount, removeLedgerAccount, updateLedgerAccounts, + toggleStxVisibility, }; }; diff --git a/src/app/screens/confirmStxTransaction/index.tsx b/src/app/screens/confirmStxTransaction/index.tsx index 0fd2c680a..24baf7560 100644 --- a/src/app/screens/confirmStxTransaction/index.tsx +++ b/src/app/screens/confirmStxTransaction/index.tsx @@ -1,4 +1,4 @@ -import IconStacks from '@assets/img/dashboard/stack_icon.svg'; +import IconStacks from '@assets/img/dashboard/stx_icon.svg'; import { ConfirmStxTransactionState, LedgerTransactionType } from '@common/types/ledger'; import AccountHeaderComponent from '@components/accountHeader'; import ConfirmStxTransactionComponent from '@components/confirmStxTransactionComponent'; diff --git a/src/app/screens/home/balanceCard/index.tsx b/src/app/screens/home/balanceCard/index.tsx index 657039b98..03d1784b8 100644 --- a/src/app/screens/home/balanceCard/index.tsx +++ b/src/app/screens/home/balanceCard/index.tsx @@ -67,13 +67,22 @@ interface BalanceCardProps { function BalanceCard(props: BalanceCardProps) { const { t } = useTranslation('translation', { keyPrefix: 'DASHBOARD_SCREEN' }); - const { fiatCurrency, btcFiatRate, stxBtcRate, stxBalance, btcBalance, btcAddress, stxAddress } = - useWalletSelector(); + const { + fiatCurrency, + btcFiatRate, + stxBtcRate, + stxBalance, + btcBalance, + btcAddress, + stxAddress, + hideStx, + coinsList, + } = useWalletSelector(); const { isLoading, isRefetching } = props; function calculateTotalBalance() { let totalBalance = new BigNumber(0); - if (stxAddress) { + if (stxAddress && !hideStx) { const stxFiatEquiv = microstacksToStx(new BigNumber(stxBalance)) .multipliedBy(new BigNumber(stxBtcRate)) .multipliedBy(new BigNumber(btcFiatRate)); @@ -85,6 +94,21 @@ function BalanceCard(props: BalanceCardProps) { ); totalBalance = totalBalance.plus(btcFiatEquiv); } + + if (coinsList) { + totalBalance = coinsList.reduce((acc, coin) => { + if (coin.visible && coin.tokenFiatRate && coin.decimals) { + const tokenUnits = new BigNumber(10).exponentiatedBy(new BigNumber(coin.decimals)); + const coinFiatValue = new BigNumber(coin.balance) + .dividedBy(tokenUnits) + .multipliedBy(new BigNumber(coin.tokenFiatRate)); + return acc.plus(coinFiatValue); + } + + return acc; + }, totalBalance); + } + return totalBalance.toNumber().toFixed(2); } diff --git a/src/app/screens/home/coinSelectModal/index.tsx b/src/app/screens/home/coinSelectModal/index.tsx index 38728bc3e..d15586d18 100644 --- a/src/app/screens/home/coinSelectModal/index.tsx +++ b/src/app/screens/home/coinSelectModal/index.tsx @@ -1,5 +1,5 @@ import IconBitcoin from '@assets/img/dashboard/bitcoin_icon.svg'; -import IconStacks from '@assets/img/dashboard/stack_icon.svg'; +import IconStacks from '@assets/img/dashboard/stx_icon.svg'; import BottomModal from '@components/bottomModal'; import TokenTile from '@components/tokenTile'; import useWalletSelector from '@hooks/useWalletSelector'; @@ -35,7 +35,7 @@ function CoinSelectModal({ }: Props) { const { t } = useTranslation('translation', { keyPrefix: 'DASHBOARD_SCREEN' }); const theme = useTheme(); - const { btcAddress, stxAddress } = useWalletSelector(); + const { btcAddress, stxAddress, hideStx } = useWalletSelector(); const handleOnBitcoinPress = () => { onSelectBitcoin?.(); onClose(); @@ -61,7 +61,7 @@ function CoinSelectModal({ onPress={handleOnBitcoinPress} /> )} - {stxAddress && ( + {stxAddress && !hideStx && ( - {stxAddress && ( + {stxAddress && !hideStx && ( )} - {stxAddress && ( + {stxAddress && !hideStx && ( ({ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', - marginTop: props.theme.spacing(11), + marginTop: props.theme.spacing(8), })); const CoinContainer = styled.div({ @@ -30,6 +30,7 @@ const CoinIcon = styled.img((props) => ({ width: 32, height: 32, resizeMode: 'stretch', + borderRadius: '50%', })); const CustomSwitch = styled(Switch)` @@ -43,12 +44,13 @@ const CustomSwitch = styled(Switch)` const TickerIconContainer = styled.div((props) => ({ display: 'flex', marginRight: props.theme.spacing(7), - height: 30, - width: 30, + height: '32px', + width: '32px', borderRadius: props.theme.radius(3), alignItems: 'center', justifyContent: 'center', backgroundColor: props.color, + flexShrink: 0, })); const TickerText = styled.h1((props) => ({ @@ -57,6 +59,7 @@ const TickerText = styled.h1((props) => ({ textAlign: 'center', wordBreak: 'break-all', fontSize: 10, + width: '100%', })); const SelectedCoinTitleText = styled.h1((props) => ({ @@ -72,9 +75,9 @@ const UnSelectedCoinTitleText = styled.h1((props) => ({ })); interface Props { - coin: Coin; + coin: Pick; disabled: boolean; - toggled(enabled: boolean, coin: Coin): void; + toggled(enabled: boolean, coin: Pick): void; enabled?: boolean; showDivider: boolean; } diff --git a/src/app/screens/manageTokens/index.tsx b/src/app/screens/manageTokens/index.tsx index e920f7331..5eacf4029 100644 --- a/src/app/screens/manageTokens/index.tsx +++ b/src/app/screens/manageTokens/index.tsx @@ -1,8 +1,13 @@ +import stacksIcon from '@assets/img/dashboard/stx_icon.svg'; +import BottomBar from '@components/tabBar'; import TopRow from '@components/topRow'; +import useWalletReducer from '@hooks/useWalletReducer'; +import useWalletSelector from '@hooks/useWalletSelector'; import CoinItem from '@screens/manageTokens/coinItem'; import { Coin, FungibleToken } from '@secretkeylabs/xverse-core'; import { StoreState } from '@stores/index'; import { FetchUpdatedVisibleCoinListAction } from '@stores/wallet/actions/actionCreators'; +import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; import { useNavigate } from 'react-router-dom'; @@ -11,8 +16,6 @@ import styled from 'styled-components'; const TokenContainer = styled.div` display: flex; flex-direction: column; - padding-left: 22px; - padding-right: 22px; overflow-y: auto; &::-webkit-scrollbar { display: none; @@ -23,15 +26,86 @@ const Container = styled.div({ display: 'flex', flexDirection: 'column', overflow: 'hidden', + paddingLeft: 16, + paddingRight: 16, }); +const ScrollableContainer = styled.div` + flex: 1; + overflow-y: auto; +`; + +const FtInfoContainer = styled.div((props) => ({ + display: 'flex', + flexDirection: 'row', + marginBottom: props.theme.spacing(8), +})); + +const Button = styled.button<{ + isSelected: boolean; +}>((props) => ({ + ...props.theme.typography.body_bold_l, + fontSize: 12, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + height: 31, + paddingLeft: props.theme.spacing(6), + paddingRight: props.theme.spacing(6), + marginRight: props.theme.spacing(2), + borderRadius: 44, + background: props.isSelected ? props.theme.colors.elevation3 : 'transparent', + color: props.theme.colors.white_0, + opacity: props.isSelected ? 1 : 0.6, + userSelect: 'none', +})); + +const Header = styled.h1((props) => ({ + ...props.theme.typography.headline_xs, + marginBottom: props.theme.spacing(8), +})); + +const Description = styled.h1((props) => ({ + ...props.theme.typography.body_m, + color: props.theme.colors.white_200, + marginBottom: props.theme.spacing(16), +})); + +function Stacks() { + const { hideStx } = useWalletSelector(); + const { toggleStxVisibility } = useWalletReducer(); + const tickerConstant = 'stx'; + return ( + + ); +} + +enum Protocols { + SIP_10 = 'SIP-10', + BRC_20 = 'BRC-20', +} + function ManageTokens() { const { t } = useTranslation('translation', { keyPrefix: 'TOKEN_SCREEN' }); const { coinsList, coins } = useSelector((state: StoreState) => state.walletState); + const [selectedProtocol, setSelectedProtocol] = useState(Protocols.SIP_10); + const navigate = useNavigate(); const dispatch = useDispatch(); - const toggled = (isEnabled: boolean, coin: Coin) => { + const toggled = (isEnabled: boolean, coin: Pick) => { /* if coins exists in list of fungible token, update the visible property otherwise add coin in list if coin is set to visible */ const coinToBeUpdated: FungibleToken | undefined = coinsList?.find( @@ -66,20 +140,44 @@ function ManageTokens() { } return ( - - - - {coins?.map((coin, index) => ( - - ))} - - + <> + + + +
{t('ADD_COINS')}
+ {t('DESCRIPTION')} + + + {/* To be uncommented when brc-20 tokens are supported */} + {/* */} + + + + {coins?.map((coin, index) => ( + + ))} + +
+
+ + ); } diff --git a/src/app/stores/wallet/actions/actionCreators.ts b/src/app/stores/wallet/actions/actionCreators.ts index 7ad9cc88d..e9802437c 100644 --- a/src/app/stores/wallet/actions/actionCreators.ts +++ b/src/app/stores/wallet/actions/actionCreators.ts @@ -273,3 +273,10 @@ export function setWalletUnlockedAction(isUnlocked: boolean): actions.SetWalletU isUnlocked, }; } + +export function setWalletHideStxAction(hideStx: boolean): actions.SetWalletHideStx { + return { + type: actions.SetWalletHideStxKey, + hideStx, + }; +} diff --git a/src/app/stores/wallet/actions/types.ts b/src/app/stores/wallet/actions/types.ts index bec02f74e..8d1ce4570 100644 --- a/src/app/stores/wallet/actions/types.ts +++ b/src/app/stores/wallet/actions/types.ts @@ -38,6 +38,8 @@ export const SetBrcCoinsListKey = 'SetBrcCoinsList'; export const SetWalletLockPeriodKey = 'SetWalletLockPeriod'; export const SetWalletUnlockedKey = 'SetWalletUnlocked'; +export const SetWalletHideStxKey = 'SetWalletHideStx'; + export enum WalletSessionPeriods { LOW = 15, STANDARD = 30, @@ -82,6 +84,7 @@ export interface WalletState { accountName: string | undefined; walletLockPeriod: WalletSessionPeriods; isUnlocked: boolean; + hideStx: boolean; } export interface SetWallet { @@ -224,6 +227,11 @@ export interface SetWalletUnlocked { type: typeof SetWalletUnlockedKey; isUnlocked: boolean; } + +export interface SetWalletHideStx { + type: typeof SetWalletHideStxKey; + hideStx: boolean; +} export type WalletActions = | SetWallet | ResetWallet @@ -250,4 +258,5 @@ export type WalletActions = | SetBrcCoinsData | SetWalletLockPeriod | SetRareSatsNoticeDismissed - | SetWalletUnlocked; + | SetWalletUnlocked + | SetWalletHideStx; diff --git a/src/app/stores/wallet/reducer.ts b/src/app/stores/wallet/reducer.ts index 41d2e543f..083c4c7c6 100644 --- a/src/app/stores/wallet/reducer.ts +++ b/src/app/stores/wallet/reducer.ts @@ -20,6 +20,7 @@ import { SetCoinRatesKey, SetFeeMultiplierKey, SetStxWalletDataKey, + SetWalletHideStxKey, SetWalletKey, SetWalletLockPeriodKey, SetWalletUnlockedKey, @@ -99,6 +100,7 @@ export const initialWalletState: WalletState = { accountName: undefined, walletLockPeriod: WalletSessionPeriods.STANDARD, isUnlocked: false, + hideStx: false, }; const walletReducer = ( @@ -263,6 +265,11 @@ const walletReducer = ( ...state, isUnlocked: action.isUnlocked, }; + case SetWalletHideStxKey: + return { + ...state, + hideStx: action.hideStx, + }; default: return state; } diff --git a/src/assets/img/dashboard/stack_icon.svg b/src/assets/img/dashboard/stack_icon.svg deleted file mode 100644 index 294ec6842..000000000 --- a/src/assets/img/dashboard/stack_icon.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/assets/img/dashboard/stacks_token.svg b/src/assets/img/dashboard/stacks_token.svg deleted file mode 100644 index 468e80753..000000000 --- a/src/assets/img/dashboard/stacks_token.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/src/assets/img/ledger/stx_icon.svg b/src/assets/img/dashboard/stx_icon.svg similarity index 100% rename from src/assets/img/ledger/stx_icon.svg rename to src/assets/img/dashboard/stx_icon.svg diff --git a/src/locales/en.json b/src/locales/en.json index e5bfdf656..d7fe25d36 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -224,7 +224,8 @@ } }, "TOKEN_SCREEN": { - "ADD_COINS": "Manage tokens" + "ADD_COINS": "Manage tokens", + "DESCRIPTION": "Select which assets you would like to see on your dashboard." }, "ACCOUNT_SCREEN": { "CHANGE_ACCOUNT": "Change account", From 61af7b1da14f616e4285d5db15300f923d5e8903 Mon Sep 17 00:00:00 2001 From: Tim Man Date: Thu, 18 Jan 2024 18:12:22 +0800 Subject: [PATCH 02/20] fix: translation strings (#764) --- src/app/screens/rareSatsBundle/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/screens/rareSatsBundle/index.tsx b/src/app/screens/rareSatsBundle/index.tsx index c6beccec5..621270972 100644 --- a/src/app/screens/rareSatsBundle/index.tsx +++ b/src/app/screens/rareSatsBundle/index.tsx @@ -322,11 +322,11 @@ function RareSatsBundle() { )} {showSendOrdinalsAlert && ( )} From dbb2266260285a514ab1292d3f98c50e88db54e1 Mon Sep 17 00:00:00 2001 From: fede erbes Date: Mon, 22 Jan 2024 10:56:43 +0100 Subject: [PATCH 03/20] fix: wrong variable used in validation to show top divider for first asset item in receive in payment section component (#762) --- .../components/confirmBtcTransaction/receiveSection.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/components/confirmBtcTransaction/receiveSection.tsx b/src/app/components/confirmBtcTransaction/receiveSection.tsx index 9aa8e6923..404f3c351 100644 --- a/src/app/components/confirmBtcTransaction/receiveSection.tsx +++ b/src/app/components/confirmBtcTransaction/receiveSection.tsx @@ -57,7 +57,8 @@ function ReceiveSection({ outputs, netAmount, onShowInscription }: Props) { (output) => output.inscriptions.length > 0 || output.satributes.length > 0, ); const areInscriptionsRareSatsInPayment = inscriptionsRareSatsInPayment.length > 0; - const showPaymentSection = areInscriptionsRareSatsInPayment || netAmount > 0; + const amountIsBiggerThanZero = netAmount > 0; + const showPaymentSection = areInscriptionsRareSatsInPayment || amountIsBiggerThanZero; return ( <> @@ -98,7 +99,7 @@ function ReceiveSection({ outputs, netAmount, onShowInscription }: Props) { {t('YOUR_PAYMENT_ADDRESS')} - {netAmount > 0 && ( + {amountIsBiggerThanZero && ( @@ -113,7 +114,7 @@ function ReceiveSection({ outputs, netAmount, onShowInscription }: Props) { satributes={output.satributes} amount={output.amount} onShowInscription={onShowInscription} - showTopDivider={areInscriptionsRareSatsInPayment && index === 0} + showTopDivider={amountIsBiggerThanZero && index === 0} showBottomDivider={inscriptionsRareSatsInPayment.length > index + 1} /> ))} From 562ca71c81b818cf10dcd38a39e191c51fb30274 Mon Sep 17 00:00:00 2001 From: fede erbes Date: Mon, 22 Jan 2024 16:25:21 +0100 Subject: [PATCH 04/20] fix: rare sats icon shape and remove extra props from rare sat icon component (#755) --- .../bundleItem.tsx | 2 +- .../components/rareSatAsset/rareSatAsset.tsx | 85 ------------------ .../components/rareSatIcon/rareSatIcon.tsx | 55 ++++-------- .../nftDashboard/rareSatsTabGridItem.tsx | 6 +- src/app/screens/ordinalDetail/index.tsx | 4 +- .../rareSatsBundle/rareSatsBundleGridItem.tsx | 2 +- .../img/nftDashboard/rareSats/1Dpali.png | Bin 3481 -> 7944 bytes src/assets/img/nftDashboard/rareSats/1stx.png | Bin 1594 -> 3022 bytes .../img/nftDashboard/rareSats/2Dpali.png | Bin 3530 -> 8108 bytes .../img/nftDashboard/rareSats/3Dpali.png | Bin 3553 -> 8246 bytes .../img/nftDashboard/rareSats/alpha.png | Bin 1652 -> 3074 bytes src/assets/img/nftDashboard/rareSats/b286.png | Bin 1814 -> 4128 bytes src/assets/img/nftDashboard/rareSats/b78.png | Bin 1758 -> 3553 bytes src/assets/img/nftDashboard/rareSats/b9.png | Bin 1477 -> 2983 bytes .../img/nftDashboard/rareSats/b9450.png | Bin 1663 -> 3371 bytes .../img/nftDashboard/rareSats/black_epic.png | Bin 0 -> 1831 bytes .../img/nftDashboard/rareSats/black_epic.svg | 9 -- .../nftDashboard/rareSats/black_legendary.png | Bin 0 -> 2249 bytes .../nftDashboard/rareSats/black_legendary.svg | 10 --- .../img/nftDashboard/rareSats/black_rare.png | Bin 0 -> 1182 bytes .../img/nftDashboard/rareSats/black_rare.svg | 7 -- .../nftDashboard/rareSats/black_uncommon.png | Bin 0 -> 1719 bytes .../nftDashboard/rareSats/black_uncommon.svg | 8 -- .../img/nftDashboard/rareSats/common.svg | 7 -- src/assets/img/nftDashboard/rareSats/epic.png | Bin 0 -> 1966 bytes src/assets/img/nftDashboard/rareSats/epic.svg | 9 -- .../img/nftDashboard/rareSats/fibonacci.png | Bin 2263 -> 3977 bytes .../img/nftDashboard/rareSats/hitman.png | Bin 1164 -> 1727 bytes src/assets/img/nftDashboard/rareSats/jpeg.png | Bin 1548 -> 2910 bytes .../img/nftDashboard/rareSats/legendary.png | Bin 0 -> 2189 bytes .../img/nftDashboard/rareSats/legendary.svg | 10 --- .../img/nftDashboard/rareSats/mythic.png | Bin 0 -> 7355 bytes .../img/nftDashboard/rareSats/mythic.svg | 19 ---- .../img/nftDashboard/rareSats/nakamoto.png | Bin 1482 -> 2774 bytes .../img/nftDashboard/rareSats/namepali.png | Bin 3504 -> 7349 bytes .../img/nftDashboard/rareSats/new_feature.svg | 24 ----- .../img/nftDashboard/rareSats/omega.png | Bin 1225 -> 2265 bytes src/assets/img/nftDashboard/rareSats/pali.png | Bin 2512 -> 4990 bytes .../img/nftDashboard/rareSats/paliblock.png | Bin 3318 -> 6028 bytes .../rareSats/perfectpaliception.png | Bin 3330 -> 7456 bytes .../img/nftDashboard/rareSats/pizza.png | Bin 1990 -> 3777 bytes src/assets/img/nftDashboard/rareSats/rare.png | Bin 0 -> 1198 bytes src/assets/img/nftDashboard/rareSats/rare.svg | 7 -- .../img/nftDashboard/rareSats/seqpali.png | Bin 4229 -> 10891 bytes .../img/nftDashboard/rareSats/silkroad.png | Bin 3124 -> 6184 bytes .../img/nftDashboard/rareSats/uncommon.png | Bin 0 -> 1788 bytes .../img/nftDashboard/rareSats/uncommon.svg | 8 -- .../img/nftDashboard/rareSats/unknown.png | Bin 0 -> 455 bytes .../img/nftDashboard/rareSats/unknown.svg | 7 -- .../img/nftDashboard/rareSats/vintage.png | Bin 1564 -> 2881 bytes 50 files changed, 23 insertions(+), 256 deletions(-) delete mode 100644 src/app/components/rareSatAsset/rareSatAsset.tsx create mode 100644 src/assets/img/nftDashboard/rareSats/black_epic.png delete mode 100644 src/assets/img/nftDashboard/rareSats/black_epic.svg create mode 100644 src/assets/img/nftDashboard/rareSats/black_legendary.png delete mode 100644 src/assets/img/nftDashboard/rareSats/black_legendary.svg create mode 100644 src/assets/img/nftDashboard/rareSats/black_rare.png delete mode 100644 src/assets/img/nftDashboard/rareSats/black_rare.svg create mode 100644 src/assets/img/nftDashboard/rareSats/black_uncommon.png delete mode 100644 src/assets/img/nftDashboard/rareSats/black_uncommon.svg delete mode 100644 src/assets/img/nftDashboard/rareSats/common.svg create mode 100644 src/assets/img/nftDashboard/rareSats/epic.png delete mode 100644 src/assets/img/nftDashboard/rareSats/epic.svg create mode 100644 src/assets/img/nftDashboard/rareSats/legendary.png delete mode 100644 src/assets/img/nftDashboard/rareSats/legendary.svg create mode 100644 src/assets/img/nftDashboard/rareSats/mythic.png delete mode 100644 src/assets/img/nftDashboard/rareSats/mythic.svg delete mode 100644 src/assets/img/nftDashboard/rareSats/new_feature.svg create mode 100644 src/assets/img/nftDashboard/rareSats/rare.png delete mode 100644 src/assets/img/nftDashboard/rareSats/rare.svg create mode 100644 src/assets/img/nftDashboard/rareSats/uncommon.png delete mode 100644 src/assets/img/nftDashboard/rareSats/uncommon.svg create mode 100644 src/assets/img/nftDashboard/rareSats/unknown.png delete mode 100644 src/assets/img/nftDashboard/rareSats/unknown.svg diff --git a/src/app/components/confirmBtcTransactionComponent/bundleItem.tsx b/src/app/components/confirmBtcTransactionComponent/bundleItem.tsx index 8bb38bd77..9b324d659 100644 --- a/src/app/components/confirmBtcTransactionComponent/bundleItem.tsx +++ b/src/app/components/confirmBtcTransactionComponent/bundleItem.tsx @@ -74,7 +74,7 @@ function BundleItem({ if (index === 4) { return ; } - return ; + return ; })} diff --git a/src/app/components/rareSatAsset/rareSatAsset.tsx b/src/app/components/rareSatAsset/rareSatAsset.tsx deleted file mode 100644 index 27c763650..000000000 --- a/src/app/components/rareSatAsset/rareSatAsset.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import RareSatIcon from '@components/rareSatIcon/rareSatIcon'; -import OrdinalImage from '@screens/ordinals/ordinalImage'; -import { Inscription } from '@secretkeylabs/xverse-core'; -import { BundleItem } from '@utils/rareSats'; -import styled from 'styled-components'; - -const Container = styled.div` - width: 100%; - height: 100%; -`; - -const InscriptionContainer = styled.div` - width: 100%; - height: 100%; - position: relative; - border-radius: 8px; - overflow: hidden; -`; - -const RareSatIconContainer = styled.div<{ isGallery: boolean }>((props) => ({ - display: 'flex', - position: 'absolute', - zIndex: 1, - left: props.isGallery ? 20 : 8, - top: props.isGallery ? 20 : 8, -})); - -const RareSatsContainer = styled.div((props) => ({ - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - width: '100%', - aspectRatio: '1', - overflow: 'hidden', - position: 'relative', - backgroundColor: props.theme.colors.elevation1, - borderRadius: 8, -})); - -const DynamicSizeContainer = styled.div<{ isCollage: boolean }>((props) => ({ - width: props.isCollage ? '40%' : '50%', - height: props.isCollage ? '40%' : '50%', -})); - -interface Props { - item: BundleItem; - isCollage?: boolean; -} - -function RareSatAsset({ item, isCollage = false }: Props) { - const isGallery: boolean = document.documentElement.clientWidth > 360; - const isInscription = item.type === 'inscription' || item.type === 'inscribed-sat'; - - return ( - - {isInscription ? ( - - {!isCollage && !!item.rarity_ranking && item.rarity_ranking !== 'COMMON' && ( - - - - )} - - - ) : ( - - - - - - )} - - ); -} - -export default RareSatAsset; diff --git a/src/app/components/rareSatIcon/rareSatIcon.tsx b/src/app/components/rareSatIcon/rareSatIcon.tsx index 6f3a3f913..741bae34f 100644 --- a/src/app/components/rareSatIcon/rareSatIcon.tsx +++ b/src/app/components/rareSatIcon/rareSatIcon.tsx @@ -5,61 +5,43 @@ import ThreeDPali from '@assets/img/nftDashboard/rareSats/3Dpali.png'; import Alpha from '@assets/img/nftDashboard/rareSats/alpha.png'; import Block78 from '@assets/img/nftDashboard/rareSats/b78.png'; import Block9 from '@assets/img/nftDashboard/rareSats/b9.png'; -import BlackEpic from '@assets/img/nftDashboard/rareSats/black_epic.svg'; -import BlackLegendary from '@assets/img/nftDashboard/rareSats/black_legendary.svg'; -import BlackRare from '@assets/img/nftDashboard/rareSats/black_rare.svg'; -import BlackUncommon from '@assets/img/nftDashboard/rareSats/black_uncommon.svg'; -import Epic from '@assets/img/nftDashboard/rareSats/epic.svg'; +import BlackEpic from '@assets/img/nftDashboard/rareSats/black_epic.png'; +import BlackLegendary from '@assets/img/nftDashboard/rareSats/black_legendary.png'; +import BlackRare from '@assets/img/nftDashboard/rareSats/black_rare.png'; +import BlackUncommon from '@assets/img/nftDashboard/rareSats/black_uncommon.png'; +import Epic from '@assets/img/nftDashboard/rareSats/epic.png'; import FibonacciSequence from '@assets/img/nftDashboard/rareSats/fibonacci.png'; import Hitman from '@assets/img/nftDashboard/rareSats/hitman.png'; import Jpeg from '@assets/img/nftDashboard/rareSats/jpeg.png'; -import Legendary from '@assets/img/nftDashboard/rareSats/legendary.svg'; -import Mythic from '@assets/img/nftDashboard/rareSats/mythic.svg'; +import Legendary from '@assets/img/nftDashboard/rareSats/legendary.png'; +import Mythic from '@assets/img/nftDashboard/rareSats/mythic.png'; import Nakamoto from '@assets/img/nftDashboard/rareSats/nakamoto.png'; import Omega from '@assets/img/nftDashboard/rareSats/omega.png'; import Palindrome from '@assets/img/nftDashboard/rareSats/pali.png'; import BlockPali from '@assets/img/nftDashboard/rareSats/paliblock.png'; import Palinception from '@assets/img/nftDashboard/rareSats/perfectpaliception.png'; import Pizza from '@assets/img/nftDashboard/rareSats/pizza.png'; -import Rare from '@assets/img/nftDashboard/rareSats/rare.svg'; +import Rare from '@assets/img/nftDashboard/rareSats/rare.png'; import SequencePali from '@assets/img/nftDashboard/rareSats/seqpali.png'; import SilkRoad from '@assets/img/nftDashboard/rareSats/silkroad.png'; -import Uncommon from '@assets/img/nftDashboard/rareSats/uncommon.svg'; -import Unknown from '@assets/img/nftDashboard/rareSats/unknown.svg'; +import Uncommon from '@assets/img/nftDashboard/rareSats/uncommon.png'; +import Unknown from '@assets/img/nftDashboard/rareSats/unknown.png'; import Vintage from '@assets/img/nftDashboard/rareSats/vintage.png'; import { RareSatsType } from '@secretkeylabs/xverse-core'; import styled from 'styled-components'; -import Theme from '../../../theme'; -const Container = styled.div<{ bgColor: string; padding: number }>((props) => ({ - backgroundColor: props.bgColor, - padding: props.padding, - borderRadius: props.bgColor && props.padding ? '100%' : 0, -})); -const ImageContainer = styled.div<{ size: number; dynamicSize: boolean }>((props) => ({ - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - width: props.dynamicSize ? '100%' : props.size, - height: props.dynamicSize ? '100%' : props.size, - position: 'relative', -})); -const Image = styled.img` - width: 100%; - height: 100%; - zindex: 2; +const Image = styled.img<{ size?: number }>` object-fit: cover; + width: ${(props) => `${props.size}px` ?? '100%'}; + height: ${(props) => `${props.size}px` ?? '100%'}; `; interface Props { type: RareSatsType; size?: number; - bgColor?: keyof (typeof Theme)['colors']['background']; - padding?: number; - isDynamicSize?: boolean; } -function RareSatIcon({ type, size = 24, bgColor, padding = 0, isDynamicSize = false }: Props) { +function RareSatIcon({ type, size }: Props) { const src = { EPIC: Epic, LEGENDARY: Legendary, @@ -95,14 +77,7 @@ function RareSatIcon({ type, size = 24, bgColor, padding = 0, isDynamicSize = fa if (!src) { return null; } - const backgroundColor = bgColor ? Theme.colors.background[bgColor] : 'transparent'; - return ( - - - {type} - - - ); + return {type}; } export default RareSatIcon; diff --git a/src/app/screens/nftDashboard/rareSatsTabGridItem.tsx b/src/app/screens/nftDashboard/rareSatsTabGridItem.tsx index 68d2e0601..e33b36bba 100644 --- a/src/app/screens/nftDashboard/rareSatsTabGridItem.tsx +++ b/src/app/screens/nftDashboard/rareSatsTabGridItem.tsx @@ -103,8 +103,10 @@ function RareSatsTabGridItem({ bundle, maxItems }: { bundle: Bundle; maxItems: n ); } - // eslint-disable-next-line react/no-array-index-key - return ; + return ( + // eslint-disable-next-line react/no-array-index-key + + ); })} )); diff --git a/src/app/screens/ordinalDetail/index.tsx b/src/app/screens/ordinalDetail/index.tsx index e73cb84ee..f07db5682 100644 --- a/src/app/screens/ordinalDetail/index.tsx +++ b/src/app/screens/ordinalDetail/index.tsx @@ -600,7 +600,7 @@ function OrdinalDetailScreen() { const satributesIcons = showSatributes && ( {ordinalSatributes.map((satribute) => ( - + ))} ); @@ -618,7 +618,7 @@ function OrdinalDetailScreen() { backgroundColor={backgroundColor} isLastItem={index + 1 >= ordinalSatributes.length} > - + {getRareSatsLabelByType(satribute)} diff --git a/src/app/screens/rareSatsBundle/rareSatsBundleGridItem.tsx b/src/app/screens/rareSatsBundle/rareSatsBundleGridItem.tsx index 881e06587..ab57a70ad 100644 --- a/src/app/screens/rareSatsBundle/rareSatsBundleGridItem.tsx +++ b/src/app/screens/rareSatsBundle/rareSatsBundleGridItem.tsx @@ -34,7 +34,7 @@ export function RareSatsBundleGridItem({ item }: { item: BundleSatRange }) { {item.satributes.map((satribute) => ( - + ))} diff --git a/src/assets/img/nftDashboard/rareSats/1Dpali.png b/src/assets/img/nftDashboard/rareSats/1Dpali.png index 3cfef014c343e2e3471b7e0e6ff9d1223d1fafc5..5b6e1776e8e1fe34e885bdee0ea870b028f27ca4 100644 GIT binary patch literal 7944 zcmb6;Ra6v!vb#%ncP$Ol-LP~^mq<5?bl1`fC?O@1(%rql0@Birq)2zy0?+&NzVH2* zJaf*}nfaLcr2SeM2a^&L007{qswn9GqtO2VI_f{%Cv7$Qk6^f|n0NvJq|E;TpsFs@ z**_)FQ&(9IP(4n)|4%@6kkyn00O}I35LPGv0ByFaf~>wD@YoC^gLtk<`quDTSU^gO zqvI%V=ubEUh?2l026!$O0nV@_w$Sc51D%Mg>FYCqoCyUj2+5=wyT}z+$Os7tB2ZgO zc)=3bypI|+2Hxv!bE^WK`qx+D0xZ`P3+rA6tL5zmyDJR{Unc+(DmFQdI!66WLT|Eq6q?WT{5UyrWL5&aQ^E)cN zeD&&8il3IM3UEh*yAyA0LMoB=NP*}^B2F{tUMuv`lm>xjJswG6_6aCDQStzwW6p@yZvhKPFYa@L|ay$M>A@vr_!yGv_C^CsM zjal#1Cs8x;u|+US0j}av%s$T?cFmF}iA=a8;h;PGBMOTwb7P&@F>`-Kb>`F~;;kw= zLhB(a!H9{MDe2-9JAfC+k1}vEMJ=_|_%al@pz)0l^gPB}W4n{F`dj*ukj@JwQ;NzK zGya~Cots6Me6E7jH80ed7s$Sc#?&p7xIzoq;nW@tq=Yz0q8`nfLm%YfDH(O^{mBfW z?$>|!E9`fcPuSL5b)xC`>@_pA9f|5`^@FB+n@0%sNb!|>>_|IyLhI zsX?V{c$FuRU$=wMqZs7G3=rV`;Ka~S_RlP&!u5U#+FMK})8FdInrNOjFr#ZBw#Sxo z%pPLRsG|6~?a+5_X8gX(jTbnsFn2&mWQN53g-*OGc_O>Rek%PB6qWOR$YVSb8!h(F z13=Z}ub1aZD`CU1`bRFyN10yVk||;I-P6AwyCiH`mKn?Y4`*Sz3C(7r!yVx^o4 zapk!vTdow6nz!9zh$6vw@OkG#*T-A&e@*U9!qU`?Ql28Ms}3?H7suNBCO~h;i?X;m z0eQX=vcyqtRBNp(Ch1zKG@z2`RtKx3RSl%IDqQAm3F0eRN*ts`^D5tjBepFdWkKtq zE^^YLo5u^vEFPaC!`rh5eO~pU;&6kpxkuV9Nf*bvL5FbbD3o)cywS;{V=mv`>-srS zAd{9O#z{HLN7`Y;+}i4?JS$4VMWC@qj`HU&=?ik-uf1)TvgG#yjcI1|G&@iz~kpaV-OZQ))ds zd9XR8OVRwzF`?9v9-i@#lWgBiqIOcIu>S9_rQ)@kCB1T6On|-xS?xmTK#3dXp%(-a z>X&P)$MbI%fzMS7Jg1y$`1sFpcSl5Ww_jIsz*z!x4Nn4KGe|i(3J-LcGN*^5I+`k% z?eK}1=_9f~K!(#YAbMTED_88hoKF(E;1rAjRppid{JK>+I4OUl>ga&Is3MODdU^MA zls1l7`dFMe^1P+U;4b_VVwnFbD!^CcC&qdrmVlU`D!NG{0c5cd&gkt)17$Co@jf!wRevBH>uy!fs|5b zsXZ=oDEQnkj6L}1*;sK2fm_@NSA|$yd6xwiW|RY1m5GZ%QMfzq#Y@6ffg|k5nkb`c ze;TB1QR%h@LO&O{QyZeWWKA_MH^|Arpi9(^Db(#>CVBVU_D?t@s*UPpRUvF%eJPnm zFG^$k^CD)oOB-3epVzN3NFYl})PDHr^812BJzHJPP;BZeui@XQstiv1l6(S>Z1Wcl z#&)eJwcA*hqeRnlU?{^D+q11nDPe{kOSku&o!-PJ&W^kJ{uuTmz1p05xshs zA9reu;Hr1;9;gwP8*aewBdH8_bK!gStH$3s%5|rUnM55|Deaq-%e*BEN4Fwcq0b)v zSDZ+l1)CRT3l%gC#{>U7);E7bj85e+RaAjY#cnf1Mz56kgg4YI`z={$>VVsA*B_Uv z+Jqvc?R%~{6Gj+E=T*HE^0X~});t;27zfv^36T_6!Vj;siS3S)CDIUl7kBD*cH!Vh z;Xg>emqv2Po8d15Dl*q~)Xo)yt?V3Rrc$3x?~)Z3aaDlqpLz#10#)KgAy}ldZnRVx zoMrETG=db!*tijp88hc{Z^iq7_<3e8qVC@2P-2qJ8&=vNYd2eptH-UWqf1W?lo+>) z`6qK1H>r;zPMdE1Q*pcobH}@%s3fbly4Gx&KSh;CQgi2|`J(GW)O7EH%Q`7Jf|#Z* zTTzBy;m^*p7`%g^X<}b@vdK4Ful?QMRZ24rSeRzi&%Z@Vpdj|Q+vCd_&%dbb#p~(r zN7wp$@5!1TKe#m-{=0W$crnS7yHMYk(((G<LPp`UK!9!6;@Y3fJb>-UWhDnk}TiiCe6Rq?0HnykZ zZrEkIt5S~0?X*+ynQCqXQj3Xq1rC>NJV67j%YE)rMhw|Yr$zZ*7+7q@mI7kI={Ram3nmVY-?W0 z2E9)G!gO=8_#{P^TqG%GQmk~>rrP{48GxaU!OmcZ0#DPybAmcuFq0M1Yc=kdbDl7;05kC@!+Y zJ?|rU{;dk}+eOmX>13GuqtQ%0g#V()gedYAftiT26#u#djUt7I@QyD7b0U^{6niv@ z?)Ku6ytwhurzT{=EiUV}C(`Zi$s7kXN|7r@Mo0SwC{xnX>@mPn303POE%%EtwhSTM zZYKwopA_%h-)0CxVpU9lOvAp|)7X@IR~tN>b+=JmUVi@XLM5yUx?gMuo05uP<~+B% z8Q1hN6M5J+;Q`8cgBFaVOPxo;#33@*y8gknG7KkE^Uu2*vgYQ{KC)@HJ3xk(h9hYr zsjeZQ$5vGo>m!!z*I^`PQ_zPVYaXO=z1|m zq@2bZxLgstS7^nhIx=;Ct-HD>*U6}~=2W2r{e}~VIbwoFvau>{KI8Td%UlpPInyM3 zhZV(k9JJ*?eYXbkPt%G?cEl$B-46;vvsvYv_Wm}ricI0XtRXX@53{A*PEw_H8dlJO zqJ7uvD)32N4mXV}kh|l@9`es&)M60u$x%3&*{}oONS-*_jAEKmnbbnW+qq_Piw<@# zTcgzyg8BQUj%IBY@!@pRrhzpTyAOWqaPVzO5%SQu6qUCYJIEthQc|peDUEJuVE$T{ zmhM|UD21pJIf7^sSDSI48q-IxYDMa&Mu8vN=jGr^+~SizDUlNLc#p8I(eU-o53Wvo zd@0+`=*S_xA1kbto?y6=hQm$;qr%7FMAFgkv}lUMvX07bGv!@g(&8+rMH*=Fu0pHG zJ7!W=EO?v4FjeLRt2h^o-7$u2euqDeMTkcOA~iqC{8woE!q_S`mtHj!Pl0j#(>KX5 zx|AL(6{Kn6{93v^B)BG5NlL>-qw+{8=jO)?IO-@+_Kjqq+A!ze*P#^ix){6Rdj*#z zW1@KX*%GD`*;W1)IX^BkA_rL%94&g#!X%a7togrDI-@VUzzO@}Pe9E=)sujptPC`Y z0L9KyCZCN{Cajf2)zA!7#*9o~=QTq#Zdw%wecMe~ySl8Qrmg8$wBc5~DAZ zN3iGluvs0-*aRHjXLaG&GGxo%QI}S)%Tp$jqOheLoo)qQLRg`h5oqGy{NO_*VV17+ zT-5lS(-*&ppM)_Imi3$RgW3&4WDL$={SIUpA>2PahjO+o^SMqr{uSy6fJERUk+%nt z7lT{RMat9{h2%Lako(>CIe{3=X1~(JIc~yfSXeyauUM@x701BWM$VeA!kKFNL#}x= zcTih+l%tHNXhuNJm#_lG7V0MZFuODIA(}~vK5TQfGkm&0&~#*D`OP2A>syA~H$WPw zCCBzYIYH?|z958>K3hdm*85^`fd^?_$K}?Eh99^6m-$KVIRnQKvQ29#Tb7XiOT&ir zynDcx3E$I9r!SB6nkrk8w9xSVHavaT^1f=Hc5(|n+Cl;=kYN;OBRbV-%sTrnqjl!H z8lwGY<2a5X>0eAi;APG{5m8x^u0KY|27KWo`N$;EO=;?^h!1zt7m1y z@wOl9!@OPY&oKacGxe%R_%ILG@F7ZndYup_kJ48CWwX}e$Y68bFD_CNR&jk`nyU>_ zr1)Z3^r<-LTRx$G#cq{F)Den$0xSE41P8ab+IY&>g8ow8GyqF) zSwbOv@W!2gi!*Xd2mRxWy4z(27Mi`ge1=&M9f*KZ;jybjGoT93^H9T3NQQ|Y-!K2Y z9xN3UFTk=HkalsrU6^nqjy2VpX7|KXqYiI?G_cM?gPAn^yio?=0NzYz$py{P0?nm< z$e?`x9;_8X6kcIid+?M;?U+42G;8O`TT5)pN;D;#4yYq*d2f1oJjBu*k#Bn@*F9Cc zftrkv?6#e(#&jFR^$|ve@Z^aKDjQzlc9wNYx)sZZ+qWP;Y6*vvnM%|eGB5g`@eb8v z+bHA8D*XiU&`lcxXoEWrzrUI0_g+sj%hNxOHqPsuK+zS<^QO8-iev0BTZF*+aFwja zM|7CV+Mb8Oj3!XhlZOl(h>1sgYx=x95sR!{@T%LJG>~RS^|ACcAem5Vzdxq;HXwN8 z`W{l7F(+FPt2A=#;uC|B8zJ_awr4!c+p0<~$@Xls>6h5@Ic0aKQ3YuAJ^$bD6n`_w zjgh0dIDx)FT5d-m8Z<(L{01z8h_+S51g#RL-ZFP++^r-R;FR7m z*Sj;6mE8=?i_r{gx92$$Gpy;D$H&1JBy4tj(HA%8g?X!S;|&B7q^0csJrPyX>G+YV z;K3+jNvN}_5%$1O9&UIL|CS=aZj_5L{rlSwrSBXKUu+imGZZjOb|p5#ttCY5@Tei{ zEd|Tbm;|2H2<=zBaV^2a_SMu$&oCK63L|JNp2?R%q|^NPhj%o)RrWqIc%mAZ)K%`i zn+J<=Z&3!(JguWz_^WaAk+%HcgQlUxu(7wvlcf60vP$^Ow9V@g-y%tv$I|Vo(pYQ$t(ay*hhG&IH31sBOtmNZw|6B ztY__>#niI#r*~Ks2_GbvcCBCxb%;;+e zL9bot1=i}f@*Q-Hf323f15tC7UbC>5JNf#<&W?)lHn+^yd+NS6W%lmkD@ZS_qdtCF z=q;E)pyY(^3+<&JN0dorT2J^}@{V@Wa7SS?7GP@Y^ssf+SUA#fG_vuQbYpqcZPQ84 zRW>91?k+8P2jVVV1@UPw@+N+7Om{1Jf*4D)g)%$D)c4AK!?EmKdN(GS23#R+42zdo z8wEUFVS@`YSpKs(4%3K6j;?0;Fn-oj;OQs#=)%X)i)!Sulf`bQ*bDtSrTy7s2GdH~4$&4eyM- zdJl89hGJYNT@a_mEmnRxXjF0<1McDjp!-BwTR~K~zS+!G0%!6pGZlTc00=A`Yw6O2qDr^E&5 zk{|n$?Rtp7+=Uo7;*6%H81B4 z?}rE_8b73#xJs&+-$b-n*hh0X)!o*~-9`d{I4<=jKZ$MxX@HHSWo+FB`vr(%pBjsy z02A6f-8gK(joCyCiW0LI{5HkIhjoi{kcMSqPcQC<)ebmA3)6_T@zD5s10%tH-QAC+ zq*b?5kv`~#qzeHm;9zZ>E1^Y11o$c|?yc32RY8Z}hncM(p;Eo+4QhB6$jcvgEK#s0 zf$sCr))g#QKKwkn-$Yh-uO=Da`xaBhaYuwfun>Poizhos=^fRE=9x}1nLX-*$JQR&(g>c&H1GT4Q<|J-}+QX2>K*UQ` z8$%Zy^6T6_d%x$ZbNouh@&Fp9D1T_(`Y^yWR{U}@%z2JA-sBNN`XCW9K_#4F@i51U zKWB7N8-{tq`{DK_L`N*go@c77_p5pqs4S%Xa45ykHT-F)Lw_+`kGLQD9@uMI}k`&xj8LpTO@qYYu3(>fq}-7_IC=-xcqw%_C)w z>w4IUyYR8&b*bMBQeMIECHBObjz;T0Sy<`K+-<6nZmlrkZ&vsT_i=sdjxgON#^jNY zPI7-`Y+s8ZASQjTdnxu}z)}A6OE4?;kpbX(mWiFZw#fx`Jyeaf4#){)~C*Hu`p$R=u zK5~LUAG+szv`%1jT8S5k{JqIUs(0}wjf3za$B@FXrt?l!dnh)#twg-wlh$ms!}qMl zda#pie}eM78) zEjZ#PfeRc~s7Q|%5o!_@$6Z})Af-+jW>BQYhQnQHtnH90Upw40ph_&Pby{k4puJa6h6?GoSdo}iaxEes^wVPkRWC!Xr?A}7 zf&Tu%Q<#epE~ z7rfU~HGLQFi17+4vRrZ8oAyEY`|RudtrF*dyBVtIjbo*?@!lCKJ%oL8xQf-Bg-zx4 zsvyv$@dEqD6kRq&<6-StYQY>v;d7JUJwU5a(_i^Z%P(qRA0+SvQtn7Gudp~84L;2ulAOySU@mW0Ek=clt=!(Uc=9f@G zWYk$$_8d{T^{QIBx*QT&FSne8U2b|iDmOmD02NpxI%hIx!0#uhCC(>J`1J$Ich81+ zc@J7HWN@#2i>P6(a9jm7L5EePcHjqKkg=<)C4tLB>0LW3F=56>PybqxS8rRxagwGc^fOsGSsQY*RpHLa7K<+}1NtcRRaN74(9-~ zYX&oluF}5bX)_5>iu11jM!&Qii*s5sw%0UV_A4%oPrnCWy{)n>J!{3O{jCfzoc?ql-%-N36fX2u9 zq$rDKW0uz?{8#lvd#UPqY#kif)GH+84G&vLI>(O=8;Xp(3e|PXMet@D?3+0@w?Z9` zZmYl$J!=U74HQ=g`gwc%LbGN8kp$}0tI?=6N>z%_vJGS5v25jh*9u6zzl=nlIw*C2 z5an{~SFEh8Y3*rqn>!`@mX#9oL}bd{w+fg*=X@S6dMk@JD{xJOTvLvs8g9A!rSdKn zzM$mMNA6E7j{Yx6YD2x}H@P*>&OyP!O<&YwLt&wG(#30xO&QPJHzCfqpT_LN-h2+> zH{5bq;gw6&KFi&DVO>}YKKA^C7(aWkSamD^(}!1*jVSdXP}=E+6nI37s}$mXHeBdw z-#vfT5rj>6)b*RWopLKjDfdHR*(kHvN`2XbT$J{|TcYgWx+Ez!jXh9Vh=rB!&Yp_(#*5h!>0?GAto0 z3~y(gsLB?(q8!2xF+5kUc)H^I{;Bg_ERpbHi$=8nq5Y2U9q4pKLxuV% zlMpWLR=8F@=f!LcD{0}jNc@%CCDmM;etySUNHD$Fk@)-spYxV>vi7NC@#LoZPvp7} z=U_bNEq)cy9`!3e-`uZGo1T^54Hu!3j26H1xX&BDQJ(vfZhCr^&*5LHQ)f^nlQ1Qv zk8T9!qU+^y4!gEU1{`07*Ce&KVIQ{1ta-Lhutsv57#;Wl#RUzv%8PQX1t_8d_*fBsPZPDQ{A#OdEu z#v#O^$6L!`NFM0zQf7nqKfQbZ-`JM?1#gU9G}PbKPUYX@8lbB9TA^CbGW`Dl1&{A_ literal 3481 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 Hu0mjfMg3`3 diff --git a/src/assets/img/nftDashboard/rareSats/1stx.png b/src/assets/img/nftDashboard/rareSats/1stx.png index 88e50cef67b1949d11303f7216fab9a9064f298f..53ddf0f9b3eba5982bf8c482abd8ed8b39cfdeb7 100644 GIT binary patch literal 3022 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 E0{|Y46#xJL delta 1583 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 Npp1%it)yAV{{!kWk>CIT literal 3530 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 Ef>#)*^Z)<= diff --git a/src/assets/img/nftDashboard/rareSats/3Dpali.png b/src/assets/img/nftDashboard/rareSats/3Dpali.png index 0aa4ef900f56e73bdf0533344e5e1b78b37d3b70..0143b38d31c27aeff660d3acbab61779d22e2410 100644 GIT binary patch literal 8246 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 literal 3553 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 bx{H4S*%kV?0>$Y+00000NkvXXu0mjfOCiH| diff --git a/src/assets/img/nftDashboard/rareSats/alpha.png b/src/assets/img/nftDashboard/rareSats/alpha.png index 67a43fc9e61945459bc78a9ee5b6ae5f27442692..37ac62c8d944b1d9756ebcd74268257c0adb85a6 100644 GIT binary patch literal 3074 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 pmMe8Lzb)XA^8cak|B!fW(KmQpYhDQH9_l7PVw zgkX&FXeok$#y~<5Y6wWwKujco2$&5PQG6675mQO9(pt?LK@HN@@-PN4(PHGO6e327 zlu!z`rR{chj%SzHh&xNW?at0d@0Uzx=AJuuXa4)X=N3>X6bgkxp?^>)6bgkxq4*y| zNCKFB8j23xg-(nBv;h(lfTlG7t~2EO@^X*T$SD{LL%Rnq*HQZVgE}~bqk{6f+;M=u z1TgdEcCba1puAx%uDZA54{rte{nsqhnY^$>a%E}whG0U+5~?ZN4hL{VP+pTe3_2Bq zF-LCoZXam$0H^w2f`7)F@bJCG(nm(i`}u?kk?PD7z~>sI5k1!N|gymLYHe7ZOSE@Jji+LB#3( z?=nqCYp{lM7d{4b8VK+#i&*C|BK-pPA>kxUP+pt6TuvJikAGUhbe{LnK5{UYGNT)XG;7O-#bCFu7FT=3BsX1|7jvxQ+25T&T0kkMi6Y>9WRHB-V@+ z^L;pk?VS=A&l!L#ZAawtZVPj}9Pgmv@>(jXwJcG*cG@U3r0+mo`Z)9iLM81ln*oQg zzf)>z3w0WiVt*1^K(v@%u38mM%y6`#np3Fr=7G2hJ%RW$IEbx+@|s!MGCo`p#k_*n zS5}}0(1D7}NWC&!F2C<4D#J~+a1eV-s2*G_80Ao;@AV^!ZY<+oDH8 zBbw_24aNvm7W#RHhYtW2;$|#qZ-;}}C#cR-EJ6d&^-}bO1LgJ7E?ajzCUwv}e-I90 zXB#@%BJ4-pG4urb0Nv<#b|Oi!MNnRu+fPozBMmr7%i=JDaq$sR_BxUfdrD|JXG&Sh zu(14ucz$6Os^{s=O<-NSqQG&Swz^s8&k3#ut%<*d(Z)ZWU?-Iew*zsS!zt zJ%R=ewl>6ZEr!|A-aH!i2+G#uM3)}SLcAb~Us`d*9zjhOL8&7moM;5%0FQgxEtxh9 zNr*jy8lD#U?8s6|7>Rf=xM`4N&TU9S>=E>}FMnF3)A-H8GJ%)R@Sv{*QYT3p+V^y9 zz!pJ?q(Tx~cywqiGH4X~iplv%QtX_|A4bTp{!f@SXvS>x0nS-GM6zp&rIBO~C7x9{ zhTd>JJC8w|WHeI&L`PZicylU&>pC7~q)&Szzr`x%5Vlgn`gpjt-}6 z6_hqq|1PKZtV|;6o9xSblDT`&E9Wm7gjVenn0TO@sDxl}JRHP+H}b}rS-5T-m&?d@ zk0}5$H=)!07QMRLa-Pq=oUy1++lso3iGSz`Wd4BtA3YdeH~l+}oCOcT(4Lj%rbCXc zw70xFXJ4;L$Y!w{8y)m&>lLW*TU-QbB`|2ObUNqY^`@aSFkV)aWOzi6qtWBS$rzeO z!n<$>w@CfVLO?H&j9c0@ZVj!Y>O7Td%D;mRBy9IxxXonwP-zGeE1}(Y{}zn+UpEJ|<>wX9R>_pqumHxw2iZk0WNtwxLzR1PwXQ zsHjqUeSS*rdha{ec&DtpgsSYZu&+J^g;Bkr(a<@jJpr+h@L2_|uP%q_nIyu7#;CEZ zX8A4IF$k7M2)RAap*JREh#f}!H1E|MmCuFUn*ZZc}Jx=&mRx3PsPKRyQ=NsfP7 z2+zL{50_qksN4>e!+>GdBdhf^tvc8oQ(DvJW+Ip}T0SjXGRuIbpGIc;DJ$~(lX8V$ op-?Ck3WY+UP$(1%h2lTOUtAXK(`P|`K>z>%07*qoM6N<$g6?(ucmMzZ diff --git a/src/assets/img/nftDashboard/rareSats/b286.png b/src/assets/img/nftDashboard/rareSats/b286.png index b910ff6a75cb5bb4e8a15756bdb2892b565dec2f..22ff7c4e609f9508d8d4c981b29bb797a9464a69 100644 GIT binary patch literal 4128 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 diff --git a/src/assets/img/nftDashboard/rareSats/b78.png b/src/assets/img/nftDashboard/rareSats/b78.png index 12bddf606ea6251c76be76e09034bd9178775a22..9ca84a1babfeb9a8ba0e89e204467cd888b3667f 100644 GIT binary patch literal 3553 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 diff --git a/src/assets/img/nftDashboard/rareSats/b9.png b/src/assets/img/nftDashboard/rareSats/b9.png index 87e2d3e3be58280e17146b805b1eb3de0093af5b..9736d7e31260d14d07e2ea7b48744a0630f0f621 100644 GIT binary patch literal 2983 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 G^uGWx;+m%b delta 1465 zcmV;q1xEU(7sU%UiBL{Q4GJ0x0000DNk~Le000110000(2nGNE00vF+-v9sr32;bR za{vGf6951U69E94oEVWdAAbc}Nkl^QR0J;9T1Y&K=Kd~f>$C@iJI{?>WoUf zb@KlTdvS*uof)^gYoLBmbj@^iqra~H>Z|G=B85VsP$(1%g+igYAb*0wfcg3PIJetf zH90xynwy*Z&ThA#ljKM*c4A`U9c)_9&dzdC=dZ@1qod19OG|$ziIG<9=;-LJsHmu~ zP<@R*Lo8j06+}ct^y9lv%gV|YNJ69&3!$kNi{yKBU1 z{DSk`f#5&LWK0zcQN@ti2pMl)_qPNnmhs?x?bv<>!5d@}rifixSxMO3+`JE|Rge%J z$T2~P72w?4*gRNRSZFmPJu_77;NW0XQBhGjs`V&&!5kZ`*ndm>9T^$~nuUF_J{ST1@@6RPjg4R{V>ng0-azCn3} z9=u9A5=lBTGBQ#HsZ(@Asn3x_+%YvMzjbtUl#-6V3~1Lq4U zZ0}n@rL?=dD+>Qz2nVDmx{B2}K0YR=)5$%B z^78U1F){JV_CrHM;(}CGR#J6!HBC)T5e6PQJw2t^*jP$SOQVd83~`|f3JR#WxcJKR z9UL5x%jKezlM^xuL&R$A@9)#h%#0AiJvb&N#%H_w`hR-D^j3(Sot>rS<>hDgudlB^ zt>@?G`xVO#=bDg=bS8ZX=zF9OGrq#a=?=TDaCS}Feuz1%GnCMfK%Cj|oxi4>;}~gwBz{n3f3gE1j64 zR-#%<0ld-~q}adEz8XS*B9kyZ!*)0vsd$L^$;C^^3I5szA{OUx^DFmg`_Sy)f=M%V zLHsR3{CxyDulw46omd?AHP#NA>gwwLB?*vjPT1ey{{|$sqFQ+Q`zyr$ij?ji)_M(w zNg|}2YgJ{>kUSz(5vOweF+(!6mW$Le>{} z(Ad@7-0UJr35y>o_xASk1f$LAbh;2nYiet24NspG3WY+UP$(1%g+ifFC~m_4H_ws% T*^62_00000NkvXXu0mjf$wSBE diff --git a/src/assets/img/nftDashboard/rareSats/b9450.png b/src/assets/img/nftDashboard/rareSats/b9450.png index 322ac1493b030ce96c181b80f04b1f0448d3cd1d..a2ad12b393873f9629448f7a7b24c05c87f882df 100644 GIT binary patch literal 3371 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 diff --git a/src/assets/img/nftDashboard/rareSats/black_epic.png b/src/assets/img/nftDashboard/rareSats/black_epic.png new file mode 100644 index 0000000000000000000000000000000000000000..d43578eb50722f2a5164dec75e1c44ee5a6d849a GIT binary patch literal 1831 zcmcgt{X5fpAOCFoZf5408F6MqN1WlDbDuJyZAK|iM{dhg%qXYBMxG|dHW3j*C7n*X z?n-$|nujn)SX?;9>7?|qR_fNnFx`>bj_&pA`2)^%y;2>FT^bU!Qy*i6 z0RZUx?_z{%Rq%yqly(k#vnI5no4P9^9RQre7sC8Q?NwS6mL9s34%()yUuzfWAT5vv zz>P{h4GReXs^6bM3**5S&r^c!B8&tRlT%Wn0SulNQ6-=zZ4;pF%VMe2fq($Fn-mix zPM2EG)}A`J*@#FaLeHy;H^B$XMo#p&rY$wg`OfpO`8!YklD};VKGdo6{go%Hk~dE5 zXn-Ad`y0G3OC(;1k{XEdQ%cHTpI+Z>T$M_tt3^dc2W)qP5+a;%Bri{Cwyki>mMzl{ z9yl&r89?w@Cnu*NYRLrw`ZIDMF!`dz0CIUWdHPgs?VmJX=;=X_cHU4fmp?yl==;>- z6zo^q1Y4qr`#r$%NUD^0HHu4(HmLtL>KigSQ&-nxuyNf55vSmCI0MJ;sRJRm1E&bi z2!=7P^4^aT5pziIVP*^nU(3$UejC5q^&2kl93*9DeY(L``Jzf%l#7}jn=Eauo@QYE z?t`?jHO{kh6}QK*M$H}FK5$Eg?&fT)U!s8FoZIboi|s~2Jh-M@RD_{QhOSo7)GmY4 zaXGpC4PN{^;I?XmP*jQE+X`Br6_u5^jEl_1K1=|OPPyB8wXk0sqDC7$5w9ZW`ieL< zg&kN$cp|}dSuQCY8lqoqY7&)W6FV=ztDv0)WHNcDC1%PK<-Kj2#o@z;TON(6KPb(sU#F`2Bugjd?58JwNPDVLXXorY#+j01v$ zhnJSxI?OJKQSKCqaAIQ5*!Z|;axx%QFE0WcDvtS3~c74LV3D%S=wjy12Mdb;LO~3Z-)Lp{iOC zMe6@SH4|4A&SVn3yu4VHADyCCdfsKy`z%BT1_mvsn9Z9Au*OOGd)ZAutZ|kRB zVoi!|8!kF$r8E}`&a8p%HskV`_LiG?CdO+Dc;CzpG5UE)zs7!^bio0A6O?B z973T8NAIuUD4W7clULX~lP{0Q6P!J3xya<(`$_E3!7Y=ZS2)@wk)(vFj5`Xf$2EF- zRBN2T{Tq|SFx==E($?S~4a!Zyj+0T$0Chvs2p;wofzVo6MzFR%dhsIh z=Yc90MY&*QB@J(5(^(u{chs0*QIMXFSXfv%4UPTrhefgoTAsBO|S@0;cBi zW9ycd7R~1$C@-PLY7a`vBw36DcfC*>xBfhG#caE6y|8P=YoTy0p zn%eSjf?l`pU<13iJ2(1pHF{k6vhFc&W;eikPswvq?v0W{kM{O@IFiXljKQ!9VQ|KO zFZ_~oDQfF9U7%LyHaCq*_BNmq2t-Io2v}WR?P@%^w73`{)Y`A=^lLE!XPd%`=3#Ps z)T^bXE*IU-&d%#uSPtZEl{e6_H8njc%hSD{Uf62KaV=H&D(q-0XWof(z2X>;vN8z}jo~M}{wlar(a2mX^YgA9EaT@}(3!E>p%W zD=TA#P=T2#rl?-p7MM&^;gvG;D~fh>{!T8JYvIWF2;E6_*LdNv8xEX!q7mFs7lNxk z{_miwj=hpzU~xv^%IEXv8}KhJZB0(STeE-ZA_29uj_J8hofg3u4#Xe`TqTTY~ht8vS6f@K0}5Dp!-MA zF#Dh~g$P83tz}Jf4RW!+Ea9z{aB-7zF-}`sBc=2iA+{kgk*mW-R@sy2B6w2N+@sq( zm_7G3UQQDDBlml*`+?H)^qC5fuh&144d32{rZ~k(@BJLQ^qnOzX*v`4z*S8`V z??NxG(@|~%9o-0BnEedN(@x;3ExaldDrq~4NHs#Lein}Q(|vq=()aG&i^dnT{*F79 Z8pOFX%|SO8C=~5q1OC22j5d0l=>J?z4~YN( literal 0 HcmV?d00001 diff --git a/src/assets/img/nftDashboard/rareSats/black_epic.svg b/src/assets/img/nftDashboard/rareSats/black_epic.svg deleted file mode 100644 index 3225f7ef7..000000000 --- a/src/assets/img/nftDashboard/rareSats/black_epic.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/src/assets/img/nftDashboard/rareSats/black_legendary.png b/src/assets/img/nftDashboard/rareSats/black_legendary.png new file mode 100644 index 0000000000000000000000000000000000000000..f6803f11f4efba4572e0fd23bf52a75e100a8cbb GIT binary patch literal 2249 zcmZ`*c{J4P8-I<#v>0QlNn{3Nm+jIJjWI(=WBVyuY?Uin!l*3M7))eOa%C3{Ng<^q z#!Om7qR3X3#$MMl*}hknJDuO(_dVx%p7TEEeb47P&z|ULZ!Nxk?{)xyxUG$)vp_O_ z6GBwLTL|9m0Vy1@-~#uxpjZI#D1FDew+H|T z4_iwMmvEukAv*_|6Eb28g7WNZ|_=Su0D{=-(6 z$7rw+eEr9e*AJ#yrio51B#LyZ9xAn3U9&Ym+R3U-tpC{R*ZWb)i*^U~I4S*8p3j1Y zsFm?H8mwE+NB)bkFJL=eG_yVznMW%G1O)}@W*r%q+AO$NSy@?od3JDdeA}~OE{4cA zzq)2_)qtlcSh@I7V0uN#l32nVe*hPq@0KiC$mm^St6C(_iben}XMp`K(a z=Wi$G@~lHS-`_tq^oBp)u>oq& z)-7*}TK#lW9U(23QYYb+Oo!L#pMpbbrs3-vG%7FYxXjZus$6yGP-1a zAF6+YHtRe&VTX7i0$xktL!M?RzOQkjadR%K25WLOEUssv+-5}1^L&(FkyWGRewU)SzkeysG|3nvSE z1Z;MjSF_x|oZcDJDo?D1ZX54<1Tn<5T3T9^xu zp-@MX%lEaI{6#749BVP9Bd2~neR@E%RvpgYXI1UljASc_jCJJUsh;VZ1(4R7tb#}i zo|IGoYnup_i`LsKQp2GxO*>7YBaeEc7X>kW2S;zMzfMMLox7L8?72{vOSvzn>q5Svk~p4nV|2a-@Q(`(9^7-ZRH)76B|2!TY@s5o!SUXjdTj5J4M@D0u< zY2&WXq2?PX{xxi&L=PlP=k{nFH+z2P)UzO`jAF?6Cnc#f9x6s@5w52JNkZ(~a2%9v zzLDigxdl@th6j$Y3ux$FBf%@4d4+{@yv5Bu+iIl%g1ms~C`3AQZ5a5DUEi-n}p&AFGJuB4n9fVPS^#^B23zDC2TFSBVQn=&j<1ytrim5{eu8Nx}2OO`}Pw`(-evUg~(V&5rK&`HkB3zG%}@LlN%g z6A&1vl6@$0sTB(H(b?43p=W8gYXgqro>7$7;p{cK@dL>RC+jZEC7!P0&f^IL`*{16 zrKKe&Q3G+XAQzTNc2td18B37BhziwQmGE+QC?c?YI}0*U zr(T=yp;(qcxc;xUn$G~kMNET8gQ*QDFUP0Z08&h+w!an&UKlQI*Xf| zql1L4c6&62OP-#SB~h&d;(Yhon<*VOfU9a|)yhh$`@;U7@k0Y^ zUlPIWj+t$Oe~d@AJPoR0>kbXrwd4SXfbtp4|?z<+q zRN#OHYK#x{B7znJmwM*)c&w5X(Fi!z+E>opD9rh>OC46gB9G#~OW2Vn=V$ z4aR%c=u*Q4Fdh`7JB482_WMy8Kq74j~)^^k(Glii2EJ z+Vj4NlT@_|5*&Pr=U{<5?O}AD+0?T9sK1%Yy@dq-bF#Unjcu+hn7RvKkh>_m$qbd@ z9cY~;Ckq)Jp!ndG>`=tgA@+lLHK$gzN% zpb*i_E_zk69R!Q6K`rkh*iHA6B(BYf;J8=gzBr9wzl+GQo@e%y>;#q_%1END&?pZx x-hPPxnLJTXPC&YwM6Cqi|7)86f6PZrM$2zz@Mzc9Hw2ppu(h(ctRi{E{tKAH2q^#n literal 0 HcmV?d00001 diff --git a/src/assets/img/nftDashboard/rareSats/black_legendary.svg b/src/assets/img/nftDashboard/rareSats/black_legendary.svg deleted file mode 100644 index 2f8d70c5f..000000000 --- a/src/assets/img/nftDashboard/rareSats/black_legendary.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/src/assets/img/nftDashboard/rareSats/black_rare.png b/src/assets/img/nftDashboard/rareSats/black_rare.png new file mode 100644 index 0000000000000000000000000000000000000000..13ffd6eafcf58d719f8327ef36a32725b2cf784d GIT binary patch literal 1182 zcmY+EYfMuI6vxlK6u1?zRKb+TpuH+lln5%TqFgC$v7k8UBn+2Hi@aQ1sxn5=t6mC& zf_)G)&cUK}xTQLI=*R*hj6!Td<^oK7OyrR}v`i6hFyT5uSHErlsvM5KG{3Kf&Cfd8o{i3(sg4l$5~p6*3p$jt|0`?jwr$%wTU*(a z1qGWT5{YZ=GEE8f^zE!8-$v`sA`jXA_KPQXY`z;Pxe5CN#x5I#X`nG^hUC>@ zRFOfWlvm>v&&4udf{M0_*W%a*T^@AZEWgahQo$rUv3QRGnhiU|nLw?Po!kJ46~axL zZn5xok* zW{nfKa48ij^+)6&ql0R(5zmx@g}!X%g^L&aNn(S4iflZF`jeiSnQ5}0(rOh!HtUw* zEOMlFdU|^JK>xdpaA!jOV4uwFwWt`;28?A_ zcFAGXE)Kv2+o6fBbMn0dsd*;(FU-S>$|09W>m;ZuXiR3X6vH*TE}y^IhdMznU#rVS zWC3HcN=qUP+Qod_8WMMp@j1>(bJG)@YLjIjWZ1>m_1Q_3m&tb>7arCbyk$>%5+x@}8R6T>F zhklls`AtJZgVOeDa&nR^KCZmqJh+Z5Dw@wJE-j)CPCZ&&Jdr_MKdRM!@-Q|ww&88~ z)YG}w4{rQuta)+srQ^$(#b0i{Y1_*RT01%-+S}W6G@7R7&m-UVO-&t4zu&?fuff?J zGqiL=Xj0X9rZ+9sj3xQmLf6yMrp#Ub$OCrZ%}mX2{AMFB?d17f19gP7E`#hUs*tp< zfNVWgOj-j$W^$d!8Uiwdu$vuN1$sZpS>{p(_=C&G-)r;LQ@?N5p2=CoO zw0gt7gz7@_a1gZURe5=Ng-FusMu+EoMZ(?OpBoo$zPPf^e>*{(OBylr{2caSlLf6} zN}i4@zQ^+Io>%J+aB)jusB@|Lt6<~_8&^!;>cQ4HJnukPDyM~Z`vS&==-*693 - - - - - - diff --git a/src/assets/img/nftDashboard/rareSats/black_uncommon.png b/src/assets/img/nftDashboard/rareSats/black_uncommon.png new file mode 100644 index 0000000000000000000000000000000000000000..abcd9aef51ce01951e2dcaba90bfc144ed376c19 GIT binary patch literal 1719 zcmZ`)dsvbO7k}Y{O+lNO^5$hzA(MH*Y~`gAjL>2_Gp`v=6kW~>T2{{Hjp#Gc`8^~> z)|V4qr77M=uT3cp<#T;H@68lbF?+{ zFs2=+2J1EZ0~>szmtCaTUI6$cmqtAjnh8^t`Mak0oArS6)LFrPyIX6YGdU%@4t(WT zXjFwnlCAD4ZK2cY_&K{N!6IVbfy0hWj;VX%_!F1{7a{M3_^pKU}hp>r` zdm}$IENrN&>tD}03yuDLpEMo&&o zZfVpT8@C%88)>GC@}4?#c9BX{Q=<^gzPFj4oXos~)Vl~XE4yK?**Q77G{!3#nM`(| zwKzFB#l?Raj`Dde5w7djhZu7jO?aDaT|)x|<@6ndaYuzE4|62T7cXA47k0F@;b?U~ zJGKM@OiUg>{%zhvtx*EdkKh;2t!`LHUJZi>C9X0D1{TQN-d_I&PE$?Ivo_}#0T4PN zv9z?LJnr|PfBzq#yO2R7%0Yx&T3zkHe+t#$k`cDUEjOmO`!xC54(04DXLoaLP9F;P z;01}e^$Rtxq`m!+APb(HQmOvXVmAM?5UqeB!%plAiegN@K{{NmM+p{`rm@`j{|7R~MBO<^9?(TfVnA9=@f}m}G!)sjN(Xx~EyZF13H)?fBMn*=c=6UGC z;^N(@spz62gBuK$N<}8XOfhWk_HTqNvas34#%AQ{Q`mUmp6i!(QW6pp?yVO(4?4AU z?o0G*2T(&DHkvhAegftZ)!!d&Y;5f7)pIH}Ir*n@67H98f!Mpkku8Oy^5Fu>xy&$= zZh%Lqvo`G@4&xrC9&Qk#Rg*eeTdP+RChD%VB8jF)M?W1C2n2ZsV&D95EA8_IY;|Vf znn)yyb|G~|rY0s9{;#Jek;PJpN|S_+!#81>ZO1ZaJ4jheb8~YnwbwgX2|@FqqoZRK zs(6k~!uWhCpYJ8P_JV7dAx=;)cKKXe!H8~YiCEv*7^m)Fw_X_^A0L2_ELB`{0(%{5 zRL*Y*lc7M5D8ddSBMIv>>!v){PErHnd#}Rt-~(P&SrPM z5eJyd{tJ7dc{w%J-&}vRmIso)`DxCPEf=W=X01{x%sDI9=BMKEEn% pZa(7e?d_#rPJ{pR?f)3b?8{qh_|7q- - - - - - - - diff --git a/src/assets/img/nftDashboard/rareSats/common.svg b/src/assets/img/nftDashboard/rareSats/common.svg deleted file mode 100644 index 4aa839085..000000000 --- a/src/assets/img/nftDashboard/rareSats/common.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/assets/img/nftDashboard/rareSats/epic.png b/src/assets/img/nftDashboard/rareSats/epic.png new file mode 100644 index 0000000000000000000000000000000000000000..53b332633335e6c9786f9cfc588996de84e4671f GIT binary patch literal 1966 zcmc(g=|9wq0>*zo%M7zPM3S+jQX-9YWF`zM>y3zUG?uZ9vJN@OJ~K>~8{(8T%;^-4 zvLrMyj8QrzOHLzGq5P)m`vkod2qUAV6Z!&p+HB<8pKnR#bEW(gW+QU$!3R8xt!kd6|C zd8BPlZv$EsdB!M9X*+UJgcm$T}JKgwm+1FbF;?`ljXI%zwYM_;TU?@^8G)x6Ba}3J5sI%Bx*5y4gU{ zDJHu`yp$X^6;#;md8=lG%@8M@i%s~JXA&@x0wPc%vt*y1!u130BPNuk{@Lr#V%*EL zT78Vmy8HqWKgbp@AZn$FY45+^`JJHOf7_;o_%_?a4^2 zk;Yvx?jEaq3iwe3Nf=D!Q@2gA=nE!IX_$9EIom zF0_}$f5`6f>|oC!1t~2?v@f&Msm}Byhl=@7$0ywU3~eh|^AiZQ`9YgPei<~!0iG9K zp|u3*RXjPu$5wl8I|b5@sBS#Cqh|kZ0<3+$hff%H?a8iFJaoffG@&nD#hcoPx(dbnRJRkD4a`VDf4A+>U6?XH4(9VCUGsTIh1n&i0F36{yrM9F{oiI z(K+$&-x3ILskUa-(#;bt*e2L*K@HS41kVa1hZ)}GHnxUDtX!L$X+D~0 zfHN(_Z!P6}%RvzXFKYL|)h45%{GR^HyHiJ(#w3n47EmjCZu53EFV>EoE}7N8E}S6x zm@6bI*{wV5m6X!JV4WfU>#IA#m=bc>npnAhi|jRo!WzMw9sxE^6z;txCFP9EtSvtsUn*jssL0xMd-D;rmQs!$)tOP8_~|+*g@eax9v0o1tn6 z#YO`xffuHHDs%aJg$iAkD2`_mq}O4TN(JJJxr?o?Yg8#v z;a=Zb`)1!ybY~(I&*YU?l7Q#vR<0cAcm*fHlLF9rY(+*G7U+Gq3;yWGsr2aNy+4r-aUR&@tHGcgP2@*PnFZ>Vb}xhvi%#9H&%*+ZiCk zmgZ;!ygNHwP5MI{f7=$r(7T#^{bZcsX+D_2zUCNj4%HFvA4r3aze1P>fNSA5p0p+a z^Y^VYe#mhLP8(0&(Tg<;Tilr}K}-A(yZ%G5$8$;Pnpp*^Xy(0ryaF~CE}gHj@TUC@ D-j!>U literal 0 HcmV?d00001 diff --git a/src/assets/img/nftDashboard/rareSats/epic.svg b/src/assets/img/nftDashboard/rareSats/epic.svg deleted file mode 100644 index 01c2310ea..000000000 --- a/src/assets/img/nftDashboard/rareSats/epic.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/src/assets/img/nftDashboard/rareSats/fibonacci.png b/src/assets/img/nftDashboard/rareSats/fibonacci.png index ec82d87ab11c19f6230daca866df0e3ed45a800d..90b90f3f668fb0ae694e48d1a5346eed4d1f93b4 100644 GIT binary patch literal 3977 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 delta 1150 zcmV-^1cCd%4U7pkiBL{Q4GJ0x0000DNk~Le0000~0000$2nGNE0E8@1OaK4?32;bR za{vGf6951U69E94oEVWdAAbZPNkl7%Q6vzLwySCX7Hz8?ds0x7;i34y0 z1PH+iqy|xmL%Ea#$6hK9aDWT4I3O-aNF1u1P(i9V1UMkX1u5YpC|BVbC>*XMcYswdG~{T>B!G)_4$K)JO2(u z^x^!9_c>xBl@8tg*{;!$aCI_@;ItaE3_ ze=O$uAMf3$c+YzkPp+vxf|_hWh$xI?0?wi0A!Ch}G=fL}Opg8Z1vH>Nu~)M+lp@36 z`kyn4!Llsawk2O}*pA(JUq5a%m5Z1V-wMVmrOODpX3<-@H5*jlkG-eVL z`cPK|uRU@ODQkC^qlrcN?o5Cl*GEU1!wYYH9Mv#{5r0FwZx1AzHf3Fo_l}h1&Uy<~ zjTCu){w#)me;Cut+O{)qn?!}m+#3D42&q(SWny1u9#8ho;g^5!$LN{6O*afVYPfC#LpJGFjdSpdmYC@Z6b|;TUaey zSS&J3oJ-+C(MmikNLHx)b2Yi;8Hrn8)0CIFi+}gv>pu?Sx7i%3!Bszf5^X`ELJJiO zZ+*50)v&cYFXifUazNU-1vKDl|AyhU0<3A)f0l&nyE%N=%9W;#7+7eu{2 z7(e>?)QJK#p*8FtI&?haX(e@KLuilFx@A2!bF8f*=TjAc&pGCG25m5(Gwd Qwg3PC07*qoM6N<$f)zp&%K!iX diff --git a/src/assets/img/nftDashboard/rareSats/jpeg.png b/src/assets/img/nftDashboard/rareSats/jpeg.png index 4867cf2ecb19f1ddc307db49bfac927fb32faefa..d9e990d11bad2f7c779f0f4e64a659c983969b5b 100644 GIT binary patch literal 2910 zcmai0_ct318%-pry|>mXYSgMxO6^sR8nH)=(%P|Uty&36t(Kap85B`UY_)6C+A1+h z%^0s)hdxtZE|_IWo# z2G_Ch1pt`&|4X2*(cQh95a?^9tp=zX;oP_};1{Y8RREyo9mRz`F#te*sH>q04Fc}` z3U=o-yG^=B_)HLqTUiK1K}3>t(D%{zxs6m4zz~j@Tb4{hjBjpzN>L#KLkl96BWV%B zKMk1TQ8~KCMEr)n56iMa2A1(p3fKIWAJ$AK-G04uRMon^7gqaVarVgl7!#O#6rwyi zur(`}y>s5|wb*j9c3(x~-yq#1jqQhPv)8o249N0&cRfjM#E7(U2%!~x0bU=KgrPWeQ^dYn^4_jyPcq>K;5c<4tKYyodRZf z59=)0nTP9L;;J-1K@+V4H&4)wSlrqEOeXt@hjxoMnbw^k=6!iWCp0+)n`Ch8!^AEI z?ZJ53Bm^*tbqZ+c`$^|q1h4JI$6KNTs$7lyKDiJa(FXQn#hFP~ymvxJ$=cfhVx53> z)bTdVaPm;=IMV;{EtloXTU4YCDhXgoi!)AM$FW_`xaC=!CR<>J9mhk8XZ?t*!|VPU z`1|rWH)yKb-f6Re@3}au)!&B#j$!W&n|VLuu+}=$bcq~+azp!DqhEhejs z4r0ZN*6r!o{PwB$rx=ou5J&K2-k)1rpr}|R{je>B&-rjf1}dgBNtU?Ik3V+20(F)x zB@V+9@U1vfCN5GbA4c0N`)(08gYXNX!f=*X?hZV|Q8g!Cy)(HugA`US6Npr(IE7-?HgT8{8#@bOTtpTh7xW7D|%MLTT?by`s(7V!7)| zKt(^4l)m^;fiSEzS+>$6fQlVBP6fs)NR+2wN)gA%U?=U|@}3{A2Rmlecs7EhfiOu> z)=^I{rfmx#rZFL=ms)Q@1o<5;`FLd!WxU@4Wcn8tjBivov8=i;``{NvDN?QQj! z0?WFI$d>4k%@qdIlw(h(e9n{I6e zYFC{2G>7gxpsDUra4FZZsp%^vQI=Tvh@DA$(rW@-ETksDwk`!@1s+MeM+ccW7`i>O zDE@`cWm`7#hD79ZDY!*XbLAJn*+MZQ##&vJ2TQEGc1XQ$8Q_)vweB;6cMeZy>b*lL&{$S{nW?fjgjD#BED!p#WCwJL)lS%rQsTV~N#+1M=&4g8 zu;fcGz}UhuqU?X`g$L;i*M=$y_PylbHyC+CoO$wZZ8+!z{1EfX`5AQ@MMdRCpI8>S zPXPS?j#A@Ar07_Yyt|&+TlIR|?)5xXR{Yb|5S4fwWx1mF8YvmNgw`_;JgoTE+QmnC`g9ss^{dRwiHq$c9;7(&E!XGM7u^$x zHPe|{wF2G!Jk{W!iRZMfXiKH8XZ2J-ABu$XyVl zvf)PLQypjhKl!*3XNHGiU%OT&+Ky)O3a5^nzn0Wr@aJ@j43qo?6l|N3N=bIaq%IPX zc`USeO^)b8i1UM^Du>h#+x(5pS1|d}^BY!;%zWdbJR}drj|HM}OWqs&Xsv+8;~i&V zxgL?{?;nr^eYyMG$A=$BlwD@(u6h7|Ixs)~#pv(}Ls$9m&I|s75RsJak!EMlb(jYy zE?(`v=B|ffaE6ReY(cJZ{2f@UQ;n8^@e6IZ&%adUEZ zZ6q5=11i-#pW~SN-*FOWrF=fhE%7xXCz<&^OwS%GCvx(S z+Doy`tLa2)c{BL42&GdhXW@-j%xK0KhCys8PoVp9?!inoO}X-JXnAawe5v|`e!bSd m2C=P)U_m$u-M@lq?YcqcnYoFwi|)-k4A9lo*Qipni~J9nd32fp delta 1537 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 u^dGX0Z7#f#AG(!U@nz-zUt(Iop@2 - - - - - - - - - diff --git a/src/assets/img/nftDashboard/rareSats/mythic.png b/src/assets/img/nftDashboard/rareSats/mythic.png new file mode 100644 index 0000000000000000000000000000000000000000..3ea283903be992f4c9c56036ea8224e6028fe014 GIT binary patch literal 7355 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 - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/nftDashboard/rareSats/nakamoto.png b/src/assets/img/nftDashboard/rareSats/nakamoto.png index 4cb74d0ba550bba3b30ea92ec6228c5dec5bc6c3..283977e495ab455f25e8aabc1b24b98c986f882e 100644 GIT binary patch literal 2774 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 Ya|tUNkm&&CDgXcg07*qoM6N<$g2G9{K>z>% diff --git a/src/assets/img/nftDashboard/rareSats/namepali.png b/src/assets/img/nftDashboard/rareSats/namepali.png index d813dc4fcb73d8932125ba7a0ff724cb227e1385..1e54f0ebf960159dc75a9aaec1619ffa18a931ce 100644 GIT binary patch literal 7349 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 YKUvh@M5Chq`!WJFRrOSmO13fo1LK=1zW@LL literal 3504 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 a1@(4AWP1=Jmj5zfQD~_>z?7hE2>$^K=9@tP diff --git a/src/assets/img/nftDashboard/rareSats/new_feature.svg b/src/assets/img/nftDashboard/rareSats/new_feature.svg deleted file mode 100644 index fa4493f88..000000000 --- a/src/assets/img/nftDashboard/rareSats/new_feature.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/assets/img/nftDashboard/rareSats/omega.png b/src/assets/img/nftDashboard/rareSats/omega.png index 0e4c6068fc57dd25b7e21990ac5a86de5e550854..1b4f77d5abf5acc82b3e62bfbccc46a5a2a3553e 100644 GIT binary patch literal 2265 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 Zz5`wop@`D*HKYIl002ovPDHLkV1j&XH{SpN diff --git a/src/assets/img/nftDashboard/rareSats/pali.png b/src/assets/img/nftDashboard/rareSats/pali.png index 2ceb76888930f67a0bed90f2e39c74f8e4e174db..19008faaf6c3e1648a10cee6bfe869a317d20487 100644 GIT binary patch literal 4990 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 F`5)68eSQD{ delta 2509 zcmV;;2{QKnCeRZ$iBL{Q4GJ0x0000DNk~Le0001D0000?2nGNE0G0z{NB{r;32;bR za{vGf6951U69E94oEVWdAAbpDNkl92wK!8FNNKrk!+-hfgVI9Z4SAJ2HNBp6}9ZRE?_z+ zP@|V9ZBwL30y72DOYD%2UVQCN{(txoN%66ww7W(yA3*GKW_CFH|9|s8W_Af6kw_#G zi9{liNF)-8L?V$$B$BO%fsKU5#{9X}T!;f46a}1p!Wd66#uonY_LVq1V9HHw) zd=S6x!EyUVg~@NdapP(XY{XVYoIE-*%Q=Lg?hmsRXJ#C4`tqB9xi$wb^8B&UAdX`a zC(0lP?yuK|F^K>1`hWZo*o1A02nEOdSx+u$v3UYGzhL*Qt7ZJ@;~|vrB%TJWwD)xC zy|KBu6xe_U4#MIZT&%g z=o~;F-mt1y)iV!Th<`UIj}8lgkW$D}Fn%06ua&Fs$U}AaX=Npd@tEdiuo>GG z5q-8dj(>-;Nh>NtW$57TX`u~tAsWqI6d;OX>PJVP374ukp(Q-3Xu-3}aCN089a(FSYFOV-gec2IEr z-WiG+{_OJIx3J`pC^t~ZI(v6|YB~m9@MAI%gAM^fijo}$DyC}4ZC{x%eoX?YJ z4I(xa>e!TmZ42?GrXRdxAG#oNMXKI9X;jUxkRo6MH{mlva5w)ZWiOY z5y!>e-4AFBJ^uOU#uUlcze9;ApghO+90?ZViBO^sgR$Px{(O67)bEIloUsTH8h=kf zn-pBiTKrH@&}0=1lyEn$fa#-fb&|?NY)}f>P3nzq zwB3h+c2GU&c61E7bgu3_0=|I(;nG@VLzciNiQo{9DMHbJwzb<1d?ZvwQ$X`-(7D40 zeb2oB`mR8^Ufb~y^dJ8*T7eIApMNgeZc-5`rkEGNAsl-!6n&;Q5x$l@ggdZdOgZ=9 z6khj8Qt@a3tium_d;EZc!dL0IUZ{EUQG6}zFjWKAvlG{UzN&Vd+L$Kbngh6D;;C6G zOBtE``WcT|61M$>CJv1m9Kz8jbj2GB*Os6AJI5zgE#^1oWIS(;4+S-uk|?p4?fLJ*pLv6OcqmF z6!zk0V0;*_s#CxDeSnT$@PG8+W}#Q#NVg=2=*v&C2)Kl67SWCp$R%Z$(uMQv^7{If z`{I~eS`}?K)Hk}!bGP{{xP&W;z?cwfM+uoPmJ5NFSgn(Q70akiy0WcxLNqpT0ap}( z3$C*pX-@*pEUw_ndTo~TI*Q=vuFB`6xKta;U~mdtz#Z4btz}1|DSrx*0JJ!i1S-^?znAF9LHtC5Y=QV}WVj;*7`Z)m2@CN}vEveK&=HD=$R5wvND5 zQM`^>StjDaN(NWRnZ_4pmce-cYWb_KYkMIBshpB8zwoRc94ovR(*W8A(L25MsuGu; zWVXFl*$nqq)*Px{*b-KN?xU#^=(44?8IQ3c9z8#ih)zwUaDPh0%Wdx6Y1#*^%vY0( zVARAO!!R7%0T>I?6b~%;@XGjL^0gPg=zkXF^VI)a_aoLNE0A3* zF8(-N`0Z0#3|zw1EspS8Nx!KxtN)=8sj2Z^$%V7T?_WG!79h8zetV>; zd#c5$CvlB%b$`<@dZIUlcOSRfo~PEVf%~?tb0dr?g}owLXQVy_>+mgk(RnktzWd4; zx4h9tZi(UoroTh4{o;qQ+WO!9`t%%E`5|mG$L}S;DvG+X?G`(5)f`KN=aV6mv*sIa zm@VbspPzW`#h=8$0zbX@dPqBXVz!lg!qj)-)sfy9xPQz8p$H=U=VZ`i>}JCToL_k5 z+1@GG06)Ky3^QQUjp`N7hn#r9*b1(@rV>IRDD1j#d$geh;?>dKC|z5pDuoYDBM*ua zhzG_ZYQg8*0muV=7AUC0CfLfpBoc{4B9TZW5{X12kw_#Gi9{liNF)-8L?V$$Y~f21 Xxv*OXGY+he00000NkvXXu0mjfB2UU1 diff --git a/src/assets/img/nftDashboard/rareSats/paliblock.png b/src/assets/img/nftDashboard/rareSats/paliblock.png index 83db6e0d5b68e43a73a1f5866eedfa21cbda01ee..8bd5c6fc620364c4c32296e04a555dc89b78d564 100644 GIT binary patch literal 6028 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 D4F8(g literal 3318 zcmVjzP)iYTIpB8n)Y_-R7GFh^S26um}oTC7PNCA+OH9T?`|FibIO+O#-Zlk*`u z!N&l39BmT7ZuxDMf?dNoWn4~ z@EP)q$PRgpw%)vv19v~QB-6%ewOZVM>&4SFFB&-6@EgQ*%XyMNV<7~gFG?N z>>t{T@$j0J$jlg8kwbwg_P^VA;f=T6M^#mAzg={d{~`BX+i{`19AQS-2)$@F+-oxA z(Si0vGMk$gEx?mctVViz8bS?5?2euL$hlQDWQp}`3u0w@Ao$w z9hTosX0!4hijhBlXkRIqVz+*=1OH&=RZ<$Xhk_8tZQ|YTM>{&Ix)CCTG_lvkr)+^g zPAX(h%FV&1Pu|C^lm;1%iBJQ~}o0d5FtW{hkWW2^e<^_##p@Y!gI=~YN&63$jQh_H7Lh$npl#u5q4V-s!o;Q z=-$1sSS%PnDVGfRhpD#WbQw1McasTh0#HcZ&FZ+8G!`b{V?Af{%UgLNOvmOPPoCA&s1r-P6)-#YwHo|9d zV_HEW#%5*FP1E4Q`6h%5VNs~jXv8f`ZdTe11_Nfzo&_=UI&@{sg-_O!YKU?g_MFxL zE`2h%(KBFeu0r{NuP}iWlwdFn`CTj){_}$mv46)NaMK@vko!w;#;d@ITEE{=|GM;1 z&|Q^{q!=$oq@{!B{g-RAcB7%TrnjfiDwIbAz??QeI~`gbhlCUjV&eLLEvFv=q3|(u zn0|)0pIeJ|bDPp$d#*}RVE~O}YF>(c61mQnPd1_WL^Z@4UZfi`oyMBB?>NSr-Xubo zKsSx@d67&oQiuql46&k&M2)1xgV;m?`XrGqyuT;%xwpWUaU{lTRz9W!(|ZXKBdIt0GKdiVtK^VO%t{LzJ2CeL=q7rlwDr3_F~E+ zl3Ml2qN3J5T8R-pdp<5`#^crJU&65yr4(u0D;(Da|>@w{KHOifO!1j#Eau}_$KH!S;K$DjW2 zJkqGR{K(4X*mJNLJ1Hrjo|y*Yf@i_SB`bX>s?_t)#`Bj{s$}N!J~twXh!Uz52A(!+ zbUefu4lcU{H*GQv9Fa)PZ7f6axmxVnz8gi~mcbM&!jQKJ+=%hY=R^&s+z?(fa5^n} zT`UzqBoVP8&!FQ6zc`NP$V3Ds{%I&2aHb5H@^3(Px(VOaHo&CQU}8oFgo%qG5P@+N zgIUNJj7$dEX+xwDQH2_{lA=2`ZW&G@bVBVxMQJHq48&`eA~x9o4U4;% zF~^fE-yzb7s6zErAkB(*D>J767%xiNNPpPI3lu|RDCIF|VVszS_?e59{X$|YQsys) zJ|&J6tRWK;6jcX42RSI5&}+G;6W+GSIy^%}70NK(&Vqm4rdKz1#Sl!5zW)xPAbrO5otu6PzD_- zRY&}lI8CVY>LD?e4RD;t=76X-KtEwRgm@!7b|tsuIg^Bhgwk0^ZSsy zh&ak4>nh6OBXTQFmMF^^vZeXb&ge$4tiVBwg0_-9P!p;V%2b*e=Nj|`1bZv8#*RX& zDM5MfblBmv*_0U&Insci@wGQWa)fM&{RcrcLK)I@UQ8Q7c_!E2u46bK7G9T$xij(+ zqq(?BBY9kKTg(F%jU`g9yYdj!hAK0qU`#Z);E^~~S~YO{IAz{mnBqcmT+D#SvpDOn zFM(E{1U_*jWRDY)t)0lEB8uAqwGkFV*|KnTbq%t!vo7oS$wsHL1#~)8A&C*^Jo%-+ zLVbnxPBt-UZwucO|m z9m-2jqNm3m)Q42N4O*LY1uzMt$-ZUFOg|HX2ewt*sTsm^bfwLq*#{UY4Gva97~YHR-dat_1Oy8 zY}Vk(f|FjpO53O1L?pt5@VPt@OBv%fdbKSvHTQb5?-%KH0VB5P;2|*8UN`MptX};9 zva-kF$AS@AQ(cL(r%U?{ZKg;H?w{y**V+p$Rbl@{1;~{5Isx^v;1I}4g z*VDb|+STTbS5uKiM**S0z`&r(%Smhe=LP>M3<=@CnPh1}@}D8Q$?1E-z!1>=KLO-5 zXs`Y|0z5TjrC_S3NRR(pfVPs#k}xoJ5Y$(5co-PcMtNyTEkD3{9%7J|_QUXW@TIam zeI^zXRX8POCucRR&V? zW(;6aiq3Dge|T=@VPKh3N5yD`^_HVlhkpA!N!x72t z)n?;qHd0wfP@5TL&>dl2Hrv%%{4%H6Z|EqbWIu(75HQTee<^}M+Pwu^8TpL@w7M^m z>|G7(Rk`?Ocg<$s%eji|W#NVFlMPs5Q`@D~-icbc5CnFkRFD;SARw!=A3bm;4BN$L zlVE4b7iimj#__K!n!(Ok>Qa&x@KsWvm*7;`V;y8;UBCGkE7PiM zeXIo}o?KiSEPPP5M6l1bt$!7eFFd8*hNN@$$37jv1Fh&H`S1ICFY(qdH-M18*~G}| zn9`VR0<3zqJ#bn@;XAPE+HwY`b_~XUkc;$V5CJ8J->YWO4*}Il1uztYdh$moULrbL z$|Rl)g&7aBnJUe2mS0KM>9(F6+_Md9B_hVTn?G*-z-spl{M*pw?Cqvo<0h9>O02eU+PEBo3v)kC{1zDwZ-&7qYuLiJ7m< z?G^9QZVi4Z?19Gnr}G3OFTF^*V`SrYbVWQ6EZL5f##>J$Y}s2BLvMK}kFF+v!RJ9| z4IGQ9xpg=?Lyr(l=k~KVELdPma<(X6=Nsg3LZ9t=J)u@e^8Mv50zwigZb zkK4r|eB3TrmT&$AWag(Ei!`aTazTNplt>lDkWNGo(Pp9rf*RdgZB+T9&ruf)TEN^hLG@ju zretnm_|Wt>KDY`7%xO7A5;Ur?10{ROUv9$MBUk}{^jD|i>kf@>p{F+TRuMD27r;E0qo_*f~5wL~b_@6p^c z_8bllcSS$cIKUT=2j=ds#V#OZF7j??@5k?-d)vu$g%lUkthy?U;`URsyD{(?4?2;W zb2fouBk``1aV-}>0o%KgR<5=UH@t-i;bJ|7fk%{U2C=+v)(r6otkO$DkikUdV8WcN z#XNBV93DSJvpm5E(^v&tNjQlW7xr)~C<&#)GXho|JTCi8;e0-hryn0+W?~SpgdDHmwc>7ZbOpXmz%O6K_3P|u=WaAA ztD6*_r4_~2Pm3`9ZBfrA0xDOm)KdBV)2amwhMjDl__(vW9c8ve9|PZaNsr^drH%Y=f&Qjxy&APw;B)k)_D@H!-?>_p(SS07x8}AGhw?lfSQ|Z^!tKCtWB^yEFHhrWXptgoPf%*v-O%v5|^(!i2|ST zOrykiEE&$PRDTE&CK8hROHnAb(n&QUWH5PNKUp%_+HHaDI9Hl`-nJ$`l?^FVaPBGq zki&*;&q}pNC7#@@!*ysqu=XSWy^j!zs;Ym!C!-YR+nvHJbwMY)!ieCS^#0bs;q5_0 z%f}qq&aEQRoGeqYxUhj(wFawdw!sA(5e1YSp#KcE0x6ztESW&AznZXmYB!7gk=llB zD=YTr6G(H6JPRUOKGSOWm&pi{Zw*}fb!Q!^xob-Hv0wwybI)vWI6m30xGbV36Z{x` z7QDD!vZ_8#SOhOWD;{*8=jga-4*hZc>h^%*c-({sho&Uw*-c=1BR?=7ype33vcm2n zw3Lo%Tq~6=pC41~VDvnq2?+uZ>vtH`2M8p;uLnOjD*pX43VNn%xU|+o~5~1@M{(kT=k1#|_DiL{syjS}W zl#DD9QE?D*RqNW6iiIFByud)BN+P)p@@Y_^)=3LZX>fqKKB%!(6x%SYa#$Xe|0>g8 zWOo0gw0{bX1q97+5FnNmfg z{)rVb|$aNIwdpnwsE%> zWgo1Jj*mqo&7?E;Z-H~=za!g;)^N4=%3r*0=T0)ee^tee=psH#`lM*0DI6($%+U?r z7^pOm#HUW3=KM~y1v0%~E60mw2z0;t0bvXAs%=Ni5)yH?aY=QC%ohCwdBG;VDGPLn z{67&xCD3+KCe(H#7b8MoiQFi-XBm zOqh;91I@3;2j%bBp{#Q4X`KAIAz>+9EDg=iq{Y?pwwB^V3qspZ2K}4Ip7$So&3r#7 zsYkQg?JmsuLSNz{x z!wi?0Lun!gQ()Q>UdKke_3-_I9I@%g?Wbm{o2oT+L$DiM3?)^i=Q%u79aMu=6nve4 z&6%W)>IBQ_FQ!|CSvBLN#BgsbCom*9qSarQyYxBSwXCloU#~X(JPw$O(xP`r!j_7( ze+u5%{teQn|4oL=1OBfa>63Icz?;bns59`p+#>l46UWI*R}mBN(NEfJi5`x%{o-ym zU)!I)PG@CxKx5NBD+R@Krk&VOw6IchTyr`l-xepR9-jHR!|@TdA5)(5eGXA-@Ui0< z97bw`;qQRXmdMf%VoD7PuH30d&@?IZs@}esIvrqSDcT@g>BmI%aqU+`1f^s0<#eq2UAUI4hvYnZ*!*R?GZ_K78ii`TH3wBO@nkg^0ogG7~+Lrf#M5-m^LMzow3PqbfMkQ*L#=e%*gpY$74YHJSQwHF0V?~ z_Sd1Ld$DMQhGvl0K%R7lQ}Sd`L!|r(2d*hd#_y}xAkP%Pz$-5v9R*b+*LfqWTxb?P z`1jXM*j@r=8!@-Mjx5oZ-8u zyZ^Z%@=&6)_w-|W!8llUGFDHnb7Ozy=G>{x4lo*s;3C!uGC(OIFmS13BDbbi@?wUl zCSbQc%m~X22_*`Nv-Qe@hP`T7y#qkg3!v8^DFaq<^seMbr&98`Il@?*oukVu&3S$< zs+Fo_*MHSR$P(7WEw?Qmp-p{7%DRjjzU0FArBps5^!D#$sWWvljY)i{%zCc`2YC5j z;BVjNu8Az1ViA7!!jBXnUam%VdyxQ9zZzOOs~(i*xwIEZ$9(d|tDDPDqU#;AF@x#^ z?AOI=MvcfhMIl9e_PURr!$Di|4YLSIYw&069y+T}{Iva`uX_5fMxljz%n+|X>7&=V zUF7kk7d?!T2+L59D2;MP^=Yw>v-dcUf(&^BzhsZ}xKRuqlvS`4(J6&wGi`qb9Jz36 zI*ci=Uq|!QgUAl|CU+8e(6(241YFQ1nwe$eaFSLNOBL1CagxP_aELbgvDrVKVen-i z*z)yr%|Hb%by^6Fz7j86=JF}EEQ<^fQqYVRBg(|TweL^GVLa*h)X% z6994PY03ClsBEl6r z-TQTA%rU0b_c!S#eQc%hXpHT1DLhkvH!^q z?S!>4keth15Ef}`-NksM6N{PD|2oJM1%UI?4uTa3x}a-iYMnI5!DrC19OZ4>qipf? z>CZ5EXmAqLt~2IBsVV8zZ?eLtC3t*Zu~k16&{dx#T}oZI0xVV>n@Z7%eUYOWr(NeP zla71rz=^J~Q{RA(PKwET(xs&4Qv$oc_P!)MhgE_V16+*Y zsc3ns%=qOX_q&|zEqJOkW+G<7`57W=QI?zcSg9h2U=?Ya{< zKbFrkO1)Xnc=m6@XbOzsG>@gQ__gquXZNJb5s@6E@9ao*x0C}>EC_dmO3rJE?Tu6g znR>j16X&U^=}k=hLzQtm4-%MxY{iL7(59?$iz>%aS1K<2#dCSBZDKMonrj_#1ssKE z{Sfuobf%UqU30z*lq?zD)ft8iYe7_-Ae|)N7yyn-Y1?Y+;nG3bh z1)V9EvfZ2~Yvl81c{6rL{HVy+Y1BDle>x=jkyyLJ?zsMyK%lEL9&$dXk9iu4+BWF2 z*#4w{daaZq!FBvxb{dkz6UD;>*jWt4G_DC1wKd{6wXJyAxIFPOjws(CJ$s>Ol*+69 z0!W-zVAF0?t3dqt^9Q}Txn!tK$tp`pCwc-z#9~uXAv5NYd7)Y$zh_E0Uw)(P4Onr$ zk28)(c=RO^eU8HbA%nk!-A!m}a;|%D15p&$bO^uPH={G|VBkHpj$KQ6c}ItoGGw`B z^<>Mibne9wPU24`s+uBdJcw-o=}x#xxnvo7q!UN2v}r@g;7((-tHOWk=2rxEa{WT& z55wz(`7dA?7R6!5VFugDry9A(d##WcVtw1(BdmXVl17Xc-FxFB_g0Vt;`vVq)v64DtvyB4 zM`7O(aGNmTH3KkC;GoCFi!QM7mGr7E$rY4I-vg=|=4U&O#e?o=R$5QCL&U8#{ z3@PR9hQB0Pa)Up4K@Bu2nyRVI;A{~qI>-(PG!7ePH%S^73l*zt@@E;Xv|mzsQ-0oS zLJJVm36|uue-k+^zvq2|I~AN5v+EjDU$m81duBrJ=owd!(pL4uA5VU41P&n=aAsZt zX*>D;BohQuVvRXy*|1qO9#oGxtAsqFBy>20x1sUYM6NuYB^$2cdaabIzDs84o~#M9BUwjdV80Z#u{t{lo^s~nag$Xk zXKtw)kZ~4vkMMx+^p5p0Y36`t5Qmq@925^nAT|SRy9cxWlZv-)$dV93MbYVlpk zKfV6*AJg_~gi7#+^rEPowy0ycAq85jta?-H>-t>+{@f{XhzzNl=@8i}^YIj&>8{vn z&u`P{FM;32T`X`$8M#8*V}H=?nzF|3Jiw{VaJ#)j1@>XA4Bt)-!=hQ{0(XCd(gQn8 z!ob)c`Q(;$wu0nwQQgca?2#ra+Phsj-P+#iBOy-T8hcJ}Fo0}fW+CJtWON1KRAlKkSmpuVv7-pUwyV4Q|Em48xYiNz0FV8A9gOY&bh(rRy3c(&QRIn9&S>?|88d~(7ke`*y=-{{lpDzVRCcowWY2GE9 zt$2V`2Og(P=SV*Zt;5b$Yjtw))c6X>ubEcJYdZEtywbG|_?Qi}{{wA7-7t1q*>t*M zkh0`1HbGabC`Xv}IS~XgTQZ5VjxQ8*j&F}@m`8=#pE(d(;2@t7r8ndQTu`2N=J`t- zdY-&QkK{pjsI|9%)1C_5kK0IiX(2%W_b<28*v33^u0#erqB6g0t+2+kwar#O#He1B zjykRcsNTEnNEAIMIS*FD*yzY$_hnD9*Q89j?$1*`9*1Kj&Xv1D#$sn=#hJF4OK0~Y zsBTo!Zg$-5qJWd|mm8giLV+b(!T9j;>uEGuQOYb}%EX<3xsumkia~dMGxUB^%%P7@ zgX4|w^|l`U35t3S7HmrS#S@Vv?pA?h2Y&8);q-vC9Ftr>)lY(wK{oq=G(U~PN*2ge7EyzZSzvVw;vKS z!BRpd$PAxkAd@bbeE!6>&A7EUnMw92@^uj`p(+8+qv5A80d6WNYEWqwMlS4!Az+9h z)`&G4LH&&7Q>IVbg@A!t!Mmo-v(be$Bzx$TiCksi~DFnh20)zk+WD%4i0tt&XC@25$1Y z9XmF|9p;V;_+)D8bCOK|oY+`|u{~|jDu(ypcjM{n%TjiUQ)~E8OHP2$MT1;G_|$quf4tFTI<^Jgl<` z1g^Y0BA#2If&#Y%ow*^nwf=BH0Z;>vJu-z22b{3?lut4l_y^wIzWo~rgHLjEjYG(o z9^m`_5Oky}oEzH`)Z7=rd3+)1Qg%F?x;VH)WTd6Vd%X1h$9r1MBa+RP;_-NH$_GRw zv=As1ff=tG2^H^}@!7;acIe;MWbjGa`oCrlFhLj$F)Xlv7(Gsfh|zGY6A!MG@3wx+VGZ} zN}) zaaf%y?p(n|q(rU!=}_VMZ6(+35nQKbo3>wBy?V7cWy%z}u0CwuO!?8HiRw8qV9x5b z-l&rW@y)DaQKL&&1<_=+xT(*ZWe~NGR&LvM7(yT<3q*JOoy#ZfkrZv3qR|I!My_|P zjsI1YnLMEZdC%$4PCYRsb=Tw(LwiKZpCnimzSn{uX$NR8=f9HvQ^%9$~+^lP3YauDkQ{Op?wn`Vw#FTVv67%vmL%X zY=x~^9&k&V$f?i_LLej;p8D{x)@5#T@cFdJ*u>bnH7|B)@L{$azC9(KHSsX!?d{tS zE?u~ECP%meLP#Vp@H?{he%iNxd()P2_gDijvHYYlAAUZ-1T4Un*&7h}FS{L(dtJFV zvBw~Xs;ccMp$+kC#xrByjEz%ArS%N6b2w(Z>caq(-!|TgX=v z)6%BRoIi7ks;MnCO&hG5)M8no!@9LF1YWqAc!W6X>=a8En`2+hftUNVHi6dzsfwgr z#`;F0;aain4d{v)7J>V6?n99YPxZDzgDT?ro6v9)6eDmJoRJm+3{11T2KKUz4Mn^g z-3W;-U;LP#`E+*utxIQ!Is zpT5i9ZnKbun7Y{c2H13?I>l2_fW5c^{~ilufLoP$HCVORsU5#0&$(8t%(s|HQMXp6R?m%%257)l zwodR`z*x`md7wxOvA%r})#N;h&-D7+J(FpI;&Uk=2>pN)p8-v4fj(E+K6B?QD$MzN zq4(4!`J5!tCehJCX7708OXGT4tt?*qVP3=1Aqz{D@V7v!DL{SzvE*Kov_*mt3270j zs6KgviyqBbiPrW~a4l>$@odAHjDvuHZVGl62h#^fa7+GKy2z_g)>cGPvYkRx90iYp z@d6VqAYg#jc&xx<7h2#8cn*9OC~6yM(CiVs%3yW)@8K>33W*2%Lcyb@3dx;)#`8s` z?Mx=1?HUTNJ=NdFiRN-oVZs@2VlNu~TR7tRhYsPQ3LAhZv+&AZ>vPse0|^{aIMJK| zrpHGKi&vh5yaEYiNhV-4qi|wfEm-hK$2LaMv-6+E3b8P}j7MZ}J6s@}W|P3GLLoW0 z=2xhFyqV0H*qDeS(?Wv~DFwTEJ%mZvUm_%B)maoN>!t+-%zP~9s`far$5>%-ipd{# z8Wf_nlojJd+-#zWfL6E?Fa8A8#jB2jb(Iu>y-@bgU;iXbvtn`fB4g3|@Ks)f5JvVD zX}iJ!VG_OtGALPufBHdi1d)K_6fjsVV88&wRjJzNfxc~w(Bu9HFtIpv0SDZ)2+b2y z8KeEbt-@^NQBY?;FjPoxjK5_7mt+{y!~p%`M6kER$&)?k1!&C1@5~`I{fmt1UU-1RnIHNGHrgj0pXxVBic>@C8z#nr(Cgw$Om%fS~&>j2V z8I1wWRC@=yzVkiY0`;<;MGNM}fFkV1%j6mlWgMsrt*r=`%ESIemHDm#IvD(UR|Nw8 zHlksO#;R`@VhD=_uF6C+;e0Nv*IQZY3|iMInTqF*7O7LRA z6Ye~&ChGyW=b78pKvM(nxl5R!-+}Y$0dN)ttWHyFmOoiO5rk~QG^8--855d?h}zI# zrhjYeHn}AMH*em|%{I14toO+pqhhf)JQpX}eENKa4K z;Mj{ijq z+5!Bvv&M5)buQD3mSR5m1eyDOF)j$#%ipciE>`X-t)&6jk6$_^aGQiIU=ksc<~5FY z=MHGTXU`rUB2ISIi2693vY@DB1{SFdILz{tYOZrbBu?AMPUH_CWTAz%2M-+uC3JUC zSwc!dw+vO5ViF;K5$USnAdu!;j!ZGIQe({(C~XwJO!Nz*tD|md9f7pBx}7 zlc636X%(i0V!(QNoL_l0?sbn$)R(X>>AGcs;l2!{pf_fk%0goKHZ0*uzj;ftGy@GQe+5|}+ zv?8Kn#--)tcszS+t;C^yN$y)W2>!>=)srpIMg1JVfBV$(Si*Fv zFX{C5A*d%pa^d9U#x6qEdpYX-07#p!{Ws6_n8r>WJ9exH+@$@AeK;j}pH$1QFeut! zF8mCTDQnzdq>F1jpt!e)TUGTbp1-|N3tpzw+L#a*F8!QVaPk=<8KFlf{suHI9bG;ihj|)|#>ph{twOjPVE>q= zdEFm8Lupw5kJYJ0P7XDIj*gCwj*gCwj*gCwj*gCwj*gCwj!r222eB}@pPjnb8vp?{%Obe*h1!7HD|LZT>0fCCGwskzR2N zV2E!+51RMm$RP8eS zE7^X3M21WKCJ#GrW~9(=#cOka60JP}oYgO}k7TfBJ4VSpnTQ+!U~2x)~oyQE8Km{ConW+U0- z#h3#|L2g=GaWq_zBipLVE+2`#DVeh4#fnUIGb~DRBv9J)DP#N#@6FubA zANY;h;N)kiJL*_c#!G&3&r%;$UtFR0-vtyP>EBC4MK=m=-SZ&MezeogB-1kl!d$Z@ zeR6c9xH<@mA?@1jS+0PV@`!%VcsTKiAmR-GqhF!Joh0(^RWO~r zA#<#er!364@xkc{k6NL7A$V2lOR>7l;0WfbR*w_QBI*|1lXecr-eJg_y94g&N-4DVm9{(Sj>qjaP=9QfU%S~;uzEVE9}1nPuf)u428=cxAP*e-pIGzA_Qpj>qr**_8c zQ;wQ=hf}*^sv&kMN-)fk94cA5TVgzIQqcXn(PVL@P<48X!*2rxu)R(#^Ro5CoY0L# zCx9;7{RHO*adKUDIC=>R#b$ltfE#lsW`1a)wwnfTH)A&0YLj^7>EiUe3bE8Q<2q)Yyi9DX3zNhw? zq?}P^33Hp8lDw@VEV$i9yEPcVR=7V=#j?M+&l8gqKBFG`+Iepq*moN5^d5CRH@&f6 z3&s$24}Ar#?#~$NB|7w6wt3y{OjISg&`1yVrfXqc)ySniZf++mV2I-OB;#Ty?~C_) zfUR-IhoV2MRIebYNn-!fvqIH@FRN#Jv6&rjk}g?pBaZk|R-rWU z)5J(w_1rn0$FA5OS3l*FyW2k@Bx{ADirI)`fqc@9{?F&U;$iFY($~D+?`Oee$&}4k zYJ7^0kRLU18eooy#Vyy)>(qIM&0q43gd5E~m~+1qK$nrwq}!yK=44O|vteDugZD0R0Gi3m*U<#2s5B8?ALlg!j1guL>HF&!W2x$8mq z*L4#L|2ll)p5e+-g+Fg5@MiQtEG?{+FRtD$n`TgUiP%H;`^ad8C~9P;FQ*_OvRlc# z8Bhtg?I7J~PP^*ezhmbTf=iHt=c8|FTdiyl&l{w(k@a^aRDyK6*nU7aQV%2Yl_04d zc(+bCmIFh8dgtbpFc&C;f{v1~&Y+DWq9xNNX|XNRZ(faUQ2LGg-$I(x(+&S;rTl=} zx9!~xOsn|6SKU*QH*j%Od5303aCTWf2aiA>Zl2=7p1SbUGA!*Lm&{G##@w*{kGTr2 z7s@{~dRJu$)~|eE%Th^Bk6VY7CT^Yd$Go*3%iRs!9jKO7?OSU4iFX^!b>7jCcLdpx zx@egS^`8{|nH6`hiGVX{w=32~P)0Dj>)6uZ8?%6q(6}{SiB^d!R)Y3LR|mTS4VCze zF|y{^*5Ja0>9m-pz325GFc$shwK-qNtdvLx4u9C}-3Wg>UF4Z-wt(IXa26moT?sVI z1OXEqI(J{B4zk~sXrOD|7a;rTwjP=+PnTd0#GB-<>No2AGB=G9&yDbn|0cFFCUs&D z+NhIAjam@ib)#NFIm`weX?r8MS~Ph79pmH@GVbOU3Hht*EuWW%KUnGD<~Ti(-uO5EgV3e8 zi$Y_&*3Mg5yh&@ThDts)5~EcZ2&$YIg*ch6zGAu?%M2`8NlC81yVz&%d7e3-v!DZx z;*i=jx0>4i%vIN9!sxsN%Uz2;PXE@O>nXOkErb^aPJJg*CT8yY{d;q(A(@R6>p}b@ z&{%)z+b^P;r?+ea9_+q}Wc>7qx2D21nt#rxd33(C#A(0UaVO6(V46RLF8&mAK#Q&n zMrBt4G9m6%f=jM9I?gHxX`MWmY|(VGr8^zppHBeaS#!B73SaE}e7LMp8jG2(^z;&q zh8g3GuKhj4NTZ(YLd$K<3R^x}?|V0ww{hW3A1fpQD(QG7RY#u%qb61s5$*?R!va>M zj&B;<>Q1tX^5RCHr&XS%cd--RrlJit>%|H(7@No?Psb-qn=Nn~cFhb;vC$t# zJDv0*S^*)Yd#9ba?$4CIESf$ST8RLeU=D}Yt@;o@EuWd>&;botE<1jHBZpyD_=tOW zl-1JcX1d`u7l*XcsPMGn!`F8g_F`krh94w+Cfhc&+=%{^k)ToadD$-DMnh?sJ+H`V zgXPJy4ego2I8(s|);xf`dJk`wdu`Q#&@?zW?-L52L`K5(I279eSD>%bRm;isv=g>5$cBoxNsNj40~f zFG<&Hk8Jm;pNuchm5H4Wv9ELoDvffvNQrj|+9Oh*uZ%>o0=9gw+~)p^fjq|+_S#tp zbe0+w*UtpIZQ3@i(Rw$QMzNKpKx=b4Uolk?+0dQD%Xv1N#Z_E~jn zPiom3PS=s|ei0MtZTHHvQlZ>ivw>f$*BS(ui|(i0?Mfy zJ_OhDwO4s7IfI*@UOUm-#)#V_XNlWe2>zDoJGHe7DGp4+9fE@9i%hNMCf7VefTm>= ziEQT}de*`41E|+KiK+s#8UU*L`K*~Vc$kT`zu2o`JbOXy*5W~#iWof$EaD)fo=fY& zP!bc5Sq)Hp#-h3RZMpmixe%$k!P<{97%-fxa@+nJjVudm;S&M3Va3Xc{ph3CDY%?j zuRo8G^C&FtG;@U)1XI?E3(mD3L&0g*qWA$n?pR8FlxmU6=FC9SQ8^j#& zQpmoL%_by;9Fw8vy86ah*DA?mDMCS93W?;nNhW-`#SXFT z$j(iyAi4Dvn3-LL`0wwGosygE#3J)E$-(TP&*og>AH*yr-z6Ld!a@p$bs*?S7b@faPOz+_LDJd zVE$>j-s@;?^ErBRJ*muZL}}jG)FyGn;+ia$kl`C;UWsq5C4&MbD9<*|I9#m$7oq)s ecq`gEh?76_2~A7~7yKhmDieeyyv5*V;(q{sJ`>CU delta 1982 zcmV;v2SND39mWqfiBL{Q4GJ0x0000DNk~Le0000~0000$2nGNE0E8@1OaK4?32;bR za{vGf6951U69E94oEVWdAAbj1Nklnrvp4ZJUl!+Jbyeq<}0G5CL#~a zLkKQH98(Sph*o|ZHVnu4h{;0;x~eG3b!tm>!=<=JQ1Ru0W&t`2#RsIu05#1=s?hWp z&+S%7Ip;QUT_dP^X4e%6M9qUN=y*aulEeHuqKj=Nrjl~P-T(RgY1ZXh8%O!Zm^_$(nW$T`&0t zDP-MJV9gFrVXKW-4s!Ckl4|iaqXM`q%l~uqAo>Qo#Iq57Adv({4aaoI*!t+M9%nO< zpjJAiou@<5a=H${E&DrAOB9C5~!U{s((jNCfrjt-EWCN5%)`) zEXD2ZgXrzIwp^L82JTHJEiSP-eJ9nAQ3hNSLs`t@mg9pgN*w2ydo1+9F=?HV$$>ccOh1mZrKKPqvzxzG3+4UAES7~0 zu$7A6Z{%C6tbbqoZcSOLpd-FhYC9an6&A|w-VB&a9ZgIY6@DLR5qF~3h9NFLwSE9w ztY)pDI`eoS2rSz;c}gUXJ6%$_!{rrm*mV9t)y2$G9pE_%1dCb_DeCJxfz`{+aJsVs8Phhk9EOMfh2*gP0osSB&KBYjl)6b?~& z3sscEd1GM~blucP6T^CKNqa+iQxkF#B?sEMO;21#mTj6!QquKX5rzX4|CKw4Rw@xX zt*+5#{%9lY_M>t!BmvqN2EAbx^q0gV{1E>e+p0qdDOFP!d-F%lZrR!c6{#dY zPm9AfB7d!nef68@hRswo%WKI<5oa+AcqIhCPWaH1xQEe+C-`j9Cam!X&CkF40Js}7 z-}vxmwwsDmt!SiHZN1etD(muvYR?kjl@M(9sdyo{9<^1A@a>a3=uIU|b11Sp-}Ma% zN=+!uu$hVps`c%LQfcTJ;qtYaeVjdhb$U^&{C}X0>&!RFx+U3nQM;2 z#yF3h=Unu~z+bpCK3w#9Mp2gr8c<6H0oh9P+EZCZWw6`r{ipqveYsV!ej{->192T>V$j1RJT8%SSO+2J<)YUX`Neh^Zk=vr`?IMEN(#MM?7dW=|Jcr^@ zF=d}$zkuW80*If7@_PKAE-LurW=@Hnw|{W5VDhb<3npb1)Ut1%$^HGpv}i)XOd)^U zyll7iMS=Beb6h`|Q;*6xYS`d|O_bN=t@`$vkjxQDWZKH4c>(&B?G|y=Z_#B-LG$7wz1}P-hFX!eP_nNUEq|wl zMvW)$?o-havYse2^;SQt>R)WWz+Ouq1|Tj?KwRg2qlOgd1FkXG1W*j(7%B>0)V@1q z3*{3ur}kXA6tjaZlnw>>SU!`xVyLS>$2C$D1eqENPIDc~*C_q_07WQ^pjzK9iU)pi zBMtHAQ_zMg(C&K78KQ2aae^A#x_`4n>F;;7643$PU<$Lvp7WhVD{Q1J%Xhrr-j&;G z=FMR-!LE!Hi`xGf^wz@J$3_WJC#`KO%hs_G$CHv(-)>1Plc{Ak4pc$hNn%b2JU|6- zKFb?or(8Wc!z=7ot4I{dd=$ZsnBrtpLZZ=cq)w=`hu@U-D>sEGN7)pW|9?mcyT%1> zO^o97(CvItn|Ql;KW}4%M|is&p9Vaa7RU{r0dghz)gHFwEWKXItmdsR;3tc4o-cIm+T18J=C1tvwkFx*qhQ&=2!XSKGG zlMK7IbGq7YOf8P+iQ}aFj!`#ef+{+u-lM~9)@*wW6@NV~QU0MLIm2A? zuyASWm`Recf*Mb?bHeM^-+yD90qH5X;TT=2ON7U1p`g6ph ze_rE8A+tELI3n)LZ-prFQmrwQ#G-t}=4!zn9v&VZ9v&VZ9v&VZ9{+Rx1DbuEcnbM{ QFaQ7m07*qoM6N<$f{l2y(f|Me diff --git a/src/assets/img/nftDashboard/rareSats/rare.png b/src/assets/img/nftDashboard/rareSats/rare.png new file mode 100644 index 0000000000000000000000000000000000000000..42faffb5a0f133c005f885de053c46bae5926037 GIT binary patch literal 1198 zcmYk5Yfuw+6vh9$EV3F!7p!0ugQ1R4Wnzt>-5NqLY2+aaD$foCry)E>MGz;gvE9T* z0U>ef3+yn7w3tW`@jHAYC#fW!e&0Wox^I9Q6T@>ng^uHU>f=bky|_u+oJrCZ{q zb7rw;0RZMiZxkmoPJ9qLgV}c?vYd>~EZCS@1i*r29|T1wemcgKP*I{Z3N+kbJj?_{ zA=)GY;G&*2o#n~I4bfuJHYN1W)9dlu66V{f;u&!*Io*{x%`I^|$Xd0uGXBd09tSuE z>kQ8Ac$V+Vk&6$+mf-|70jsOX`jL9rQ(bj=D&y44M+G7G2L}K3`mC^oIrH(TzI`{| z_owBQFe!OpbUeho#Iu!LOYW=PnX{+)S$Zj3)CRtpzH3UR|2}EvglP((X~E4Bw#W>0 zi!#OKfpK7Y1+PXjk4tpvcwC7)g1D^X)rj3Vk?ZioxCM)>^(5;h_WNw3h0;Dp`#oI~ zv@IWLfZM`uj`^#928DWFv&79M5=qu(ta7e#oVLXxXFbdeo~M2S^7OoVscba}C0S0a zk`>uU+wzdJunWK0=&!Bzb?A+=77%IJMG2k6X-Utqx+}R zuU>dCuuC6tVf1i?acON#c_*h;dt{Cv4PW~vV@mkbUEjkuipbmUySAgp6Bb4eQ<4_6 z+jC343JZm|L)}X<#buG3K$_hzfx87GexoEmqIF)zV%4BPQwQa6GL<@G8c4GHWdxah zh$c$wK+C*j3=Y-QKq(9^Gp2wH26Kabh^v(LFnY<$U98FyXzC#)Co@GCm<|%`en~-X zK9N@_Z7Yi3%I&7KO(=dtK2B+CQT+N+yRbb5sqXhCIUP|5|JiKvy{e;qxvtG`*?bN<5a2X8xf-m5v2)mNVBI-dQmdR)1u$*=<6_kOUr3!Cr%!`R!7 zgG)D^J0^@Nap&|pHtUB`?z(mxJ)95_g_NG4x=4J10FEV1((HL*a6V~rWc!4|N0_Xb zzYMl8SurGlounyf))-qoL3MquRUWb4p@+?yX0cm7z4z;^o}jTc-{Y5H3#MEo3`j3+ zLf7jIx`wtGB*U@Uk$hGFcao+R!NxH7C27hJJ`<`zNs}eGW|=0KdoeNFaE?Vh zEDP+VG~c^-g~RBrCNM=oR4^s119 z=Q{%K*{pPXOFmgBb$t;;^7%a78ejDps_I;_qfT#qOt<$te|h+otU#6hYJz&fU4=*R z7lN(6>OrdN2}qeh%k`acUe-E^Tc*m607>iN - - - - - - diff --git a/src/assets/img/nftDashboard/rareSats/seqpali.png b/src/assets/img/nftDashboard/rareSats/seqpali.png index f2eea05227db5c81f8074b1e6f8640448f199a1d..0cd887b899fefd9334832f6de4b433d8fde1d72d 100644 GIT binary patch literal 10891 zcmbVyRZ|=cuq^Hp++lI|MFYWQaR>x=cXxLPF2UU)xGlE0TX1&?F2SAio%0XwedwN= zdFq*}sp_ekXcZ+HbQEF~C@3g&kgTNIe=+EPfrRiM4}3G8_%9$k$!fboLE+Q>FQ7qc zG}r$XpVfa;h~_I03b;*buZ`(f8-3@#r6l^s;W+( zOn(H62#p_zPKk=5)ViV`PN-1;hew0wO{eID9*{%wKMU2AwqRmYmz1_)D_1()(7oNLsYmWTTOA^IV82_K? zbIs@&3ZF#_@Vn)sNpU#BHHCmFnbB4D+vdR{_CCEp3RB1=L(2SIeD~8{41(G*2R@pv zk>{bWk8RZ)D%}kspR0ftooPgCv6tNLi{IQYcN;{EwsTAq5YCsR99@w2~W?h|`W?!VN?90DRo&*1I1>Y59@W?0EL3IJ$_RN^Eg zaBOMSF&OyQe5T}O=_L&9|B-25wa!k&F>-O=8Qf4W>8GIUx$*a1zLT0Y%8c_tRuqF!%a zi_oH7k#ERs10IEBR^{lu-{=ssSN8bb^-h%yXYcdBhwn3 zzXD(TY_b`>LH-sc6i=x+nejSNzFfPug+=k`wqLc{L?7Yyj(oHJCwAc4Xxgs+QUY*K z3O_SwJWVP{$eNKotPCsCPGGS*0u0cN<~-HWQx17UUJLf{hL*RO`XuLz#U#cY<&}ku zhzq)6Bvm7>!GA}r6F@Wghc^jJfgMR9#X|fYv8RZ?|4*KN(`o(_K`>J4s!i8~b^nH< zo3i6<5FOG4^<($dv(e@>oU11ot(sRNc5_UaqsI@K4k8nqy}X-<`uX12!B>^;FQZd?V6p@~d$hS~jABnF|B^q?RtbzjW&et>&0+wlmXCB?5 zKRCPhBA@WTF3z&uZC4?>yU$mTYC-UB9U0>f2b8cNlOQkH}|AMQPdpbs8^TMed& z?BwbT`H~A-%cI&(@fql$3&d9}Alc;l>TiiJIr65G%fA_$7Np?L2dm~k1dbInXGfcs zqXTxnMm^sil~a%A)96AsDSE*D96(skEM|G_tq@X>p!s}E z_w@|e%(HFoBq)wR`({Prb!qbGoi1Qj4$B(}SDT{Fl`M+WhqU<Di$?DLt~vkFUtx%$ZK!fQw2(#5!Lc2qYi$LUQH=@R> z-X9!DFSEJ2<_51#_S*p@R!604k?`zJ?UDNja%_yteB?C5@DREX@?yogpqJP#Hv~TK zK1!3k8)A%}r7k?}9`o!%N8dCq)RrWAg~s&HDs#cQa+Su2E2!EGXAmV{OFE+n*SDLt zM4OuoTdN0|NM#zjJj0M|KbZVs=ZyhIjk#7|kQs$wlNo~zw}dJttM^lp!=J+3V-?=( zGEJr!S}Cd_Sj_`x>cQRL3#Q*<#ox*1B>LN+xqObe&W0&%7)($Frw$zxT&jyf`ty?m zyIcDlyu!pD!C6LgqC#I3JU87tZjn*T6epTN7{p%RzTUJgSdt#hE+YwSdQB?Dir80# z+#)c@x&qN5`OTZPF0k2;XFv?PSpLa^6+%4$Ga1=gGwRoW^7JDmzDE`s|0(+dyYWV+ zNmLh*#`Pn;l^+Cmznp2d99|lHd{)uTRhDlq(M<}s?vFG_za4T#2XU{*rc6Fx%Lqm? z;3r@7_V#i}3C3}s8jKPfo_-)OzSKoj!gm@_H7Q0!fV7;}Vkcq6gl@lIdV06#>HXoM zU2c{K)@n6SNp!Irc6ad_WJ}GLKU&laizciPQd#mM9_+^Sz9`4rtP6+ks-d`GfKdBe zFLW7vF~o1w`WFiOk25jtMGdXsz4=Sl>n}-Q4O6q?g84YlG_W5%G|$_}Ha}}4k1RGL zF$yHm08aB&StPQrBE(mIqkjs$(G@6)j7~*;g5J$lMAlH&r$en~&h*N8Wgyr7-;p7X z&_bT92O;GLWlYKaW0=8d-V(oF}t~A_gB{ z)}mwr-eqh1;(Sl`RU*$bAm_E_JX7m>0{43xiPf7Duuu6F6f$KNqU7LMmUm-SdomQP zz*uxs*LSB+f1Xr`IV+5DP99%kChBMPtt!tQnqTAc;8z+f+RVNrP{$U2y87be4#77+ z;|Bycs0$Q$nGn!Rhm#KFQ&hiRuGdYp^a5UmCXfK`GQ$79$n* z(5c3YOn4q0jVWr8Rr+6qb{gjpDN!7_(D`lXklWIbO>ZIN{Xl^aZGRc_{j_kXD-8V7 zg(-9$G#~WOH066y!~l*Pyr~WkrBr7U`93l5%XTiJ_ZvEE6#Gn!x`DGYay(`$_CI-3 zX|eF{JJrI_Bf`qm7lF%03c(Toq`2l>e@PCO9}Vy+VZLIMNk$SGeQ|kEv*Nytn;-4X z+)$1%9bv0*^hd%+TDRS}2)k-rz1A1Z9W>XJPWNS6O0ai{zsuj%z-x- zMVq9sQzGrmIht;cd=x^)B#dBaC?62S5qPEGOv&f||?>MmL zI@*yA$B*<7&F*47wW+9@?u2ipA|atbgpbT4YOiT+Af548pL*l^9m!OX6r!?|$)}0rSrZU{p zRoW*I|Ir`T;H{ptLhc5WYXT*q_Mq~}Y?_Ay#2;*xl990AJ`YzXYOO4(DobqHMemyg zMXDx-4`|pU(aEBeh|N%)==Y+Seum=3lal*G_2ZKtMFgP2f+bBqhS0t{7adDE(Pr60>bszQNi8ZS6Md(m$E z>Bcz9!rXZ|T3_Op^VX0zVnI&3b5xc_G4;IRH{bLKGuH!)TJc=5^efzp-SZLZFz>thZ%5z(H7R-zt83XflEvkHuskht>BYurL6Ev=u#Wff;z^-@TblQd`cL^MGDIZ=9Y z3iT^2Kp7w@eMEttFo{IN2)c&OV#nW4u-jHw08pj>P=m!W%msgLhbRsACnDvn)cO9> z2@@MBn}o0n8jI|Sp6~ppEdB3Yy%vntO+?lfD;tEauS|>!jFe8RlIlb#7(;R5QGuME zQSLtf*GB*79vY{+N@B^%nlN2gd;7Kix9mYfKU?XGl=#V{_R*IY60?tky3y|I^igkP zGU@vrEucZfZ#=p$5gEY0I`S;b`+9OCTdQ50=pr43KaU>~UoxcAKBln#I8#I584Rwo zlarGx1nBKhs7EOZls9Wy1>OM(TBLZpG2!wI$K0$`vWCyGQs)Sc3@9{5 zp^=b^_j=L}#y;1hQlwbs{xU8LDdheBgU(ry+fQgXD*)#3SIr+2|FET6lyL7vH(53N z1IL>2eN=jHU>Y^}5zQ%GO_ce^9^GsF$gD*I@rCh3=rEFWH4s^T^I`q{NtZOag04B0 zvE0v?EFCu~k{8_pjVa zI>?fVKV6UiD4S#_;1o|*CV}405L^nAHwV>~t|U6e?OAXW;#z-FANIb8{1I(p6}uW= zka7%!W#A=Ma0^%l#S`>!?k0_rGn^}#aDw|NBKv8O6nHd53S}gCAcZbLO3XMC}n&m9%>k z39Nc;9OIu_K-a%S3$=qf<;woQB$@b{H2c3r5q?&ikeuUQYVZXtnNZTwvyYso;rxiX zA1=1F!k~#n^EVTL>o7OngpRO3j9xZU`&K|)W)P!PG>=wb+mB?1?5Yg`ag~R^Id#2q zW`IHc5kFsw9iG*^9JJ83lf5}QkHVB^+W4nOgAUXRy8jWiC$|u;6|>S&66sOb0e&TF ze8w3TkO{XJcr*XJfr!#W%uw|d4Udvg_(#Rh-$!{%P-s@oz$yA1vVQ4?ng=LBY}5Q2Q{@mlw)Vc z9!DpZP+T!KW5jW`(Q9Lb#EROY>$UIo7t#3m!6!%I#lb)vaoE>hKF6Pj?5Ic}BX%cM zqfr_B8{`Aa7Eb%!Q7XDNt{l(uUSjk`vKTr;H)*q!q!-yDjBYA~FDL6j;tS36Z8EZd z>(9Gs$fU_mjI>zpsWSSDWm2U6t~9dUPzTggIn+0BFh>ayyhTxd0pUV4U*BBmc{>Cj z{0PqReyJ=w3_+o2n1efva(w*?tCYN5{8F~-N0>ofyZ5r&`D702~&tmVe@^)dP zyAx<`_RfEuISycisM#RF@UP~!Bsd&uu>ES_-e(mMI;(V;ra-CLPY1X-KBjSI74IVK ztXc@x^JkOAlp1aHwh{$5>ueyMxzD`Lb{n+=07!KEtULWNi54b}D>Z~BX;;w4ii^gc zL{r}bL01|_J8akKB)EMbRPlPDg_<`{Kr$cv%5KGAO)1O6`pUFXAd6%T7&M!QKEnWd zIV7eddB&U6gjr7%5nInpM)*N1^07c%*1EQmj+T4C-A)r_tSfJQ7S)E3ZpVXmA&F;l z&S|r=%aicL7e$-*fYcToRGyIjTIbH9H-BjAO}uxzy+9l5mD7|@suA{DN{Tv4&lN;V z<_UPlBAxGCXq0Z)x#>`1j;zW6Xt$@#eT%eZ*(nrlIbudP_6Wzv-1dp8P*nO)%k!f{{)NGZl~6#<>W4 z;=I@;SX5IY4e8Xfov)}fW|P<`S(OVdurZdLCJGi2@8TU~IH8slV5;)%9N#9#@PMHR zZAhrDy;hq@{MLssU!#?{dy7#!ylL@8SoQD#6zO~S{jgRT>q}%#b}QJ^=ri&MV~0Pl zOBH%BYPgWL@0)ft$X3Xl5kbgDqnih8YYY4d0P&$z0o;kj=oxHtmRemQcO{WsJUIKy zNHdVl#;zT>sa@V;vW1YwY}s-~#BaMpGJ!L7i#6WcyD!!QjB6nc-Z=KZ6zo%#_(PiE zrld}&SA3q@mi>a9-TRC6JFaRYe*STx1y28EggTI5xID0gT_}OsPy2ZEUoLR$Bc`DYW8O0P z&s&~U6Z=~Zn|S;;SI(RJQc73dC>LmwRqBW4YpS@vYEeaNT98Q7-I?yCJ&dDM%zjjg zv2xd*H(nrDSU7XF%3VTu|L^RQCUPLL=3(F0+Si~a_Z|CiUJQKWk1QmZD*m~shS~r z-g1~+H2A<8Xzmd05(Cp-d66*fj}|S`#?;6Z&E$E;0SOR+m}sQds=`AR>0F>3)k@a8 z!nvDG>*|mRf3lDo^YAery+T)aw&_-TG06Wu`0SS_WsD_F$U68u%0U6U@AVMbO2%)- zBDQ4a*~w4rKi!hpK@DFjgCMdn$^B4pSjo8Raf}I%$#75glY@iK1FU36UPshiL0z+P zvU)ZU-+qz5cE9OCCL0EFKje2`TyeR%?e#)|HPbu@$xBX`_=3MrwfD#mstx~jsqCZ zz~Uc1J*8~?L^3|+Pkt!Jf%h}8suVDeCn?Wgay+XO>zF|lLh*^pp-Zedg|0DQMrE7Mb1=vIBV6}lZn+%A9>B6Nf?W(i zY?G^3*55WuGmB;5CEgSZ11xyK7Yi=4q+|TuX&aFdV=6z!sRtJy&KZ75Xh0nYxdqSW6I5;q_e9r9(lW$CExwM$%~1V&d`vV_-B5K!6vDC2#Jo$MJi+H7u2lwclAh@Tk61hKqJW6?4)%3r%ZV?xIJ`E%!}1dunniF zBzS&u7wB-Kkdq2cqbsKOJ=r{lQ>$@M{Hxe_G*?jDexdd%Vj>OA1d`+!ZrS!T(`OHK zyPzm+vut&jSh5j1zpa<$(pQ`?072YX$aV5zZ9^0{fun4En1kKUf45${m8D7^hJxv7Ph9+!di}L( zc=0`$h(B@zGOL&T(#M6-9;2QY$6`OGBTdCAH{J)pQVfQc&eWY}S1+0>%u_yUl(GW8 zB@+`U>A2Nc20iqbFcj|+bZfvA5fTsK0%bjD{kAQTxXEKWOIwqm2U&?TBnc@kG8oYY z7glu-&{44ofeYW}h(|KRJc4U$n%`a#; zIl1bE>*<$e?p;$oK`sK91i?}Aiwqt=X`j)_jmlfos_;mD^5ZVhuFhs%-(QEo_cCc@+U`LkY~u4Rw@cSK8WEA{YwusKh$OAv6>c<- z6{&@CEUX!SY;Q1}A%aGKr-=!fjH@S@%W}!h@q?3dg_M8yzu%P*z;aeu(?AOj?MmbY zX{eQ6w!D53>ZmiPrpoahZFf?F^kuy>(H79qBI~xB?h_&xjX*ToEK6H@_U%`~ zy^ive{gT@59x*tyB_39>{SnUrmKa&Fn3CX9FbP4jII&Q8h*nscFST301s)y!BH8dvm2wHS;vRK#A0Hnv51ObRFTz@ zLuexNZzSXIsqnykPeZNuK4QmWc?&Y;O_%@^3T!b!Qx+|y`Ij%zB_Ub6%`{R%NaW*< znaAl4Mo^GOJJQC%-C~CAe*meME;N~U6eSy%qVPPb{MNL70{Ljh-(OMX%n6Ujrp=0_AYtGlyZ0Pee1m?gIgv;h0Q0 z2kb*P&+R<7=&b!m<*N>tt4NoiF3Ide0fxLO4#>)s82{ZCgtmy z0M$p7m%Sa3Zr&HpIc)%Sy%tu5FVMPFr6xxJ^NI|O{2!H*f*mCZ{8eM-3iXlEzO&_B zHMWn)7tzu6$+jgw4o^oYOggqg0s|1RaL9QVZ?rYdz*RXt*c$>o_@$P=Y}cHum&6fD z7a~R!-0iR8yDGVGcxdDwP7?X{PuPiSd`$(N?R^t%Y4}`pWY#=D306{oD4XohjHEET zYG)<9SWa$b-}_s(Q&-Qb4*N=5O^&o=<3#;L_Gi@K#5c2W@E$!K|u(zj-OOd^Fx-Oq}ds~)#MskHB-N|HA zBYM{Tx-m|t83~f|u;z6a7SiOCfhi}l?zf@iTT+$nbqwDV&O2AzI1>dpXA|sv!0Cl_ z4Q@2-Zw$v+ovwZ!w56At8An(bjd<~Nxib*rNAQt*A!8MVhWEx}FKstPzuI^r>-H0XGcGN5tSQ*Oz&gF3M-vc_A$7Sr_d` zV1hyiIP{I*R$s#GD;Q@>%Jqv-dWPpxnA0X~gG*)S#M)z>-FAY5&F{$!RI=sNu0JV; zYTsb=$Dw%HhiAwTz)MPBRIA~e(?Bw!b|e!1ARwe|%sFwvCrSAOB84jUrsKG99vi~I zbXX^*n>4Djz_Y9K!b(@)LFbXcE6e$T+Jn74Xx zeNce04gq=*@Ifv(4r%tQ2{9nYVl5{H;3DHi$Nfi3-5sM2ve{;-kP>3HSvY>@?cqUya z7t#RK5|o)V?Z@J7tgukt_ST+*$`QFXky|1@`=x$QgrvWh{GLWErG-ok+P-gIVUYz<0Xt=mFRGcjHHG>1!+BvwX)nI^D z4>g7Z_Zv*|#7J-xLbydymkO_(@4H5=2^#v>&5$omPDm6PZ9WsWijT*{znReUP6^9vKFQ_n6d)-{aJlx zX#O?z9_g@CKGLRvGlfvc6Tgg2Q*MYB7x{`Vtf>)YoE4I`S-7FzAdco7mDKoq!|w{! zJpGbt)6f10c23wR#k%UZlVM z_#g?*M#mk&_vCJJ!08_LxAK3>-Xxdbu@bNM#1PrO7JnH=kJ<4#skx4rLqghTcXeS8 zW2!Rhxo{5iIU4X7q0RW(%)Yh#r=EG~gGN8*44Nawf1LJ`3Yn|@S>UqJDo<+?iyDq4 zDiD+uTUZNqP7wg_zQ2TzVE05-0>bd#t()6@Ui^im#5Amw@NX-IO@so&z1D$+B{C%DDtb~^5Zl(Lh8$yewh}!$_L1t+>AAhj!>bJ%cL|IS|o=J}x z6i3D%;TMU~xzGyaZh6tunDuxpg*{e7P_YHr+*CWtuU}%|;B?qJ*}WoEG0P}YBD1{n@fKBZ^=yx8CE5fv6<-=oPRd_cObx7&S3tkK9b#wQDf?6{wq$xlL{lg zU(MB2)Ze=3osM?2qcG|l-`n7U`eMN=@1Sy!uspObK2n{ViTQE*GH?(!ykXY&JE56_ zJf?%QZLC#eXjrMZ{c{^9&z3!y*Mg9f`Q$b8UI4zR)mDYh?vQ@x?G@q99d1@%(T-x? zm6SuHD}qfrJu<|)`?8?i`>QI6#@}Np5T2?+M?3!_;$wK`Uxyc^&}vK@x#hJ+vLAV( zA62;4jn*ee(qW^Yk3+SEy_vHs^xW~pL3MLF^n}skU^BcRtCgV*q@oNX6`(q7X^vP$ zg`w#M^_Y1Rit*~!I}8eZjgKG>H&iB)@+Mz|d*u`OC7f+|b6=ZvRgHaqr0p_RAEgf3 ziXn|K8X;->Y15&pHHjyLMG=d<^%U*)8uL4_wdOaHBf1U7@Vkdpv{_I~6tq&F=Y~&8$sRH_{o3k>eE`qlBIt2M|5h*Eta_~pboTk;0 zWNLC$GC6N7A05Sw|E zH43{&Fo8auU-HrNmWz!g5rsreQBioDmM-?yk*1BisLrGuG2CD3NrxlU^u4@3@J4F? zWX4$}mKc5QF>+wi$v99^0c)oibi+OOUdk_ZJSVE+THzq1l)2Py!c+akmds>w7_Zes zKVF1El!Wp6TwKFpbHGKRDnN75&?BWUKgqBiFZUFI9`x86Kk65?K$L_XNv zi2pgGHpYgi=Py?kQzwfml!T4-a_g}j3kb}Npv;G z+P`WswHOW|kjEcJtKDD-P?XdZDNmS^^J7emIcm|OqqBKdJfSz`C}mUE2M_i%r=O?H z?Q0Dn;QCwJf}@a@hvVRxqbj*X4~t3&lb+x-4IX}xW@1@~79XH0@LY(9 zEej8pUh%u|QPT7DJO2=WDX%E3neLL01i=U1+5B4$pt%D3=2Xc6-knR`;(e|ns_kXS z2myycHU6*z>zBCO?okKjEm<>+#Z%r+<1N(-;XUm4J((I)PEm=#AzLzke0XwwIeufB zyVwdSaqa!V@ccfJ0JFS(C?!~>iKTi(%SJhuFbc<-TxpZ{u!MalR`8&|djZ~Fv4Nos zoMXmQrFKM*6Zb7Wt+Qkx&5tce7q3}XOf-Wd2e8I+XHjO~8&CDi{rgZ|DQRiYUrwKv z>G}h5xf|i!_ua_&y=Hq14gJji9XYw0PTA`!AIzT(P*_e7$_6Qx;-i}pIsDf4`WqbD z2|45O<=K(^xwG#@u`9Yt3W;|Y&qWCRzTQo9e7nHi<`pRPwtp?-?Ts|iKM5RYU~xgf zZXKy4;`b};qXf!{9`D1O!`!t1|B1Sfj5?Az8j~8`%&(7nCzFh_T*eqk^S+Ej&;NOL7vF-hZDB zi;Eh|)W@&5xv&kwpDHGPe^2PTdry8rVbupD8>BH*M=YV_|RMa|C4&OlWFEzpyaRP6s5tcWtWJH<4#v zUfv}zw7~QAs0jr3gFj2W#9q_vTrbm&l>Ds>F0Hv=vi9$)f@Bi~k#H?U#6s@~D??i? z`w2aWlf8#3sk(c|V_bNNBfMERJEug}Ci(03u{$sN+65@~zlL4K!&yOrLP)mwwM(4L z85@qgvNl)EXw)_m@Z=`X?OpyhzPUzF@0>H{Q;NKlqs}L*eeHp?`Cp7}b+BzK`j_=$ zH3fW1!!bt(4`FlPkklBGe3iR8YmZO=VUY7H6=i#D&@%ygZzsOx@UoyVK1l(Ts`DG= zdZu;c=#X}s(~&*O*fF5AOe8!|Mukh_Fy~6>bYkOSPkia+#{sUIEM@rrCn@cGLMsM+ W65sd&;{N*`g#t+_N!E%Rh5Qf28r`S> literal 4229 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 bbQb>&xxZx?1JIX<00000NkvXXu0mjfFJdKa diff --git a/src/assets/img/nftDashboard/rareSats/silkroad.png b/src/assets/img/nftDashboard/rareSats/silkroad.png index 4cbacb6aca4906cffcfa1a6f99827286e9238a8c..b4895f6466413747301e18acb49cee8b172742bf 100644 GIT binary patch literal 6184 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 - - - - - - - diff --git a/src/assets/img/nftDashboard/rareSats/unknown.png b/src/assets/img/nftDashboard/rareSats/unknown.png new file mode 100644 index 0000000000000000000000000000000000000000..b3b2e81b3b3ba83aaf37e3596ee70c3909153d5e GIT binary patch literal 455 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSoCO|{#S9E$svykh8Km+7D9BhG z-%+bfsAghd34H;3e*4KfQVVlf#LPV51nqo;_UNT>bui@=v`y*{E!J z)5fylG?fp?S^Ghh{i4JY*5@V|bW0hkHlQ^W{6fMnjaB zfB4Y3;AYkP`N=nR_S`xzb^1}G8^iK_IpK|Gm|KJ$EEw?<5JL_`{bk&7H-1Lm?S1_q N5l>e?mvv4FO#t$1q5l8? literal 0 HcmV?d00001 diff --git a/src/assets/img/nftDashboard/rareSats/unknown.svg b/src/assets/img/nftDashboard/rareSats/unknown.svg deleted file mode 100644 index 43881e799..000000000 --- a/src/assets/img/nftDashboard/rareSats/unknown.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/assets/img/nftDashboard/rareSats/vintage.png b/src/assets/img/nftDashboard/rareSats/vintage.png index 5a26a329e1b89e79e9db6c4236a2ad52ca66186c..b8136d5897c64d514395d7da17b747533773ebfc 100644 GIT binary patch delta 2881 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 VH(;8v-!@ETtdG#E(gC^P{|B|RblLy_ delta 1553 zcmV+s2JZR67Mu(=iBL{Q4GJ0x0000DNk~Le0000~0000$2nGNE0E8@1OaK4?32;bR za{vGf6951U69E94oEVWdAAbe0NklYxN7{~v!>z6f&?R7{9Zxu$Ws8qEf z=^JVl74^^F72U6flrY1#Hj*q+%T%zN(JOlE+s9(p=}CzvlH8~ zcfC8)nN3_0lt6^d%mVw9MzcHXm(^d-JkLDO&H@1e0RaI40RaI40e>$%1QMzKJDMgM zF7iVY65#(e9_<=F1Brwn5#5DJHP#DBFh-8^?&TFUvUhlbTj@z?G>!7q3Apt%S_x&) zmMM_jHBQ?TQ!dCQNJi?EZ&1Dd@o5G&g69CSDD=5{5sdan27aD^WTaZCppeU$%y}dx z3+>={(S>Gl?Iq}R4u6PN!J?W;EOLqupM+$j3u@G0vNiuooEgyMQMkM{Ln!|xpKjHa z6J1O^?p73_K{Bxqvw1M&*N&0WH8caU^B}@H8}MmQ8M_PxNEcK<;SQ`7m9@QxW?I&-WdKN9fwrXC_*PhKSXj_1?C@)B*QpKf(g)Cz%vv%Am z6?t3qnxI}Wv|y16hj_I**dgAVRklyx_#B2e~6_! zx3D}l3A$==Pb0E@Cn7ud!cJ^QIMtOu(EILwC`PG;3Q}EN+1uy8KJQdYr?&c?%JMRz z;RrDQ0Q31j;5ZKAJ%^+hLN>w|%OMBGGro3F7g~dvz5;;b1SthONcpq*+)y0jKUvlq%(G8pEaj zv*TI#5P#)CX(I}SLOMk0K}tyP^p984^S5sxwrej)q9bj--oqbnunC+Uhad4(F3*KD zONGLyM(I7C_Y862(v54H5rYv=nw`7$WQ_LqvB`;>_a<{U2jMe(BdD-SE@X~UI<(RC z&W}Dt^6d|x^9Y~p=tSJ?NOOlZHhC*I3LoJML4Q}pyvh&Ln|)^C&TptZ{OAAkZSjN< zRV4C5eiGFeg6fQo;R#}fqA`@F?xJ-6&voNMRMCW}zvpfa!3QXh@=J4bgN%@|&91w( zDqL&jX{OGF)wwTSi>NfsPX8^cO~o+N_P^5ACIJ`oi7BTLFp})>+T&shh^8Ud)-DcB z3x9MFicu=4@SVKdbg$D&)oFd(sBjY9w$1A`LXJT(O4p(G%wN51+&|7PW|U2PB|)ij!QduKPE z6)ogq-=RrFw$-66InYWpA7*+V8OTZxPD(74P^^pWZ5 Date: Tue, 23 Jan 2024 09:41:24 +0100 Subject: [PATCH 05/20] chore: use new core version with btc signer version upgrade (#763) * chore: use new core version with btc signer version upgrade * chore: bump local dep of scure btc-signer 1.2.1 too * chore: move getSeed method from constructor to method in rbf --------- Co-authored-by: Tim Man --- package-lock.json | 114 ++++++------------- package.json | 4 +- src/app/hooks/useRbfTransactionData.ts | 4 - src/app/screens/speedUpTransaction/index.tsx | 1 + 4 files changed, 40 insertions(+), 83 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4508b2e8f..d80e31636 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,8 @@ "@ledgerhq/hw-transport-webusb": "^6.27.13", "@phosphor-icons/react": "^2.0.10", "@react-spring/web": "^9.6.1", - "@scure/btc-signer": "^1.1.1", - "@secretkeylabs/xverse-core": "8.0.1", + "@scure/btc-signer": "1.2.1", + "@secretkeylabs/xverse-core": "9.0.0", "@stacks/connect": "7.4.1", "@stacks/stacks-blockchain-api-types": "6.1.1", "@stacks/transactions": "6.9.0", @@ -1660,9 +1660,9 @@ ] }, "node_modules/@scure/base": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.4.tgz", - "integrity": "sha512-wznebWtt+ejH8el87yuD4i9xLSbYZXf1Pe4DY0o/zq/eg5I0VQVXVbFs6XIM0pNVCJ/uE3t5wI9kh90mdLUxtw==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", + "integrity": "sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==", "funding": { "url": "https://paulmillr.com/funding/" } @@ -1721,52 +1721,30 @@ ] }, "node_modules/@scure/btc-signer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scure/btc-signer/-/btc-signer-1.1.1.tgz", - "integrity": "sha512-oXDbQFnGEQNHLNcTxM/MHXaQnZzSmoxunwXQbBr2Eg9ALAjYB9xvUa+EywkUSbU82Gn0/OEm0Gg9dz5HYifAIg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/btc-signer/-/btc-signer-1.2.1.tgz", + "integrity": "sha512-/Zle18/aWhYDBuBeXGDGJTdo0/LKpQhU8ETBJeWABCQkbk0QHCFCinidTiz9hdQFfh0HtasPGq5p6EodVCfEew==", "dependencies": { "@noble/curves": "~1.3.0", "@noble/hashes": "~1.3.3", - "@scure/base": "~1.1.4", - "micro-packed": "~0.4.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/btc-signer/node_modules/@noble/curves": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", - "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", - "dependencies": { - "@noble/hashes": "1.3.3" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/btc-signer/node_modules/@noble/hashes": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", - "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", - "engines": { - "node": ">= 16" + "@scure/base": "~1.1.5", + "micro-packed": "~0.5.1" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@secretkeylabs/xverse-core": { - "version": "8.0.1", - "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/8.0.1/4ad617c75c0435ca61da77d8d0cf0d4b961d8d87", - "integrity": "sha512-Y6qH74fUZ4Wv8CX/7Ax/9DMf7HfW4GYIgdsn2a8mgCMyAxre7MPcomxmzWSvK8CViHim18b6K2P+wNOOE4l70Q==", + "version": "9.0.0", + "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/9.0.0/125e215d0df4ea6fe030bacb83f3da8d7e2d7b2d", + "integrity": "sha512-fpc9urv2JrtxDYt7TcEK8oA4V8tDVVRhp08yIDy664E00u29JQMtwZ8gdtiVTmTzVl8/yV5ELZHoj3SYKDPc2g==", "license": "ISC", "dependencies": { "@bitcoinerlab/secp256k1": "^1.0.2", "@noble/curves": "^1.2.0", "@noble/secp256k1": "^1.7.1", "@scure/base": "^1.1.1", - "@scure/btc-signer": "1.1.1", + "@scure/btc-signer": "1.2.1", "@stacks/auth": "^6.9.0", "@stacks/connect": "^7.4.1", "@stacks/encryption": "6.9.0", @@ -9810,17 +9788,14 @@ } }, "node_modules/micro-packed": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/micro-packed/-/micro-packed-0.4.0.tgz", - "integrity": "sha512-H1+8SUMwcm68RXLOj3t5S8wXVf49FuR5m9IAG7XZ1XUOexbnQriyql5lk2I3fx/KyYf48LWNf5Lnbc2OjyQFMw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/micro-packed/-/micro-packed-0.5.1.tgz", + "integrity": "sha512-VjBHcsMAVfivjCZPnqAEEkcihPBbNd39KLEMH76ksL3ORKSZE04gkrtsAmXtaTor67PmTO5h0Rq9+j3PA4zNrw==", "dependencies": { - "@scure/base": "~1.1.3" + "@scure/base": "~1.1.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/micromatch": { @@ -15424,9 +15399,9 @@ "optional": true }, "@scure/base": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.4.tgz", - "integrity": "sha512-wznebWtt+ejH8el87yuD4i9xLSbYZXf1Pe4DY0o/zq/eg5I0VQVXVbFs6XIM0pNVCJ/uE3t5wI9kh90mdLUxtw==" + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", + "integrity": "sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==" }, "@scure/bip32": { "version": "1.1.3", @@ -15462,41 +15437,26 @@ } }, "@scure/btc-signer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scure/btc-signer/-/btc-signer-1.1.1.tgz", - "integrity": "sha512-oXDbQFnGEQNHLNcTxM/MHXaQnZzSmoxunwXQbBr2Eg9ALAjYB9xvUa+EywkUSbU82Gn0/OEm0Gg9dz5HYifAIg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/btc-signer/-/btc-signer-1.2.1.tgz", + "integrity": "sha512-/Zle18/aWhYDBuBeXGDGJTdo0/LKpQhU8ETBJeWABCQkbk0QHCFCinidTiz9hdQFfh0HtasPGq5p6EodVCfEew==", "requires": { "@noble/curves": "~1.3.0", "@noble/hashes": "~1.3.3", - "@scure/base": "~1.1.4", - "micro-packed": "~0.4.0" - }, - "dependencies": { - "@noble/curves": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", - "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", - "requires": { - "@noble/hashes": "1.3.3" - } - }, - "@noble/hashes": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", - "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==" - } + "@scure/base": "~1.1.5", + "micro-packed": "~0.5.1" } }, "@secretkeylabs/xverse-core": { - "version": "8.0.1", - "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/8.0.1/4ad617c75c0435ca61da77d8d0cf0d4b961d8d87", - "integrity": "sha512-Y6qH74fUZ4Wv8CX/7Ax/9DMf7HfW4GYIgdsn2a8mgCMyAxre7MPcomxmzWSvK8CViHim18b6K2P+wNOOE4l70Q==", + "version": "9.0.0", + "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/9.0.0/125e215d0df4ea6fe030bacb83f3da8d7e2d7b2d", + "integrity": "sha512-fpc9urv2JrtxDYt7TcEK8oA4V8tDVVRhp08yIDy664E00u29JQMtwZ8gdtiVTmTzVl8/yV5ELZHoj3SYKDPc2g==", "requires": { "@bitcoinerlab/secp256k1": "^1.0.2", "@noble/curves": "^1.2.0", "@noble/secp256k1": "^1.7.1", "@scure/base": "^1.1.1", - "@scure/btc-signer": "1.1.1", + "@scure/btc-signer": "1.2.1", "@stacks/auth": "^6.9.0", "@stacks/connect": "^7.4.1", "@stacks/encryption": "6.9.0", @@ -21668,11 +21628,11 @@ "dev": true }, "micro-packed": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/micro-packed/-/micro-packed-0.4.0.tgz", - "integrity": "sha512-H1+8SUMwcm68RXLOj3t5S8wXVf49FuR5m9IAG7XZ1XUOexbnQriyql5lk2I3fx/KyYf48LWNf5Lnbc2OjyQFMw==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/micro-packed/-/micro-packed-0.5.1.tgz", + "integrity": "sha512-VjBHcsMAVfivjCZPnqAEEkcihPBbNd39KLEMH76ksL3ORKSZE04gkrtsAmXtaTor67PmTO5h0Rq9+j3PA4zNrw==", "requires": { - "@scure/base": "~1.1.3" + "@scure/base": "~1.1.5" } }, "micromatch": { diff --git a/package.json b/package.json index 8fbac2c97..be9b81a8a 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,8 @@ "@ledgerhq/hw-transport-webusb": "^6.27.13", "@phosphor-icons/react": "^2.0.10", "@react-spring/web": "^9.6.1", - "@scure/btc-signer": "^1.1.1", - "@secretkeylabs/xverse-core": "8.0.1", + "@scure/btc-signer": "1.2.1", + "@secretkeylabs/xverse-core": "9.0.0", "@stacks/connect": "7.4.1", "@stacks/stacks-blockchain-api-types": "6.1.1", "@stacks/transactions": "6.9.0", diff --git a/src/app/hooks/useRbfTransactionData.ts b/src/app/hooks/useRbfTransactionData.ts index 8df8b543f..dcc74c7b6 100644 --- a/src/app/hooks/useRbfTransactionData.ts +++ b/src/app/hooks/useRbfTransactionData.ts @@ -18,7 +18,6 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import useBtcClient from './useBtcClient'; import useNetworkSelector from './useNetwork'; -import useSeedVault from './useSeedVault'; import useWalletSelector from './useWalletSelector'; // TODO: move the types and helper functions below to xverse-core @@ -117,7 +116,6 @@ const useRbfTransactionData = (transaction?: BtcTransactionData | StxTransaction const [rbfData, setRbfData] = useState({}); const { accountType, network, selectedAccount, stxAvailableBalance, feeMultipliers } = useWalletSelector(); - const seedVault = useSeedVault(); const btcClient = useBtcClient(); const selectedNetwork = useNetworkSelector(); const { t } = useTranslation('translation', { keyPrefix: 'SPEED_UP_TRANSACTION' }); @@ -227,7 +225,6 @@ const useRbfTransactionData = (transaction?: BtcTransactionData | StxTransaction : selectedAccount.id, network: network.type, esploraProvider: btcClient, - getSeedPhrase: seedVault.getSeed, }); const mempoolFees = await mempoolApi.getRecommendedFees(network.type); @@ -253,7 +250,6 @@ const useRbfTransactionData = (transaction?: BtcTransactionData | StxTransaction transaction, accountType, network.type, - seedVault, btcClient, fetchStxData, t, diff --git a/src/app/screens/speedUpTransaction/index.tsx b/src/app/screens/speedUpTransaction/index.tsx index d1842d925..856604557 100644 --- a/src/app/screens/speedUpTransaction/index.tsx +++ b/src/app/screens/speedUpTransaction/index.tsx @@ -213,6 +213,7 @@ function SpeedUpTransactionScreen() { const signedTx = await rbfTransaction.getReplacementTransaction({ feeRate: Number(feeRateInput), ledgerTransport: transport, + getSeedPhrase: getSeed, }); await btcClient.sendRawTransaction(signedTx.hex); From 85f91aa1b0258dc1bb77256ff67ca08ac806afa4 Mon Sep 17 00:00:00 2001 From: Den <36603049+dhriaznov@users.noreply.github.com> Date: Wed, 24 Jan 2024 12:38:17 +0100 Subject: [PATCH 06/20] [ENG-3036] feat: Rename accounts from the change account screen (#740) * [ENG-3036] feat: Rename accounts from the Extension change account screen * Add the acc renaming logic for regular and ledger accounts * Update Change Account screen icons * Add the space between the account list and control buttons * Make some small code tweaks * Fix the 0 index account renaming issue --- src/app/components/accountHeader/index.tsx | 4 +- src/app/components/accountRow/index.tsx | 237 +++++++++++++----- .../components/ledger/ledgerInput/index.tsx | 32 +-- src/app/components/topRow/index.tsx | 2 +- src/app/hooks/useWalletReducer.ts | 18 ++ src/app/layouts/sendLayout.tsx | 2 +- src/app/screens/accountList/index.tsx | 111 ++++---- src/app/screens/coinDashboard/index.tsx | 2 +- .../ledger/importLedgerAccount/index.tsx | 37 +-- .../importLedgerAccount/stepControls.tsx | 2 +- src/app/screens/nftCollection/index.tsx | 2 +- src/app/screens/nftDetail/index.tsx | 2 +- src/app/screens/ordinalDetail/index.tsx | 2 +- src/app/screens/ordinalsCollection/index.tsx | 2 +- src/app/screens/speedUpTransaction/index.tsx | 2 +- .../stores/wallet/actions/actionCreators.ts | 11 + src/app/stores/wallet/actions/types.ts | 9 + src/app/stores/wallet/reducer.ts | 7 + src/app/utils/constants.ts | 2 + src/app/utils/helper.ts | 34 +++ src/locales/en.json | 21 +- 21 files changed, 350 insertions(+), 191 deletions(-) diff --git a/src/app/components/accountHeader/index.tsx b/src/app/components/accountHeader/index.tsx index 2291c6ed3..1a95d033b 100644 --- a/src/app/components/accountHeader/index.tsx +++ b/src/app/components/accountHeader/index.tsx @@ -1,4 +1,3 @@ -import threeDotsIcon from '@assets/img/dots_three_vertical.svg'; import AccountRow from '@components/accountRow'; import PasswordInput from '@components/passwordInput'; import ResetWalletPrompt from '@components/resetWallet'; @@ -11,6 +10,7 @@ import styled from 'styled-components'; import OptionsDialog, { OPTIONS_DIALOG_WIDTH } from '@components/optionsDialog/optionsDialog'; import useSeedVault from '@hooks/useSeedVault'; import useWalletSelector from '@hooks/useWalletSelector'; +import { DotsThreeVertical } from '@phosphor-icons/react'; const SelectedAccountContainer = styled.div<{ showBorderBottom?: boolean }>((props) => ({ display: 'flex', @@ -177,7 +177,7 @@ function AccountHeaderComponent({ /> {!disableMenuOption && ( - Options + )} {showOptionsDialog && ( diff --git a/src/app/components/accountRow/index.tsx b/src/app/components/accountRow/index.tsx index b1d0f4058..6795ea816 100644 --- a/src/app/components/accountRow/index.tsx +++ b/src/app/components/accountRow/index.tsx @@ -1,4 +1,3 @@ -import threeDotsIcon from '@assets/img/dots_three_vertical.svg'; import LedgerBadge from '@assets/img/ledger/ledger_badge.svg'; import BarLoader from '@components/barLoader'; import BottomModal from '@components/bottomModal'; @@ -6,25 +5,26 @@ import ActionButton from '@components/button'; import OptionsDialog, { OPTIONS_DIALOG_WIDTH } from '@components/optionsDialog/optionsDialog'; import useWalletReducer from '@hooks/useWalletReducer'; import useWalletSelector from '@hooks/useWalletSelector'; -import { CaretDown } from '@phosphor-icons/react'; +import { CaretDown, DotsThreeVertical } from '@phosphor-icons/react'; import { Account } from '@secretkeylabs/xverse-core'; -import { LoaderSize } from '@utils/constants'; +import InputFeedback from '@ui-library/inputFeedback'; +import { LoaderSize, MAX_ACC_NAME_LENGTH } from '@utils/constants'; import { getAccountGradient } from '@utils/gradient'; -import { isHardwareAccount } from '@utils/helper'; +import { isLedgerAccount, validateAccountName } from '@utils/helper'; import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Tooltip } from 'react-tooltip'; import 'react-tooltip/dist/react-tooltip.css'; import styled from 'styled-components'; -interface GradientCircleProps { +const GradientCircle = styled.div<{ firstGradient: string; secondGradient: string; thirdGradient: string; -} -const GradientCircle = styled.div((props) => ({ - width: 20, - height: 20, + isBig: boolean; +}>((props) => ({ + width: props.isBig ? 32 : 20, + height: props.isBig ? 32 : 20, borderRadius: 25, background: `linear-gradient(to bottom,${props.firstGradient}, ${props.secondGradient},${props.thirdGradient} )`, })); @@ -54,29 +54,23 @@ const CurrentAccountTextContainer = styled.div((props) => ({ display: 'flex', flexDirection: 'row', alignItems: 'center', - gap: props.theme.spacing(4), -})); - -const CurrentSelectedAccountText = styled.h1((props) => ({ - ...props.theme.body_bold_m, - color: props.theme.colors.white_0, - textAlign: 'start', + gap: props.theme.space.xs, })); -const CurrentUnSelectedAccountText = styled.h1((props) => ({ - ...props.theme.body_m, - color: props.theme.colors.white_400, +const AccountName = styled.h1<{ isSelected: boolean }>((props) => ({ + ...props.theme.typography.body_bold_m, + color: props.isSelected ? props.theme.colors.white_0 : props.theme.colors.white_400, textAlign: 'start', })); const BarLoaderContainer = styled.div((props) => ({ width: 200, - paddingTop: props.theme.spacing(2), + paddingTop: props.theme.space.xxs, backgroundColor: 'transparent', })); export const StyledToolTip = styled(Tooltip)` - background-color: #ffffff; + background-color: ${(props) => props.theme.colors.white_0}; color: #12151e; border-radius: 8px; padding: 7px; @@ -102,19 +96,17 @@ const ModalContent = styled.div((props) => ({ const ModalDescription = styled.div((props) => ({ fontSize: '0.875rem', color: props.theme.colors.white_200, - marginBottom: props.theme.spacing(16), })); -const ModalControlsContainer = styled.div({ +const ModalControlsContainer = styled.div((props) => ({ display: 'flex', -}); + columnGap: props.theme.space.s, + marginTop: props.theme.space.xl, +})); -const ModalButtonContainer = styled.div((props) => ({ +const ModalButtonContainer = styled.div({ width: '100%', - '&:first-child': { - marginRight: props.theme.spacing(6), - }, -})); +}); const ButtonRow = styled.button` display: flex; @@ -136,6 +128,45 @@ const ButtonRow = styled.button` } `; +const InputLabel = styled.div((props) => ({ + ...props.theme.typography.body_medium_m, + color: props.theme.colors.white_200, + marginBottom: props.theme.space.xs, +})); + +const InputContainer = styled.div<{ withError?: boolean }>((props) => ({ + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + marginTop: props.theme.spacing(4), + marginBottom: props.theme.spacing(6), + border: `1px solid ${ + props.withError ? props.theme.colors.danger_dark_200 : props.theme.colors.white_800 + }`, + backgroundColor: props.theme.colors.elevation1, + borderRadius: props.theme.radius(1), + padding: props.theme.spacing(5), +})); + +const InputField = styled.input((props) => ({ + ...props.theme.typography.body_m, + backgroundColor: 'transparent', + color: props.theme.colors.white_0, + border: 'transparent', + width: '100%', + '&::-webkit-outer-spin-button': { + '-webkit-appearance': 'none', + margin: 0, + }, + '&::-webkit-inner-spin-button': { + '-webkit-appearance': 'none', + margin: 0, + }, + '&[type=number]': { + '-moz-appearance': 'textfield', + }, +})); + function AccountRow({ account, isSelected, @@ -153,16 +184,20 @@ function AccountRow({ const { t: optionsDialogTranslation } = useTranslation('translation', { keyPrefix: 'OPTIONS_DIALOG', }); - const { accountsList } = useWalletSelector(); + const { accountsList, ledgerAccountsList } = useWalletSelector(); const gradient = getAccountGradient(account?.stxAddress || account?.btcAddress!); const btcCopiedTooltipTimeoutRef = useRef(); const stxCopiedTooltipTimeoutRef = useRef(); const [showOptionsDialog, setShowOptionsDialog] = useState(false); const [showRemoveAccountModal, setShowRemoveAccountModal] = useState(false); + const [showRenameAccountModal, setShowRenameAccountModal] = useState(false); + const [accountName, setAccountName] = useState(''); + const [accountNameError, setAccountNameError] = useState(null); + const [isAccountNameChangeLoading, setIsAccountNameChangeLoading] = useState(false); const [optionsDialogIndents, setOptionsDialogIndents] = useState< { top: string; left: string } | undefined >(); - const { removeLedgerAccount } = useWalletReducer(); + const { removeLedgerAccount, renameAccount, updateLedgerAccounts } = useWalletReducer(); useEffect( () => () => { @@ -178,7 +213,7 @@ function AccountRow({ account?.bnsName ?? `${t('ACCOUNT_NAME')} ${`${(account?.id ?? 0) + 1}`}`; - return name.length > 20 ? `${name.slice(0, 20)}...` : name; + return name.length > MAX_ACC_NAME_LENGTH ? `${name.slice(0, MAX_ACC_NAME_LENGTH)}...` : name; }; const handleClick = () => { @@ -208,6 +243,14 @@ function AccountRow({ setShowRemoveAccountModal(false); }; + const handleRenameAccountModalOpen = () => { + setShowRenameAccountModal(true); + }; + + const handleRenameAccountModalClose = () => { + setShowRenameAccountModal(false); + }; + const handleRemoveLedgerAccount = async () => { if (!account) { return; @@ -222,6 +265,37 @@ function AccountRow({ } }; + const handleRenameAccount = async () => { + if (!account) { + return; + } + + const validationError = validateAccountName( + accountName, + optionsDialogTranslation, + accountsList, + ledgerAccountsList, + ); + if (validationError) { + setAccountNameError(validationError); + return; + } + + try { + setIsAccountNameChangeLoading(true); + if (isLedgerAccount(account)) { + await updateLedgerAccounts({ ...account, accountName }); + } else { + await renameAccount({ ...account, accountName }); + } + handleRenameAccountModalClose(); + } catch (err) { + console.error(err); + } finally { + setIsAccountNameChangeLoading(false); + } + }; + return ( @@ -229,18 +303,17 @@ function AccountRow({ firstGradient={gradient[0]} secondGradient={gradient[1]} thirdGradient={gradient[2]} + isBig={isAccountListView} /> {account && ( - {isSelected ? ( - {getName()} - ) : ( - {getName()} + {getName()} + {isLedgerAccount(account) && Ledger icon} + {isSelected && !disabledAccountSelect && !isAccountListView && ( + )} - {isHardwareAccount(account) && Ledger icon} - {isSelected && !disabledAccountSelect && } )} @@ -254,41 +327,81 @@ function AccountRow({ - {isAccountListView && isHardwareAccount(account) && ( + {isAccountListView && ( - Options + )} {showOptionsDialog && ( - - {optionsDialogTranslation('REMOVE_FROM_LIST')} + + {optionsDialogTranslation('RENAME_ACCOUNT')} + {isLedgerAccount(account) && ( + + {optionsDialogTranslation('REMOVE_FROM_LIST')} + + )} )} - - - {t('REMOVE_FROM_LIST_DESCRIPTION')} - - - + + {t('REMOVE_FROM_LIST_DESCRIPTION')} + + + + + + + + + + + )} + + {showRenameAccountModal && ( + + + {optionsDialogTranslation('RENAME_ACCOUNT_MODAL.LABEL')} + + setAccountName(e.target.value)} + autoFocus /> - - - - - - - + + {accountNameError && } + + + + + + + + )} ); } diff --git a/src/app/components/ledger/ledgerInput/index.tsx b/src/app/components/ledger/ledgerInput/index.tsx index be76d61bc..56dfc7e5e 100644 --- a/src/app/components/ledger/ledgerInput/index.tsx +++ b/src/app/components/ledger/ledgerInput/index.tsx @@ -1,21 +1,21 @@ +import InputFeedback from '@ui-library/inputFeedback'; import styled from 'styled-components'; -const LedgerInputContainer = styled.div((props) => ({ +const Container = styled.div({ width: '100%', display: 'flex', flexDirection: 'column', gap: '8px', -})); +}); -const LedgerInputLabel = styled.label((props) => ({ - ...props.theme.body_medium_m, +const Label = styled.label((props) => ({ + ...props.theme.typography.body_medium_m, })); -interface LedgerInputFieldProps { +const InputField = styled.input<{ error?: boolean; -} -const LedgerInputField = styled.input((props) => ({ - ...props.theme.body_medium_m, +}>((props) => ({ + ...props.theme.typography.body_medium_m, background: props.theme.colors.elevation_n1, border: `1px solid ${props.error ? props.theme.colors.feedback.error : '#303354'}`, borderRadius: '8px', @@ -24,12 +24,6 @@ const LedgerInputField = styled.input((props) => ({ transition: 'border 0.2s ease', })); -const ErrorText = styled.h1((props) => ({ - ...props.theme.body_xs, - color: props.theme.colors.feedback.error, - fontWeight: 600, -})); - interface Props { id: string; label?: string; @@ -41,9 +35,9 @@ interface Props { function LedgerInput({ id, label, placeholder, value, error, onChange: handleChange }: Props) { return ( - - {label} - + + - {!!error && {error}} - + {!!error && } + ); } diff --git a/src/app/components/topRow/index.tsx b/src/app/components/topRow/index.tsx index eb37ba91b..a6b12ea33 100644 --- a/src/app/components/topRow/index.tsx +++ b/src/app/components/topRow/index.tsx @@ -53,7 +53,7 @@ function TopRow({ title, onClick, showBackButton = true, className }: Props) { back button )} - {title} + {title && {title}} ); } diff --git a/src/app/hooks/useWalletReducer.ts b/src/app/hooks/useWalletReducer.ts index 119743fd6..d4fe25249 100644 --- a/src/app/hooks/useWalletReducer.ts +++ b/src/app/hooks/useWalletReducer.ts @@ -21,6 +21,7 @@ import { addAccountAction, fetchAccountAction, getActiveAccountsAction, + renameAccountAction, resetWalletAction, selectAccount, setWalletAction, @@ -81,6 +82,7 @@ const useWalletReducer = () => { stxAddress: walletAccounts[0].stxAddress, stxPublicKey: walletAccounts[0].stxPublicKey, bnsName: walletAccounts[0].bnsName, + accountName: walletAccounts[0].accountName, }; let selectedAccountData: Account | undefined; @@ -373,6 +375,21 @@ const useWalletReducer = () => { } }; + const renameAccount = async (updatedAccount: Account) => { + const newAccountsList = accountsList.map((account) => + account.accountType === updatedAccount.accountType && account.id === updatedAccount.id + ? updatedAccount + : account, + ); + const newSelectedAccount = + selectedAccount?.accountType === updatedAccount.accountType && + selectedAccount?.id === updatedAccount.id + ? { ...selectedAccount, accountName: updatedAccount.accountName } + : selectedAccount; + + dispatch(renameAccountAction(newAccountsList, newSelectedAccount)); + }; + return { unlockWallet, lockWallet, @@ -385,6 +402,7 @@ const useWalletReducer = () => { addLedgerAccount, removeLedgerAccount, updateLedgerAccounts, + renameAccount, toggleStxVisibility, }; }; diff --git a/src/app/layouts/sendLayout.tsx b/src/app/layouts/sendLayout.tsx index bc2d7fdd3..030fc266c 100644 --- a/src/app/layouts/sendLayout.tsx +++ b/src/app/layouts/sendLayout.tsx @@ -87,7 +87,7 @@ function SendLayout({ {isScreenLargerThanXs || showAccountHeader ? ( ) : ( - + )} diff --git a/src/app/screens/accountList/index.tsx b/src/app/screens/accountList/index.tsx index 9e50a792c..a1d4e1d83 100644 --- a/src/app/screens/accountList/index.tsx +++ b/src/app/screens/accountList/index.tsx @@ -1,12 +1,13 @@ import ConnectLedger from '@assets/img/dashboard/connect_ledger.svg'; -import Plus from '@assets/img/dashboard/plus.svg'; import { filterLedgerAccounts } from '@common/utils/ledger'; import AccountRow from '@components/accountRow'; +import ActionButton from '@components/button'; import Separator from '@components/separator'; import TopRow from '@components/topRow'; import { broadcastResetUserFlow } from '@hooks/useResetUserFlow'; import useWalletReducer from '@hooks/useWalletReducer'; import useWalletSelector from '@hooks/useWalletSelector'; +import { Plus } from '@phosphor-icons/react'; import { Account } from '@secretkeylabs/xverse-core'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -14,30 +15,16 @@ import { useNavigate } from 'react-router-dom'; import styled from 'styled-components'; export const Container = styled.div({ + height: '100%', display: 'flex', flexDirection: 'column', + justifyContent: 'space-between', overflowY: 'auto', '&::-webkit-scrollbar': { display: 'none', }, }); -const ButtonContainer = styled.button((props) => ({ - width: '100%', - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - background: 'transparent', - paddingTop: props.theme.spacing(4), - paddingBottom: props.theme.spacing(4), - paddingLeft: props.theme.spacing(11), - paddingRight: props.theme.spacing(11), - transition: 'background-color 0.2s ease', - ':hover': { - backgroundColor: props.theme.colors.elevation1, - }, -})); - const AccountContainer = styled.div((props) => ({ display: 'flex', flexDirection: 'column', @@ -47,38 +34,25 @@ const AccountContainer = styled.div((props) => ({ gap: props.theme.spacing(8), })); -const AddAccountContainer = styled.div((props) => ({ - display: 'flex', - height: 40, - width: 40, - borderRadius: 25, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: props.theme.colors.elevation1, - marginRight: props.theme.spacing(8), -})); - -const ButtonImage = styled.img({ - alignSelf: 'center', - transform: 'all', -}); - -const AddAccountText = styled.h1((props) => ({ - ...props.theme.body_m, - opacity: 0.8, - color: props.theme.colors.white_0, -})); - const ButtonsWrapper = styled.div( (props) => ` + display: flex; + flex-direction: column; position: sticky; bottom: 0; + row-gap: ${props.theme.space.s}; background-color: ${props.theme.colors.elevation0}; - margin-top: ${props.theme.spacing(8)}px; - margin-bottom: ${props.theme.spacing(11)}px; + padding: ${props.theme.space.m}; + padding-top: ${props.theme.space.l}; + padding-bottom: ${props.theme.space.xxl}; `, ); +const Title = styled.div((props) => ({ + ...props.theme.typography.headline_xs, + marginBottom: props.theme.space.m, +})); + function AccountList(): JSX.Element { const { t } = useTranslation('translation', { keyPrefix: 'ACCOUNT_SCREEN' }); const navigate = useNavigate(); @@ -118,33 +92,36 @@ function AccountList(): JSX.Element { return ( - - - {displayedAccountsList.map((account) => ( -
- - -
- ))} -
+
+ + + {t('TITLE')} + {displayedAccountsList.map((account) => ( +
+ + +
+ ))} +
+
- - - - - {t('NEW_ACCOUNT')} - - - - - - {t('LEDGER_ACCOUNT')} - + } + onPress={onCreateAccount} + text={t('NEW_ACCOUNT')} + transparent + /> + } + onPress={onImportLedgerAccount} + text={t('LEDGER_ACCOUNT')} + transparent + />
); diff --git a/src/app/screens/coinDashboard/index.tsx b/src/app/screens/coinDashboard/index.tsx index c312e9fc3..72ac13ce0 100644 --- a/src/app/screens/coinDashboard/index.tsx +++ b/src/app/screens/coinDashboard/index.tsx @@ -147,7 +147,7 @@ export default function CoinDashboard() { return ( <> - + {ft && ( diff --git a/src/app/screens/ledger/importLedgerAccount/index.tsx b/src/app/screens/ledger/importLedgerAccount/index.tsx index ff928558a..dc7edb272 100644 --- a/src/app/screens/ledger/importLedgerAccount/index.tsx +++ b/src/app/screens/ledger/importLedgerAccount/index.tsx @@ -13,7 +13,8 @@ import { importTaprootAccountFromLedger, } from '@secretkeylabs/xverse-core'; import { DEFAULT_TRANSITION_OPTIONS } from '@utils/constants'; -import { useEffect, useState } from 'react'; +import { validateAccountName } from '@utils/helper'; +import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import StepControls from './stepControls'; import Steps from './steps'; @@ -45,12 +46,14 @@ function ImportLedger(): JSX.Element { const [isStxAddressRejected, setIsStxAddressRejected] = useState(false); const [isBtcAddressRejected, setIsBtcAddressRejected] = useState(false); const [isOrdinalsAddressRejected, setIsOrdinalsAddressRejected] = useState(false); - const { t } = useTranslation('translation', { keyPrefix: 'LEDGER_IMPORT_SCREEN' }); + const { t } = useTranslation('translation', { + keyPrefix: 'OPTIONS_DIALOG', + }); const { addLedgerAccount, updateLedgerAccounts } = useWalletReducer(); const [selectedLedgerLiveOption, setSelectedLedgerLiveOption] = useState(null); const [isTogglerChecked, setIsTogglerChecked] = useState(false); - const { ledgerAccountsList, network } = useWalletSelector(); + const { accountsList, ledgerAccountsList, network } = useWalletSelector(); const transition = useTransition(currentStep, DEFAULT_TRANSITION_OPTIONS); const importBtcAccounts = async (showAddress: boolean, masterFingerPrint?: string) => { @@ -335,6 +338,12 @@ function ImportLedger(): JSX.Element { return; } + const validationError = validateAccountName(accountName, t, accountsList, ledgerAccountsList); + if (validationError) { + setAccountNameError(validationError); + return; + } + try { setIsButtonDisabled(true); const accountToUpdate = ledgerAccountsList.find( @@ -346,10 +355,10 @@ function ImportLedger(): JSX.Element { const updatedAccount: Account = { ...accountToUpdate, accountName }; await updateLedgerAccounts(updatedAccount); await delay(1000); - setIsButtonDisabled(false); handleClickNext(); } catch (err) { console.error(err); + } finally { setIsButtonDisabled(false); } }; @@ -380,26 +389,6 @@ function ImportLedger(): JSX.Element { } }; - const validateAccountName = () => { - const MAX_LENGTH = 20; - - if (accountName.length > MAX_LENGTH) { - setAccountNameError(t('LEDGER_ADD_ACCOUNT_NAME.ERRORS.MAX_LENGTH', { number: MAX_LENGTH })); - return; - } - - if (ledgerAccountsList.find((account) => account.accountName === accountName)) { - setAccountNameError(t('LEDGER_ADD_ACCOUNT_NAME.ERRORS.ALREADY_EXISTS')); - return; - } - - setAccountNameError(undefined); - }; - - useEffect(() => { - validateAccountName(); - }, [accountName]); - return ( diff --git a/src/app/screens/ledger/importLedgerAccount/stepControls.tsx b/src/app/screens/ledger/importLedgerAccount/stepControls.tsx index 3ff9ae7c3..4e7b80a0a 100644 --- a/src/app/screens/ledger/importLedgerAccount/stepControls.tsx +++ b/src/app/screens/ledger/importLedgerAccount/stepControls.tsx @@ -143,7 +143,7 @@ function StepControls({ case ImportLedgerSteps.ADD_ACCOUNT_NAME: return ( ) : ( - + )} diff --git a/src/app/screens/nftDetail/index.tsx b/src/app/screens/nftDetail/index.tsx index f06155188..5073e9234 100644 --- a/src/app/screens/nftDetail/index.tsx +++ b/src/app/screens/nftDetail/index.tsx @@ -576,7 +576,7 @@ function NftDetailScreen() { {isGalleryOpen ? ( ) : ( - + )} {isGalleryOpen ? galleryView : extensionView} {!isGalleryOpen && ( diff --git a/src/app/screens/ordinalDetail/index.tsx b/src/app/screens/ordinalDetail/index.tsx index f07db5682..ac8e6aca8 100644 --- a/src/app/screens/ordinalDetail/index.tsx +++ b/src/app/screens/ordinalDetail/index.tsx @@ -819,7 +819,7 @@ function OrdinalDetailScreen() { {isGalleryOpen ? ( ) : ( - + )} {showSendOridnalsAlert && ( ) : ( - + )} diff --git a/src/app/screens/speedUpTransaction/index.tsx b/src/app/screens/speedUpTransaction/index.tsx index 856604557..5ecbcba78 100644 --- a/src/app/screens/speedUpTransaction/index.tsx +++ b/src/app/screens/speedUpTransaction/index.tsx @@ -357,7 +357,7 @@ function SpeedUpTransactionScreen() { return ( <> - + {isLoading ? ( diff --git a/src/app/stores/wallet/actions/actionCreators.ts b/src/app/stores/wallet/actions/actionCreators.ts index e9802437c..64292c2fb 100644 --- a/src/app/stores/wallet/actions/actionCreators.ts +++ b/src/app/stores/wallet/actions/actionCreators.ts @@ -274,6 +274,17 @@ export function setWalletUnlockedAction(isUnlocked: boolean): actions.SetWalletU }; } +export function renameAccountAction( + accountsList: Account[], + selectedAccount: Account | null, +): actions.RenameAccount { + return { + type: actions.RenameAccountKey, + accountsList, + selectedAccount, + }; +} + export function setWalletHideStxAction(hideStx: boolean): actions.SetWalletHideStx { return { type: actions.SetWalletHideStxKey, diff --git a/src/app/stores/wallet/actions/types.ts b/src/app/stores/wallet/actions/types.ts index 8d1ce4570..e874c9baa 100644 --- a/src/app/stores/wallet/actions/types.ts +++ b/src/app/stores/wallet/actions/types.ts @@ -37,6 +37,7 @@ export const UpdateLedgerAccountsKey = 'UpdateLedgerAccountsKey'; export const SetBrcCoinsListKey = 'SetBrcCoinsList'; export const SetWalletLockPeriodKey = 'SetWalletLockPeriod'; export const SetWalletUnlockedKey = 'SetWalletUnlocked'; +export const RenameAccountKey = 'RenameAccountKey'; export const SetWalletHideStxKey = 'SetWalletHideStx'; @@ -228,10 +229,17 @@ export interface SetWalletUnlocked { isUnlocked: boolean; } +export interface RenameAccount { + type: typeof RenameAccountKey; + accountsList: Account[]; + selectedAccount: Account | null; +} + export interface SetWalletHideStx { type: typeof SetWalletHideStxKey; hideStx: boolean; } + export type WalletActions = | SetWallet | ResetWallet @@ -259,4 +267,5 @@ export type WalletActions = | SetWalletLockPeriod | SetRareSatsNoticeDismissed | SetWalletUnlocked + | RenameAccount | SetWalletHideStx; diff --git a/src/app/stores/wallet/reducer.ts b/src/app/stores/wallet/reducer.ts index 083c4c7c6..a9ab6bd80 100644 --- a/src/app/stores/wallet/reducer.ts +++ b/src/app/stores/wallet/reducer.ts @@ -12,6 +12,7 @@ import { FetchAccountKey, GetActiveAccountsKey, RareSatsNoticeDismissedKey, + RenameAccountKey, ResetWalletKey, SelectAccountKey, SetBrcCoinsListKey, @@ -265,6 +266,12 @@ const walletReducer = ( ...state, isUnlocked: action.isUnlocked, }; + case RenameAccountKey: + return { + ...state, + accountsList: action.accountsList, + selectedAccount: action.selectedAccount, + }; case SetWalletHideStxKey: return { ...state, diff --git a/src/app/utils/constants.ts b/src/app/utils/constants.ts index cbc11f1e7..1323d438e 100644 --- a/src/app/utils/constants.ts +++ b/src/app/utils/constants.ts @@ -62,5 +62,7 @@ export const DEFAULT_TRANSITION_OPTIONS = { }, }; +export const MAX_ACC_NAME_LENGTH = 20; + // UI export const EMPTY_LABEL = '--'; diff --git a/src/app/utils/helper.ts b/src/app/utils/helper.ts index 4e85c5f19..034422ceb 100644 --- a/src/app/utils/helper.ts +++ b/src/app/utils/helper.ts @@ -9,9 +9,11 @@ import { } from '@secretkeylabs/xverse-core'; import { ChainID } from '@stacks/transactions'; import BigNumber from 'bignumber.js'; +import { TFunction } from 'react-i18next'; import { BTC_TRANSACTION_STATUS_URL, BTC_TRANSACTION_TESTNET_STATUS_URL, + MAX_ACC_NAME_LENGTH, TRANSACTION_STATUS_URL, } from './constants'; @@ -203,3 +205,35 @@ export const handleKeyDownFeeRateInput = (e: React.KeyboardEvent, + accountsList: Account[], + ledgerAccountsList: Account[], +) => { + const regex = /^[a-zA-Z0-9 ]*$/; + + if (!name.length) { + return t('RENAME_ACCOUNT_MODAL.REQUIRED_ERR'); + } + + if (name.length > MAX_ACC_NAME_LENGTH) { + return t('RENAME_ACCOUNT_MODAL.MAX_SYMBOLS_ERR', { + maxLength: MAX_ACC_NAME_LENGTH, + }); + } + + if ( + ledgerAccountsList.find((account) => account.accountName === name) || + accountsList.find((account) => account.accountName === name) + ) { + return t('RENAME_ACCOUNT_MODAL.ALREADY_EXISTS_ERR'); + } + + if (!regex.test(name)) { + return t('RENAME_ACCOUNT_MODAL.PROHIBITED_SYMBOLS_ERR'); + } + + return null; +}; diff --git a/src/locales/en.json b/src/locales/en.json index d7fe25d36..7cb2f7a7f 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -76,11 +76,7 @@ "TITLE": "Name your Ledger wallet", "SUBTITLE": "You can give your wallet a custom name.", "INPUT": "Wallet name", - "PLACEHOLDER": "My ledger", - "ERRORS": { - "MAX_LENGTH": "Account name should not be longer than {{number}} characters.", - "ALREADY_EXISTS": "Account with the same name already exists. Please choose another name." - } + "PLACEHOLDER": "My ledger" }, "LEDGER_IMPORT_END": { "TITLE": "Your wallet is ready!", @@ -208,6 +204,7 @@ "SEND": "Send", "SWAP": "Swap", "BUY": "Buy", + "SAVE": "Save", "MANAGE_TOKEN": "Manage token list", "STACKS_AND_TOKEN": "Stacks NFTs & SIP-10 tokens", "ORDINALS": "Ordinals & BRC-20 Tokens", @@ -228,8 +225,8 @@ "DESCRIPTION": "Select which assets you would like to see on your dashboard." }, "ACCOUNT_SCREEN": { - "CHANGE_ACCOUNT": "Change account", - "NEW_ACCOUNT": "Create a new account", + "TITLE": "Accounts", + "NEW_ACCOUNT": "Generate account", "LEDGER_ACCOUNT": "Connect hardware wallet" }, "RECEIVE": { @@ -763,7 +760,15 @@ "SWITCH_ACCOUNT": "Switch Account", "LOCK": "Lock", "RESET_WALLET": "Reset Wallet", - "REMOVE_FROM_LIST": "Remove from list" + "REMOVE_FROM_LIST": "Remove from list", + "RENAME_ACCOUNT": "Rename account", + "RENAME_ACCOUNT_MODAL": { + "LABEL": "Account name", + "REQUIRED_ERR": "Account name is required", + "MAX_SYMBOLS_ERR": "Account name cannot be more than {{maxLength}} characters", + "PROHIBITED_SYMBOLS_ERR": "Account name can only contain alphabetic and numeric characters and space", + "ALREADY_EXISTS_ERR": "Account with the same name already exists. Please choose another name." + } }, "BUY_SCREEN": { "BUY": "Buy", From 35d76d0bbb30eae37150e7736609b4c4d451496b Mon Sep 17 00:00:00 2001 From: Jordan K <65149726+jordankzf@users.noreply.github.com> Date: Thu, 25 Jan 2024 00:01:15 +0800 Subject: [PATCH 07/20] [ENG-3614] feat: Ability to manage a BRC-20 token's visibility (hide) (#765) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add UpdateVisibleBrc20CoinListKey * Bicoin toggled first attempt * Fix ScrollableContainer not taking full height * Don't override existing token's visiblity setting * Hide brc20Coins if visible === false * Delegate logic upstream to xverse-core * Copy pasta * Rename getFtTicker to more generic name * CSS showDivider alternative * Revert "CSS showDivider alternative" This reverts commit 97dde6ed28a27f4879f43e790ed7972a7578c604. * Bump to beta version of xverse-core * xverse-core will default visible to true * Bump xverse-core again * Bump xverse-core again * BRC-20 fiat value support * Fix triple source of truth bug 🤪 * Update Brc20Token type for getBrc20Tokens * Remove fallback * Concurrent fetching * Stop naively assuming brc20 tokens have no principal * Bump xverse-core * fix: brc20 detail page should have round token image * chore: bump core beta version * refactor: css only for manage token dividers * refactor: remove a lot of fluff and use FiatAmountText component * Safe type assertion * FREE THYSELF FROM TYPECASTS * Tim's changes * Bump xverse-core to v9.1.0 * tokenFiatRate can be passed directly * Fix closing extension does not apply changes bug --------- Co-authored-by: Tim Man --- package-lock.json | 16 +-- package.json | 2 +- src/app/components/fiatAmountText/index.tsx | 8 +- src/app/components/tokenImage/index.tsx | 2 +- src/app/components/tokenTile/index.tsx | 135 ++++-------------- src/app/hooks/queries/useBtcCoinsBalance.ts | 77 ++++++++-- src/app/screens/coinDashboard/index.tsx | 2 +- src/app/screens/home/balanceCard/index.tsx | 14 ++ src/app/screens/home/index.tsx | 24 ++-- .../screens/manageTokens/coinItem/index.tsx | 98 ++++++------- src/app/screens/manageTokens/index.tsx | 98 ++++++++----- 11 files changed, 242 insertions(+), 234 deletions(-) diff --git a/package-lock.json b/package-lock.json index d80e31636..c1fcce149 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@phosphor-icons/react": "^2.0.10", "@react-spring/web": "^9.6.1", "@scure/btc-signer": "1.2.1", - "@secretkeylabs/xverse-core": "9.0.0", + "@secretkeylabs/xverse-core": "9.1.0", "@stacks/connect": "7.4.1", "@stacks/stacks-blockchain-api-types": "6.1.1", "@stacks/transactions": "6.9.0", @@ -1735,9 +1735,9 @@ } }, "node_modules/@secretkeylabs/xverse-core": { - "version": "9.0.0", - "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/9.0.0/125e215d0df4ea6fe030bacb83f3da8d7e2d7b2d", - "integrity": "sha512-fpc9urv2JrtxDYt7TcEK8oA4V8tDVVRhp08yIDy664E00u29JQMtwZ8gdtiVTmTzVl8/yV5ELZHoj3SYKDPc2g==", + "version": "9.1.0", + "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/9.1.0/4cd1e5ec21fe43d21f8c91bb9d7cd5521f8f1169", + "integrity": "sha512-cub6HJ79eAJY6tw7Pi8r1wyuitX8eEQQFHJMnVLX8EKkubCbYkC+DblVmCebH1P9L4I41BXp5XifpN366uwNvQ==", "license": "ISC", "dependencies": { "@bitcoinerlab/secp256k1": "^1.0.2", @@ -15448,9 +15448,9 @@ } }, "@secretkeylabs/xverse-core": { - "version": "9.0.0", - "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/9.0.0/125e215d0df4ea6fe030bacb83f3da8d7e2d7b2d", - "integrity": "sha512-fpc9urv2JrtxDYt7TcEK8oA4V8tDVVRhp08yIDy664E00u29JQMtwZ8gdtiVTmTzVl8/yV5ELZHoj3SYKDPc2g==", + "version": "9.1.0", + "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/9.1.0/4cd1e5ec21fe43d21f8c91bb9d7cd5521f8f1169", + "integrity": "sha512-cub6HJ79eAJY6tw7Pi8r1wyuitX8eEQQFHJMnVLX8EKkubCbYkC+DblVmCebH1P9L4I41BXp5XifpN366uwNvQ==", "requires": { "@bitcoinerlab/secp256k1": "^1.0.2", "@noble/curves": "^1.2.0", @@ -16932,7 +16932,7 @@ "requires": { "@types/bn.js": "^5.1.0", "@types/node": "^18.0.4", - "buffer": "^6.0.3" + "buffer": "6.0.3" } }, "@stacks/network": { diff --git a/package.json b/package.json index be9b81a8a..910e00b9e 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "@phosphor-icons/react": "^2.0.10", "@react-spring/web": "^9.6.1", "@scure/btc-signer": "1.2.1", - "@secretkeylabs/xverse-core": "9.0.0", + "@secretkeylabs/xverse-core": "9.1.0", "@stacks/connect": "7.4.1", "@stacks/stacks-blockchain-api-types": "6.1.1", "@stacks/transactions": "6.9.0", diff --git a/src/app/components/fiatAmountText/index.tsx b/src/app/components/fiatAmountText/index.tsx index ee72e6edf..913f65883 100644 --- a/src/app/components/fiatAmountText/index.tsx +++ b/src/app/components/fiatAmountText/index.tsx @@ -9,13 +9,19 @@ export function FiatAmountText({ fiatCurrency, }: { className?: string; - fiatAmount: BigNumber; + fiatAmount?: BigNumber; fiatCurrency: SupportedCurrency; }) { if (!fiatAmount || !fiatCurrency) { return null; } + if (fiatAmount.isEqualTo(0)) { + return ( + {`${currencySymbolMap[fiatCurrency]}0.00 ${fiatCurrency}`} + ); + } + if (fiatAmount.isLessThan(0.01)) { return ( ((prop justifyContent: 'center', height: props.size ?? 44, width: props.size ?? 44, - borderRadius: props.round ? '50%' : props.theme.radius(2), + borderRadius: '50%', backgroundColor: props.color, })); diff --git a/src/app/components/tokenTile/index.tsx b/src/app/components/tokenTile/index.tsx index acdb7883a..430827aa9 100644 --- a/src/app/components/tokenTile/index.tsx +++ b/src/app/components/tokenTile/index.tsx @@ -1,6 +1,7 @@ import { BetterBarLoader } from '@components/barLoader'; +import { StyledFiatAmountText } from '@components/fiatAmountText'; import type { FungibleToken } from '@secretkeylabs/xverse-core'; -import { currencySymbolMap, microstacksToStx, satsToBtc } from '@secretkeylabs/xverse-core'; +import { microstacksToStx, satsToBtc } from '@secretkeylabs/xverse-core'; import { StoreState } from '@stores/index'; import { CurrencyTypes } from '@utils/constants'; import { getTicker } from '@utils/helper'; @@ -53,7 +54,7 @@ const TickerIconContainer = styled.div((props) => ({ })); const TickerIconText = styled.h1((props) => ({ - ...props.theme.body_bold_m, + ...props.theme.typography.body_bold_m, color: props.theme.colors.white_0, textAlign: 'center', wordBreak: 'break-all', @@ -73,7 +74,9 @@ const TextContainer = styled.div((props) => ({ })); const AmountContainer = styled.div({ - alignContent: 'flex-end', + display: 'flex', + flexDirection: 'column', + alignItems: 'flex-end', }); const LoaderMainContainer = styled.div({ @@ -91,7 +94,7 @@ const LoaderImageContainer = styled.div((props) => ({ })); const CoinTickerText = styled.h1((props) => ({ - ...props.theme.body_bold_m, + ...props.theme.typography.body_bold_m, color: props.theme.colors.white_0, })); @@ -105,15 +108,8 @@ const SubText = styled.h1((props) => ({ textOverflow: 'ellipsis', })); -const FiatAmountText = styled.h1((props) => ({ - ...props.theme.headline_category_s, - color: props.theme.colors.white_400, - fontSize: 12, - textAlign: 'end', -})); - const CoinBalanceText = styled.h1((props) => ({ - ...props.theme.body_medium_m, + ...props.theme.typography.body_medium_m, color: props.theme.colors.white_0, textAlign: 'end', })); @@ -192,107 +188,21 @@ function TokenTile({ } } - const renderStxBalanceView = ( - {value}} - /> - ); - - const renderBtcBalanceView = ( - {value}} - /> - ); - - const renderFtBalanceView = ( - {value}} - /> - ); - - function getBalance() { - if (currency === 'STX') return renderStxBalanceView; - if (currency === 'BTC') return renderBtcBalanceView; - return renderFtBalanceView; - } - - function getFtFiatEquivalent() { - if (fungibleToken?.tokenFiatRate) { - const balance = new BigNumber(getFtBalance(fungibleToken)); - const rate = new BigNumber(fungibleToken.tokenFiatRate); - return balance.multipliedBy(rate).toFixed(2).toString(); - } - return ''; - } - - function getFiatEquivalent() { + function getFiatEquivalent(): BigNumber | undefined { switch (currency) { case 'STX': return microstacksToStx(new BigNumber(stxBalance)) - .multipliedBy(new BigNumber(stxBtcRate)) - .multipliedBy(new BigNumber(btcFiatRate)) - .toFixed(2) - .toString(); + .multipliedBy(stxBtcRate) + .multipliedBy(btcFiatRate); case 'BTC': - return satsToBtc(new BigNumber(btcBalance)) - .multipliedBy(new BigNumber(btcFiatRate)) - .toFixed(2) - .toString(); + return satsToBtc(new BigNumber(btcBalance)).multipliedBy(btcFiatRate); case 'FT': - return getFtFiatEquivalent(); - default: - return ''; - } - } - - function renderFiatEquivalentView() { - switch (currency) { - case 'STX': - return ( - {value}} - /> - ); - case 'BTC': - return ( - {value}} - /> - ); - case 'FT': - if (fungibleToken?.tokenFiatRate) { - return ( - {value}} - /> - ); - } - break; + case 'brc20': + return fungibleToken?.tokenFiatRate + ? new BigNumber(getFtBalance(fungibleToken)).multipliedBy(fungibleToken.tokenFiatRate) + : undefined; default: + return undefined; } } @@ -331,7 +241,7 @@ function TokenTile({ onPress({ coin: currency as CurrencyTypes, ft: fungibleToken && fungibleToken.principal, - brc20Ft: !fungibleToken?.principal ? fungibleToken?.name : undefined, + brc20Ft: currency === 'brc20' ? fungibleToken?.principal : undefined, }); }; @@ -355,8 +265,13 @@ function TokenTile({ ) : ( - {getBalance()} - {renderFiatEquivalentView()} + {value}} + /> + )} diff --git a/src/app/hooks/queries/useBtcCoinsBalance.ts b/src/app/hooks/queries/useBtcCoinsBalance.ts index 9a8495f1e..f294d78f1 100644 --- a/src/app/hooks/queries/useBtcCoinsBalance.ts +++ b/src/app/hooks/queries/useBtcCoinsBalance.ts @@ -1,22 +1,81 @@ import useWalletSelector from '@hooks/useWalletSelector'; -import { getOrdinalsFtBalance } from '@secretkeylabs/xverse-core'; +import { + Brc20Token, + FungibleToken, + getBrc20Tokens, + getOrdinalsFtBalance, +} from '@secretkeylabs/xverse-core'; import { setBrcCoinsDataAction } from '@stores/wallet/actions/actionCreators'; import { useQuery } from '@tanstack/react-query'; import { useDispatch } from 'react-redux'; +const brc20TokenToFungibleToken = (coin: Brc20Token): FungibleToken => ({ + name: coin.name, + principal: coin.ticker ?? coin.name, + balance: '0', + total_sent: '', + total_received: '', + assetName: coin.name ?? coin.ticker, + visible: false, + ticker: coin.ticker, +}); + const useBtcCoinBalance = () => { const dispatch = useDispatch(); - const { ordinalsAddress, network } = useWalletSelector(); + const { ordinalsAddress, network, brcCoinsList, fiatCurrency } = useWalletSelector(); + // For future lost souls: + // brcCoinsList = current local store + // ordinalsFtBalance = latest brc20 balance + // brc20Tokens = brc20 coin metadata, + // WITH additional supported tokens returned even if not passed const fetchBrcCoinsBalances = async () => { try { - const list = await getOrdinalsFtBalance(network.type, ordinalsAddress); - dispatch( - setBrcCoinsDataAction( - list.map((brcToken) => ({ ...brcToken, ticker: brcToken.ticker?.toUpperCase() })), - ), - ); - return list; + // Fetch concurrently to speed things up + const [ordinalsFtBalance, brc20Tokens] = await Promise.all([ + getOrdinalsFtBalance(network.type, ordinalsAddress), + getBrc20Tokens(network.type, brcCoinsList?.map((o) => o.ticker!) ?? [], fiatCurrency), + ]); + + const brcCoinsListMap = new Map(brcCoinsList?.map((token) => [token.ticker, token])); + + const mergedList: FungibleToken[] = ordinalsFtBalance.map((newToken) => { + const existingToken = brcCoinsListMap.get(newToken.ticker); + + const reconstitutedFt = { + ...existingToken, + ...newToken, + // The `visible` property from `xverse-core` defaults to true. + // We override `visible` to ensure that the existing state is preserved. + ...(existingToken ? { visible: existingToken.visible } : {}), + }; + + return reconstitutedFt; + }); + + brc20Tokens?.forEach((b) => { + const existingToken = brcCoinsListMap.get(b.ticker); + const pendingToken = mergedList?.find((m) => m.ticker === b.ticker); + const tokenFiatRate = Number(b?.tokenFiatRate); + + // No duplicates + if (pendingToken) { + pendingToken.tokenFiatRate = tokenFiatRate; + return; + } + + if (existingToken) { + mergedList.push({ + ...existingToken, + tokenFiatRate, + }); + } else { + mergedList.push(brc20TokenToFungibleToken(b)); + } + }); + + dispatch(setBrcCoinsDataAction(mergedList)); + return mergedList; } catch (e: any) { return Promise.reject(e); } diff --git a/src/app/screens/coinDashboard/index.tsx b/src/app/screens/coinDashboard/index.tsx index 72ac13ce0..637eeebd7 100644 --- a/src/app/screens/coinDashboard/index.tsx +++ b/src/app/screens/coinDashboard/index.tsx @@ -123,7 +123,7 @@ export default function CoinDashboard() { const ft = coinsList?.find((ftCoin) => ftCoin.principal === ftAddress); let brc20Ft: FungibleToken | undefined; if (brc20FtName) { - brc20Ft = brcCoinsList?.find((brc20FtCoin) => brc20FtCoin.name === brc20FtName); + brc20Ft = brcCoinsList?.find((brc20FtCoin) => brc20FtCoin.principal === brc20FtName); } const openContractDeployment = () => { diff --git a/src/app/screens/home/balanceCard/index.tsx b/src/app/screens/home/balanceCard/index.tsx index 03d1784b8..087e65b6f 100644 --- a/src/app/screens/home/balanceCard/index.tsx +++ b/src/app/screens/home/balanceCard/index.tsx @@ -77,6 +77,7 @@ function BalanceCard(props: BalanceCardProps) { stxAddress, hideStx, coinsList, + brcCoinsList, } = useWalletSelector(); const { isLoading, isRefetching } = props; @@ -109,6 +110,19 @@ function BalanceCard(props: BalanceCardProps) { }, totalBalance); } + if (brcCoinsList) { + totalBalance = brcCoinsList.reduce((acc, coin) => { + if (coin.visible && coin.tokenFiatRate) { + const coinFiatValue = new BigNumber(coin.balance).multipliedBy( + new BigNumber(coin.tokenFiatRate), + ); + return acc.plus(coinFiatValue); + } + + return acc; + }, totalBalance); + } + return totalBalance.toNumber().toFixed(2); } diff --git a/src/app/screens/home/index.tsx b/src/app/screens/home/index.tsx index 7cdf45d99..4335225bc 100644 --- a/src/app/screens/home/index.tsx +++ b/src/app/screens/home/index.tsx @@ -521,17 +521,19 @@ function Home() { onPress={handleTokenPressed} /> ))} - {brcCoinsList?.map((coin) => ( - - ))} + {brcCoinsList + ?.filter((ft) => ft.visible) + .map((coin) => ( + + ))} )} ({ - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - marginTop: props.theme.spacing(8), -})); +const RowContainer = styled.div` + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding: ${(props) => props.theme.space.m} 0; +`; const CoinContainer = styled.div({ display: 'flex', @@ -21,10 +19,6 @@ const CoinContainer = styled.div({ alignItems: 'center', }); -const BottomContainer = styled.div({ - marginBottom: 30, -}); - const CoinIcon = styled.img((props) => ({ marginRight: props.theme.spacing(7), width: 32, @@ -54,7 +48,7 @@ const TickerIconContainer = styled.div((props) => ({ })); const TickerText = styled.h1((props) => ({ - ...props.theme.body_xs, + ...props.theme.typography.body_s, color: props.theme.colors.white_0, textAlign: 'center', wordBreak: 'break-all', @@ -63,67 +57,65 @@ const TickerText = styled.h1((props) => ({ })); const SelectedCoinTitleText = styled.h1((props) => ({ - ...props.theme.body_bold_m, + ...props.theme.typography.body_bold_m, color: props.theme.colors.white_0, textAlign: 'center', })); const UnSelectedCoinTitleText = styled.h1((props) => ({ - ...props.theme.body_m, + ...props.theme.typography.body_m, color: props.theme.colors.white_400, textAlign: 'center', })); interface Props { - coin: Pick; + id: string; + name: string; + image?: string; + ticker?: string; disabled: boolean; - toggled(enabled: boolean, coin: Pick): void; + toggled(enabled: boolean, coinName: string, coinKey: string): void; enabled?: boolean; - showDivider: boolean; } -function CoinItem({ coin, disabled, toggled, enabled, showDivider }: Props) { +function CoinItem({ id, name, image, ticker, disabled, toggled, enabled }: Props) { const [isEnabled, setIsEnabled] = useState(enabled); const toggleSwitch = () => { setIsEnabled((previousState) => !previousState); - toggled(!isEnabled, coin); + toggled(!isEnabled, name, id); }; - function getFtTicker() { - const { ticker } = coin; - return !ticker && coin.name ? getTicker(coin.name) : ticker; + function getTickerName() { + return !ticker && name ? getTicker(name) : ticker; } - const background = stc(getFtTicker()); + const background = stc(getTickerName()); return ( - <> - - - {coin.image ? ( - - ) : ( - - {getFtTicker()} - - )} - {isEnabled ? ( - {coin.name} - ) : ( - {coin.name} - )} - - - - {showDivider ? : } - + + + {image ? ( + + ) : ( + + {getTickerName()} + + )} + {isEnabled ? ( + {name} + ) : ( + {name} + )} + + + ); } diff --git a/src/app/screens/manageTokens/index.tsx b/src/app/screens/manageTokens/index.tsx index 5eacf4029..4aba66754 100644 --- a/src/app/screens/manageTokens/index.tsx +++ b/src/app/screens/manageTokens/index.tsx @@ -6,7 +6,10 @@ import useWalletSelector from '@hooks/useWalletSelector'; import CoinItem from '@screens/manageTokens/coinItem'; import { Coin, FungibleToken } from '@secretkeylabs/xverse-core'; import { StoreState } from '@stores/index'; -import { FetchUpdatedVisibleCoinListAction } from '@stores/wallet/actions/actionCreators'; +import { + FetchUpdatedVisibleCoinListAction, + setBrcCoinsDataAction, +} from '@stores/wallet/actions/actionCreators'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; @@ -20,6 +23,11 @@ const TokenContainer = styled.div` &::-webkit-scrollbar { display: none; } + + margin-bottom: ${(props) => props.theme.space.xl}; + > *:not(:last-child) { + border-bottom: 1px solid ${(props) => props.theme.colors.elevation3}; + } `; const Container = styled.div({ @@ -28,6 +36,7 @@ const Container = styled.div({ overflow: 'hidden', paddingLeft: 16, paddingRight: 16, + height: '100%', }); const ScrollableContainer = styled.div` @@ -74,20 +83,17 @@ const Description = styled.h1((props) => ({ function Stacks() { const { hideStx } = useWalletSelector(); const { toggleStxVisibility } = useWalletReducer(); - const tickerConstant = 'stx'; + const tickerConstant = 'STX'; return ( ); } @@ -99,45 +105,55 @@ enum Protocols { function ManageTokens() { const { t } = useTranslation('translation', { keyPrefix: 'TOKEN_SCREEN' }); - const { coinsList, coins } = useSelector((state: StoreState) => state.walletState); + const { coinsList, coins, brcCoinsList } = useSelector((state: StoreState) => state.walletState); const [selectedProtocol, setSelectedProtocol] = useState(Protocols.SIP_10); const navigate = useNavigate(); const dispatch = useDispatch(); - const toggled = (isEnabled: boolean, coin: Pick) => { + const toggled = (isEnabled: boolean, coinName, coinKey) => { /* if coins exists in list of fungible token, update the visible property otherwise add coin in list if coin is set to visible */ - const coinToBeUpdated: FungibleToken | undefined = coinsList?.find( - (ft) => ft.principal === coin.contract, - ); - if (coinToBeUpdated) coinToBeUpdated.visible = isEnabled; - else if (!coinToBeUpdated && isEnabled) { + + const coinToBeUpdated = + coinsList?.find((ft) => ft.principal === coinKey) ?? + brcCoinsList?.find((ft) => ft.principal === coinKey); + + if (coinToBeUpdated) { + coinToBeUpdated.visible = isEnabled; + } else if (isEnabled) { const coinToBeAdded: FungibleToken = { - name: coin?.name, + name: coinName, visible: true, - principal: coin?.contract, + principal: coinKey, balance: '0', total_sent: '', total_received: '', assetName: '', }; - coinsList?.push(coinToBeAdded); + if (selectedProtocol === Protocols.SIP_10) { + coinsList?.push(coinToBeAdded); + } else if (selectedProtocol === Protocols.BRC_20) { + brcCoinsList?.push(coinToBeAdded); + } } - if (coinsList) { + + if (coinsList && selectedProtocol === Protocols.SIP_10) { const modifiedCoinsList = [...coinsList]; dispatch(FetchUpdatedVisibleCoinListAction(modifiedCoinsList)); } + + if (brcCoinsList && selectedProtocol === Protocols.BRC_20) { + const modifiedCoinsList = [...brcCoinsList]; + dispatch(setBrcCoinsDataAction(modifiedCoinsList)); + } }; const handleBackButtonClick = () => { navigate('/'); }; - function showDivider(index: number): boolean { - if (coins) return !(index === coins.length - 1); - return false; - } + const selectedCoins = selectedProtocol === Protocols.SIP_10 ? coins : brcCoinsList; return ( <> @@ -153,26 +169,30 @@ function ManageTokens() { > {Protocols.SIP_10} - {/* To be uncommented when brc-20 tokens are supported */} - {/* */} + - - {coins?.map((coin, index) => ( - - ))} + {selectedProtocol === Protocols.SIP_10 && } + {selectedCoins?.map((coin: FungibleToken | Coin) => { + const coinId = 'principal' in coin ? coin.principal : coin.contract; + return ( + + ); + })} From e3668178eb7900d9b730e81b792386538839efe1 Mon Sep 17 00:00:00 2001 From: Jordan K <65149726+jordankzf@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:18:17 +0800 Subject: [PATCH 08/20] Revert to procedural calls (#769) --- src/app/hooks/queries/useBtcCoinsBalance.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/app/hooks/queries/useBtcCoinsBalance.ts b/src/app/hooks/queries/useBtcCoinsBalance.ts index f294d78f1..e78df750b 100644 --- a/src/app/hooks/queries/useBtcCoinsBalance.ts +++ b/src/app/hooks/queries/useBtcCoinsBalance.ts @@ -31,11 +31,12 @@ const useBtcCoinBalance = () => { // WITH additional supported tokens returned even if not passed const fetchBrcCoinsBalances = async () => { try { - // Fetch concurrently to speed things up - const [ordinalsFtBalance, brc20Tokens] = await Promise.all([ - getOrdinalsFtBalance(network.type, ordinalsAddress), - getBrc20Tokens(network.type, brcCoinsList?.map((o) => o.ticker!) ?? [], fiatCurrency), - ]); + const ordinalsFtBalance = await getOrdinalsFtBalance(network.type, ordinalsAddress); + const brc20Tokens = await getBrc20Tokens( + network.type, + ordinalsFtBalance?.map((o) => o.ticker!) ?? [], + fiatCurrency, + ); const brcCoinsListMap = new Map(brcCoinsList?.map((token) => [token.ticker, token])); From 156995f3952044c723bd8a7d6e9906800a043963 Mon Sep 17 00:00:00 2001 From: GitHub Actions Bot <> Date: Thu, 25 Jan 2024 08:20:32 +0000 Subject: [PATCH 09/20] release: v0.29.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c1fcce149..e0ab1f92a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "xverse-web-extension", - "version": "0.28.1", + "version": "0.29.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "xverse-web-extension", - "version": "0.28.1", + "version": "0.29.0", "dependencies": { "@ledgerhq/hw-transport-webusb": "^6.27.13", "@phosphor-icons/react": "^2.0.10", diff --git a/package.json b/package.json index 910e00b9e..63bb8436e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "xverse-web-extension", "description": "A Bitcoin wallet for Web3", - "version": "0.28.1", + "version": "0.29.0", "private": true, "engines": { "node": "^18.18.2" From 17746b304cfa4f975d3541294be93194f3f7eb2c Mon Sep 17 00:00:00 2001 From: Tim Man Date: Thu, 25 Jan 2024 17:03:49 +0800 Subject: [PATCH 10/20] chore: fix update description on build-rc workflow --- .github/workflows/build-rc.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-rc.yml b/.github/workflows/build-rc.yml index 800a28c02..1412ae734 100644 --- a/.github/workflows/build-rc.yml +++ b/.github/workflows/build-rc.yml @@ -86,6 +86,7 @@ jobs: PR_ID: ${{ github.event.pull_request.number }} run: | # update PR description + cd scripts cat release.json | jq -r .body > body.md echo -e "\n\nRelease candidate: $(cat release.json | jq -r .html_url)" >> body.md echo -e "\nTo publish this rc as latest: Merge Commit this PR" >> body.md From be39b42885ac93cf32a760fe9aedcaeb145f56f4 Mon Sep 17 00:00:00 2001 From: fede erbes Date: Thu, 25 Jan 2024 10:59:42 +0100 Subject: [PATCH 11/20] fix: hide stx in receive modal when stx token is disabled in token management screen (#773) --- src/app/screens/home/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/screens/home/index.tsx b/src/app/screens/home/index.tsx index 4335225bc..1fd7b2d1e 100644 --- a/src/app/screens/home/index.tsx +++ b/src/app/screens/home/index.tsx @@ -378,7 +378,7 @@ function Home() {
- {stxAddress && !hideStx && ( + {stxAddress && ( Date: Thu, 25 Jan 2024 18:56:33 +0800 Subject: [PATCH 12/20] chore: use POSIX compliant shell increment --- scripts/find-tag.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/find-tag.sh b/scripts/find-tag.sh index be974b155..ade904106 100755 --- a/scripts/find-tag.sh +++ b/scripts/find-tag.sh @@ -18,8 +18,8 @@ if cat releases.json | jq '.[].tag_name' | grep $TAG; then echo $TAG was already released exit 1; elif [[ -n "$LATEST_RC" ]]; then - ((LATEST_RC++)) - NEXT_TAG="$TAG-rc.$LATEST_RC" + echo incrementing rc + NEXT_TAG="$TAG-rc.$((LATEST_RC+1))" fi else echo no releases matching $TAG yet From b47bf6da8c6052eb0899fedad224b21636d72a17 Mon Sep 17 00:00:00 2001 From: Den <36603049+dhriaznov@users.noreply.github.com> Date: Thu, 25 Jan 2024 15:30:11 +0100 Subject: [PATCH 13/20] [ENG-3513] feat: View accounts balances in the Change Account screen (#756) * [ENG-3036] feat: Rename accounts from the Extension change account screen * Add the acc renaming logic for regular and ledger accounts * Update Change Account screen icons * Add the space between the account list and control buttons * [ENG-3513] feat: View accounts balances in the Change Account screen * Make some small code tweaks * Add a `useAccountBalances` hook * Improve and optimize the logic * Improve the account balances fetching logic * Improve the account balances fetching logic * Update the account balance fetching logic * Add the balance refetching logic (when users switches the account) * Add the empty label and loader for the fetching account balance * Update the balance refetching logic * Remove the unused import * Make some account balance fetching logic tweaks * Fix the 0 index account renaming issue * Add support for fetching and displaying fungible token balances * Update the useAccountBalance hook logic * Move `calculateTotalBalance` method to utils to reuse it * Remove the redundant fetching logic from the account total balance calc * Update the `useAccountBalance` hook fetching logic * Add setAccountBalance method in the useAccountBalance hook * Add the brc coin list fetching logic to the useAccountBalance hook * Add a todo comment * Show the Manage token list for the ledger account when there is no stx address --- src/app/components/accountRow/index.tsx | 50 ++-- .../components/accountRow/lazyAccountRow.tsx | 36 +++ src/app/hooks/queries/useAccountBalance.ts | 231 ++++++++++++++++++ src/app/hooks/queries/useBtcCoinsBalance.ts | 2 +- src/app/hooks/queries/useCoinData.ts | 10 +- src/app/hooks/useIntersectionObserver.ts | 25 ++ src/app/screens/accountList/index.tsx | 7 +- src/app/screens/home/balanceCard/index.tsx | 65 ++--- src/app/screens/home/index.tsx | 18 +- src/app/screens/manageTokens/index.tsx | 22 +- .../swapConfirmation/stxInfoBlock/index.tsx | 23 +- .../stores/wallet/actions/actionCreators.ts | 11 + src/app/stores/wallet/actions/types.ts | 12 +- src/app/stores/wallet/reducer.ts | 10 + src/app/utils/helper.ts | 66 +++++ 15 files changed, 494 insertions(+), 94 deletions(-) create mode 100644 src/app/components/accountRow/lazyAccountRow.tsx create mode 100644 src/app/hooks/queries/useAccountBalance.ts create mode 100644 src/app/hooks/useIntersectionObserver.ts diff --git a/src/app/components/accountRow/index.tsx b/src/app/components/accountRow/index.tsx index 6795ea816..927c311b7 100644 --- a/src/app/components/accountRow/index.tsx +++ b/src/app/components/accountRow/index.tsx @@ -6,14 +6,15 @@ import OptionsDialog, { OPTIONS_DIALOG_WIDTH } from '@components/optionsDialog/o import useWalletReducer from '@hooks/useWalletReducer'; import useWalletSelector from '@hooks/useWalletSelector'; import { CaretDown, DotsThreeVertical } from '@phosphor-icons/react'; -import { Account } from '@secretkeylabs/xverse-core'; +import { Account, currencySymbolMap } from '@secretkeylabs/xverse-core'; import InputFeedback from '@ui-library/inputFeedback'; -import { LoaderSize, MAX_ACC_NAME_LENGTH } from '@utils/constants'; +import { EMPTY_LABEL, LoaderSize, MAX_ACC_NAME_LENGTH } from '@utils/constants'; import { getAccountGradient } from '@utils/gradient'; import { isLedgerAccount, validateAccountName } from '@utils/helper'; import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Tooltip } from 'react-tooltip'; +import { NumericFormat } from 'react-number-format'; +import { MoonLoader } from 'react-spinners'; import 'react-tooltip/dist/react-tooltip.css'; import styled from 'styled-components'; @@ -69,13 +70,6 @@ const BarLoaderContainer = styled.div((props) => ({ backgroundColor: 'transparent', })); -export const StyledToolTip = styled(Tooltip)` - background-color: ${(props) => props.theme.colors.white_0}; - color: #12151e; - border-radius: 8px; - padding: 7px; -`; - const TransparentSpan = styled.span` background: transparent; `; @@ -113,11 +107,10 @@ const ButtonRow = styled.button` align-items: center; background-color: transparent; justify-content: flex-start; - padding-left: 24px; - padding-right: 24px; - padding-top: 11px; - padding-bottom: 11px; - font: ${(props) => props.theme.body_medium_m}; + padding: ${(props) => props.theme.space.l}; + padding-top: ${(props) => props.theme.space.s}; + padding-bottom: ${(props) => props.theme.space.s}; + font: ${(props) => props.theme.typography.body_medium_m}; color: ${(props) => props.theme.colors.white_0}; transition: background-color 0.2s ease; :hover { @@ -167,6 +160,15 @@ const InputField = styled.input((props) => ({ }, })); +const Balance = styled.div<{ isSelected?: boolean }>((props) => ({ + ...props.theme.typography.body_medium_m, + marginTop: props.theme.space.xxs, + color: props.isSelected ? props.theme.colors.white_200 : props.theme.colors.white_400, + display: 'flex', + alignItems: 'center', + columnGap: props.theme.space.xs, +})); + function AccountRow({ account, isSelected, @@ -184,7 +186,8 @@ function AccountRow({ const { t: optionsDialogTranslation } = useTranslation('translation', { keyPrefix: 'OPTIONS_DIALOG', }); - const { accountsList, ledgerAccountsList } = useWalletSelector(); + const { accountsList, ledgerAccountsList, fiatCurrency, accountBalances } = useWalletSelector(); + const totalBalance = accountBalances[account?.btcAddress ?? '']; const gradient = getAccountGradient(account?.stxAddress || account?.btcAddress!); const btcCopiedTooltipTimeoutRef = useRef(); const stxCopiedTooltipTimeoutRef = useRef(); @@ -315,6 +318,21 @@ function AccountRow({ )} + {isAccountListView && totalBalance && ( + {value}} + /> + )} + {isAccountListView && !totalBalance && ( + + {EMPTY_LABEL} + + + )} )} diff --git a/src/app/components/accountRow/lazyAccountRow.tsx b/src/app/components/accountRow/lazyAccountRow.tsx new file mode 100644 index 000000000..593ac1420 --- /dev/null +++ b/src/app/components/accountRow/lazyAccountRow.tsx @@ -0,0 +1,36 @@ +import useIntersectionObserver from '@hooks/useIntersectionObserver'; +import useWalletSelector from '@hooks/useWalletSelector'; +import { Account } from '@secretkeylabs/xverse-core'; +import { useEffect, useRef, useState } from 'react'; +import AccountRow from '.'; + +function LazyAccountRow(props: { + account: Account | null; + isSelected: boolean; + onAccountSelected: (account: Account, goBack?: boolean) => void; + isAccountListView?: boolean; + disabledAccountSelect?: boolean; + fetchBalance?: (account: Account | null) => void; +}) { + const { fetchBalance, account } = props; + const { accountBalances } = useWalletSelector(); + const totalBalance = accountBalances[account?.btcAddress ?? '']; + const [shouldFetch, setShouldFetch] = useState(false); + const ref = useRef(null); + + useIntersectionObserver(ref, () => setShouldFetch(true), {}); + + useEffect(() => { + if (fetchBalance && shouldFetch && !totalBalance) { + fetchBalance(account); + } + }, [shouldFetch, totalBalance]); + + return ( +
+ +
+ ); +} + +export default LazyAccountRow; diff --git a/src/app/hooks/queries/useAccountBalance.ts b/src/app/hooks/queries/useAccountBalance.ts new file mode 100644 index 000000000..5a1b7d8ad --- /dev/null +++ b/src/app/hooks/queries/useAccountBalance.ts @@ -0,0 +1,231 @@ +import useBtcClient from '@hooks/useBtcClient'; +import useNetworkSelector from '@hooks/useNetwork'; +import useWalletSelector from '@hooks/useWalletSelector'; +import { + API_TIMEOUT_MILLI, + Account, + BtcAddressData, + FungibleToken, + TokensResponse, + getBrc20Tokens, + getCoinsInfo, + getNetworkURL, + getOrdinalsFtBalance, +} from '@secretkeylabs/xverse-core'; +import { setAccountBalanceAction } from '@stores/wallet/actions/actionCreators'; +import { calculateTotalBalance } from '@utils/helper'; +import axios from 'axios'; +import BigNumber from 'bignumber.js'; +import { useEffect, useRef, useState } from 'react'; +import { useDispatch } from 'react-redux'; +import { brc20TokenToFungibleToken } from './useBtcCoinsBalance'; + +const useAccountBalance = () => { + const btcClient = useBtcClient(); + const stacksNetwork = useNetworkSelector(); + const { btcFiatRate, stxBtcRate, fiatCurrency, network, coinsList, brcCoinsList, hideStx } = + useWalletSelector(); + const dispatch = useDispatch(); + const queue = useRef([]); + const [isProcessingQueue, setIsProcessingQueue] = useState(false); + const [queueLength, setQueueLength] = useState(0); + + // TODO: reuse it for both useAccountBalance and useBtcCoinBalance hooks + const fetchBrcCoinsBalances = async (ordinalsAddress: string) => { + try { + const ordinalsFtBalance = await getOrdinalsFtBalance(network.type, ordinalsAddress); + const brc20Tokens = await getBrc20Tokens( + network.type, + ordinalsFtBalance?.map((o) => o.ticker!) ?? [], + fiatCurrency, + ); + + const brcCoinsListMap = new Map(brcCoinsList?.map((token) => [token.ticker, token])); + + const mergedList: FungibleToken[] = ordinalsFtBalance.map((newToken) => { + const existingToken = brcCoinsListMap.get(newToken.ticker); + + const reconstitutedFt = { + ...existingToken, + ...newToken, + // The `visible` property from `xverse-core` defaults to true. + // We override `visible` to ensure that the existing state is preserved. + ...(existingToken ? { visible: existingToken.visible } : {}), + }; + + return reconstitutedFt; + }); + + brc20Tokens?.forEach((b) => { + const existingToken = brcCoinsListMap.get(b.ticker); + const pendingToken = mergedList?.find((m) => m.ticker === b.ticker); + const tokenFiatRate = Number(b?.tokenFiatRate); + + // No duplicates + if (pendingToken) { + pendingToken.tokenFiatRate = tokenFiatRate; + return; + } + + if (existingToken) { + mergedList.push({ + ...existingToken, + tokenFiatRate, + }); + } else { + mergedList.push(brc20TokenToFungibleToken(b)); + } + }); + + return mergedList; + } catch (e: any) { + return Promise.reject(e); + } + }; + + const fetchBalances = async (account: Account | null) => { + if (!account) { + return; + } + + let btcBalance = '0'; + let stxBalance = '0'; + let ftCoinList: FungibleToken[] | null = null; + let finalBrcCoinList: FungibleToken[] | null = null; + + if (account.btcAddress) { + const btcData: BtcAddressData = await btcClient.getBalance(account.btcAddress); + btcBalance = btcData.finalBalance.toString(); + } + + if (account.ordinalsAddress) { + finalBrcCoinList = await fetchBrcCoinsBalances(account.ordinalsAddress); + } + + if (account.stxAddress) { + const apiUrl = `${getNetworkURL(stacksNetwork)}/extended/v1/address/${ + account.stxAddress + }/balances`; + + const response = await axios.get(apiUrl, { + timeout: API_TIMEOUT_MILLI, + }); + + const availableBalance = new BigNumber(response.data.stx.balance); + const lockedBalance = new BigNumber(response.data.stx.locked); + stxBalance = availableBalance.plus(lockedBalance).toString(); + + const fungibleTokenList: FungibleToken[] = []; + Object.entries(response.data.fungible_tokens).forEach(([key, value]: [string, any]) => { + const fungibleToken: FungibleToken = value; + const index = key.indexOf('::'); + fungibleToken.assetName = key.substring(index + 2); + fungibleToken.principal = key.substring(0, index); + fungibleToken.protocol = 'stacks'; + fungibleTokenList.push(fungibleToken); + }); + + const visibleCoins: FungibleToken[] | null = coinsList; + if (visibleCoins) { + visibleCoins.forEach((visibleCoin) => { + const coinToBeUpdated = fungibleTokenList.find( + (ft) => ft.principal === visibleCoin.principal, + ); + if (coinToBeUpdated) coinToBeUpdated.visible = visibleCoin.visible; + else if (visibleCoin.visible) { + visibleCoin.balance = '0'; + fungibleTokenList.push(visibleCoin); + } + }); + } else { + fungibleTokenList.forEach((ft) => { + ft.visible = true; + }); + } + + const contractids: string[] = fungibleTokenList.map((ft) => ft.principal); + const coinsResponse = await getCoinsInfo(network.type, contractids, fiatCurrency); + + if (coinsResponse) { + coinsResponse.forEach((coin) => { + if (!coin.name) { + const coinName = coin.contract.split('.')[1]; + coin.name = coinName; + } + }); + + // update attributes of fungible token list + fungibleTokenList.forEach((ft) => { + coinsResponse.forEach((coin) => { + if (ft.principal === coin.contract) { + ft.ticker = coin.ticker; + ft.decimals = coin.decimals; + ft.supported = coin.supported; + ft.image = coin.image; + ft.name = coin.name; + ft.tokenFiatRate = coin.tokenFiatRate; + coin.visible = ft.visible; + } + }); + }); + + ftCoinList = fungibleTokenList; + } + } + + const totalBalance = calculateTotalBalance({ + stxBalance, + btcBalance, + ftCoinList, + brcCoinsList: finalBrcCoinList, + stxBtcRate, + btcFiatRate, + hideStx, + }); + dispatch(setAccountBalanceAction(account.btcAddress, totalBalance)); + return totalBalance; + }; + + const processQueue = async () => { + if (queue.current.length === 0) { + setIsProcessingQueue(false); + return; + } + + const account = queue.current.shift() || null; + await fetchBalances(account); + setQueueLength(queue.current.length); + + setTimeout(processQueue, 1000); + }; + + useEffect(() => { + if (!isProcessingQueue && queue.current.length > 0) { + setIsProcessingQueue(true); + processQueue(); + } + }, [queueLength]); + + const enqueueFetchBalances = (account: Account | null) => { + if (!account) { + return; + } + + queue.current.push(account); + setQueueLength(queue.current.length); + if (!isProcessingQueue) { + setIsProcessingQueue(true); + processQueue(); + } + }; + + const setAccountBalance = (account: Account | null, balance: string) => { + if (account) { + dispatch(setAccountBalanceAction(account.btcAddress, balance)); + } + }; + + return { enqueueFetchBalances, setAccountBalance }; +}; + +export default useAccountBalance; diff --git a/src/app/hooks/queries/useBtcCoinsBalance.ts b/src/app/hooks/queries/useBtcCoinsBalance.ts index e78df750b..c6fbfadf3 100644 --- a/src/app/hooks/queries/useBtcCoinsBalance.ts +++ b/src/app/hooks/queries/useBtcCoinsBalance.ts @@ -9,7 +9,7 @@ import { setBrcCoinsDataAction } from '@stores/wallet/actions/actionCreators'; import { useQuery } from '@tanstack/react-query'; import { useDispatch } from 'react-redux'; -const brc20TokenToFungibleToken = (coin: Brc20Token): FungibleToken => ({ +export const brc20TokenToFungibleToken = (coin: Brc20Token): FungibleToken => ({ name: coin.name, principal: coin.ticker ?? coin.name, balance: '0', diff --git a/src/app/hooks/queries/useCoinData.ts b/src/app/hooks/queries/useCoinData.ts index 057aa7bc5..9af41cfef 100644 --- a/src/app/hooks/queries/useCoinData.ts +++ b/src/app/hooks/queries/useCoinData.ts @@ -8,7 +8,7 @@ import { } from '@secretkeylabs/xverse-core'; import { setCoinDataAction } from '@stores/wallet/actions/actionCreators'; import { useQuery } from '@tanstack/react-query'; -import { handleRetries, InvalidParamsError } from '@utils/query'; +import { InvalidParamsError, handleRetries } from '@utils/query'; import { useDispatch } from 'react-redux'; export const useCoinsData = () => { @@ -22,7 +22,7 @@ export const useCoinsData = () => { throw new InvalidParamsError('No stx address'); } - const fungibleTokenList: Array = await getFtData( + const fungibleTokenList: FungibleToken[] = await getFtData( stxAddress, currentNetworkInstance, ); @@ -44,11 +44,9 @@ export const useCoinsData = () => { }); } - const contractids: string[] = []; // getting contract ids of all fts - fungibleTokenList.forEach((ft) => { - contractids.push(ft.principal); - }); + const contractids: string[] = fungibleTokenList.map((ft) => ft.principal); + let coinsResponse = await getCoinsInfo(network.type, contractids, fiatCurrency); if (!coinsResponse) { coinsResponse = await getCoinMetaData(contractids, currentNetworkInstance); diff --git a/src/app/hooks/useIntersectionObserver.ts b/src/app/hooks/useIntersectionObserver.ts new file mode 100644 index 000000000..08beef4e0 --- /dev/null +++ b/src/app/hooks/useIntersectionObserver.ts @@ -0,0 +1,25 @@ +import { useEffect } from 'react'; + +const useIntersectionObserver = (ref, callback, options) => { + useEffect(() => { + const observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + callback(); + } + }); + }, options); + + if (ref.current) { + observer.observe(ref.current); + } + + return () => { + if (ref.current) { + observer.unobserve(ref.current); + } + }; + }, [ref, callback, options]); +}; + +export default useIntersectionObserver; diff --git a/src/app/screens/accountList/index.tsx b/src/app/screens/accountList/index.tsx index a1d4e1d83..d06dbe076 100644 --- a/src/app/screens/accountList/index.tsx +++ b/src/app/screens/accountList/index.tsx @@ -1,9 +1,10 @@ import ConnectLedger from '@assets/img/dashboard/connect_ledger.svg'; import { filterLedgerAccounts } from '@common/utils/ledger'; -import AccountRow from '@components/accountRow'; +import LazyAccountRow from '@components/accountRow/lazyAccountRow'; import ActionButton from '@components/button'; import Separator from '@components/separator'; import TopRow from '@components/topRow'; +import useAccountBalance from '@hooks/queries/useAccountBalance'; import { broadcastResetUserFlow } from '@hooks/useResetUserFlow'; import useWalletReducer from '@hooks/useWalletReducer'; import useWalletSelector from '@hooks/useWalletSelector'; @@ -58,6 +59,7 @@ function AccountList(): JSX.Element { const navigate = useNavigate(); const { network, accountsList, selectedAccount, ledgerAccountsList } = useWalletSelector(); const { createAccount, switchAccount } = useWalletReducer(); + const { enqueueFetchBalances } = useAccountBalance(); const displayedAccountsList = useMemo(() => { const networkLedgerAccounts = filterLedgerAccounts(ledgerAccountsList, network.type); @@ -98,10 +100,11 @@ function AccountList(): JSX.Element { {t('TITLE')} {displayedAccountsList.map((account) => (
- diff --git a/src/app/screens/home/balanceCard/index.tsx b/src/app/screens/home/balanceCard/index.tsx index 087e65b6f..daa7aff0f 100644 --- a/src/app/screens/home/balanceCard/index.tsx +++ b/src/app/screens/home/balanceCard/index.tsx @@ -1,8 +1,11 @@ import BarLoader from '@components/barLoader'; +import useAccountBalance from '@hooks/queries/useAccountBalance'; import useWalletSelector from '@hooks/useWalletSelector'; import { currencySymbolMap, microstacksToStx, satsToBtc } from '@secretkeylabs/xverse-core'; import { LoaderSize } from '@utils/constants'; +import { calculateTotalBalance } from '@utils/helper'; import BigNumber from 'bignumber.js'; +import { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { NumericFormat } from 'react-number-format'; import { MoonLoader } from 'react-spinners'; @@ -74,57 +77,35 @@ function BalanceCard(props: BalanceCardProps) { stxBalance, btcBalance, btcAddress, - stxAddress, hideStx, coinsList, + selectedAccount, + accountBalances, brcCoinsList, } = useWalletSelector(); + const { setAccountBalance } = useAccountBalance(); const { isLoading, isRefetching } = props; + const oldTotalBalance = accountBalances[btcAddress]; - function calculateTotalBalance() { - let totalBalance = new BigNumber(0); - if (stxAddress && !hideStx) { - const stxFiatEquiv = microstacksToStx(new BigNumber(stxBalance)) - .multipliedBy(new BigNumber(stxBtcRate)) - .multipliedBy(new BigNumber(btcFiatRate)); - totalBalance = totalBalance.plus(stxFiatEquiv); - } - if (btcAddress) { - const btcFiatEquiv = satsToBtc(new BigNumber(btcBalance)).multipliedBy( - new BigNumber(btcFiatRate), - ); - totalBalance = totalBalance.plus(btcFiatEquiv); - } - - if (coinsList) { - totalBalance = coinsList.reduce((acc, coin) => { - if (coin.visible && coin.tokenFiatRate && coin.decimals) { - const tokenUnits = new BigNumber(10).exponentiatedBy(new BigNumber(coin.decimals)); - const coinFiatValue = new BigNumber(coin.balance) - .dividedBy(tokenUnits) - .multipliedBy(new BigNumber(coin.tokenFiatRate)); - return acc.plus(coinFiatValue); - } + const balance = calculateTotalBalance({ + stxBalance, + btcBalance, + ftCoinList: coinsList, + brcCoinsList, + btcFiatRate, + stxBtcRate, + hideStx, + }); - return acc; - }, totalBalance); + useEffect(() => { + if (!balance || !selectedAccount || isLoading || isRefetching) { + return; } - if (brcCoinsList) { - totalBalance = brcCoinsList.reduce((acc, coin) => { - if (coin.visible && coin.tokenFiatRate) { - const coinFiatValue = new BigNumber(coin.balance).multipliedBy( - new BigNumber(coin.tokenFiatRate), - ); - return acc.plus(coinFiatValue); - } - - return acc; - }, totalBalance); + if (oldTotalBalance !== balance) { + setAccountBalance(selectedAccount, balance); } - - return totalBalance.toNumber().toFixed(2); - } + }, [balance, oldTotalBalance, selectedAccount, isLoading, isRefetching]); return ( <> @@ -141,7 +122,7 @@ function BalanceCard(props: BalanceCardProps) { ) : ( - {!!stxAddress && ( - - - - )} + + + state.walletState); - const [selectedProtocol, setSelectedProtocol] = useState(Protocols.SIP_10); + const { coinsList, coins, brcCoinsList, selectedAccount } = useSelector( + (state: StoreState) => state.walletState, + ); + const [selectedProtocol, setSelectedProtocol] = useState( + selectedAccount?.stxAddress ? Protocols.SIP_10 : Protocols.BRC_20, + ); const navigate = useNavigate(); const dispatch = useDispatch(); @@ -163,12 +167,14 @@ function ManageTokens() {
{t('ADD_COINS')}
{t('DESCRIPTION')} - + {selectedAccount?.stxAddress && ( + + )}
- - } - onPress={onCreateAccount} - text={t('NEW_ACCOUNT')} - transparent - /> - } - onPress={onImportLedgerAccount} - text={t('LEDGER_ACCOUNT')} - transparent - /> - + {!hideListActions ? ( + + } + onPress={onCreateAccount} + text={t('NEW_ACCOUNT')} + transparent + /> + } + onPress={onImportLedgerAccount} + text={t('LEDGER_ACCOUNT')} + transparent + /> + + ) : null} ); } diff --git a/src/app/screens/btcSelectAddressScreen/accountView.tsx b/src/app/screens/btcSelectAddressScreen/accountView.tsx deleted file mode 100644 index d4ca8a77d..000000000 --- a/src/app/screens/btcSelectAddressScreen/accountView.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import OrdinalsIcon from '@assets/img/nftDashboard/white_ordinals_icon.svg'; -import { Account } from '@secretkeylabs/xverse-core'; -import { getAccountGradient } from '@utils/gradient'; -import { getTruncatedAddress } from '@utils/helper'; -import { useTranslation } from 'react-i18next'; -import styled from 'styled-components'; - -interface GradientCircleProps { - firstGradient: string; - secondGradient: string; - thirdGradient: string; -} -const GradientCircle = styled.div((props) => ({ - height: 40, - width: 40, - borderRadius: 25, - marginRight: 9, - background: `linear-gradient(to bottom,${props.firstGradient}, ${props.secondGradient},${props.thirdGradient} )`, -})); - -const Container = styled.div({ - display: 'flex', - flexDirection: 'row', - alignItems: 'center', -}); - -const ColumnContainer = styled.div({ - display: 'flex', - flexDirection: 'column', -}); - -const AddressContainer = styled.div({ - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', -}); - -const RowContainer = styled.div({ - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', -}); - -const CurrentSelectedAccountText = styled.h1((props) => ({ - ...props.theme.body_bold_m, - color: props.theme.colors.white_0, - textAlign: 'start', -})); - -const AddressText = styled.h1((props) => ({ - ...props.theme.body_m, - marginTop: props.theme.spacing(1), - color: props.theme.colors.white_400, -})); - -const BitcoinDot = styled.div((props) => ({ - borderRadius: 20, - background: props.theme.colors.feedback.caution, - width: 10, - marginRight: 4, - marginLeft: 4, - height: 10, -})); - -const OrdinalImage = styled.img({ - width: 12, - height: 12, - marginRight: 4, -}); - -interface Props { - account: Account; - isBitcoinTx: boolean; -} -function AccountView({ account, isBitcoinTx }: Props) { - const gradient = getAccountGradient(account?.stxAddress || account?.btcAddress!); - const { t } = useTranslation('translation', { keyPrefix: 'DASHBOARD_SCREEN' }); - - function getName() { - return ( - account?.accountName ?? - account?.bnsName ?? - `${t('ACCOUNT_NAME')} ${`${(account?.id ?? 0) + 1}`}` - ); - } - - return ( - - - - - {getName()} - - - - {`${getTruncatedAddress(account?.ordinalsAddress)} / `} - - - - {`${getTruncatedAddress(account?.btcAddress)}`} - - - - - ); -} - -export default AccountView; diff --git a/src/app/screens/btcSelectAddressScreen/index.tsx b/src/app/screens/btcSelectAddressScreen/index.tsx deleted file mode 100644 index 5eb6b86a3..000000000 --- a/src/app/screens/btcSelectAddressScreen/index.tsx +++ /dev/null @@ -1,306 +0,0 @@ -import OrdinalsIcon from '@assets/img/nftDashboard/white_ordinals_icon.svg'; -import XverseLogo from '@assets/img/settings/logo.svg'; -import DropDownIcon from '@assets/img/transactions/dropDownIcon.svg'; -import DappPlaceholderIcon from '@assets/img/webInteractions/authPlaceholder.svg'; -import { filterLedgerAccounts } from '@common/utils/ledger'; -import AccountRow from '@components/accountRow'; -import ActionButton from '@components/button'; -import Separator from '@components/separator'; -import useBtcAddressRequest from '@hooks/useBtcAddressRequest'; -import useWalletReducer from '@hooks/useWalletReducer'; -import useWalletSelector from '@hooks/useWalletSelector'; -import { animated, useSpring } from '@react-spring/web'; -import { Account } from '@secretkeylabs/xverse-core'; -import { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useNavigate } from 'react-router-dom'; -import { AddressPurpose } from 'sats-connect'; -import styled from 'styled-components'; -import AccountView from './accountView'; - -const TitleContainer = styled.div({ - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - overflow: 'hidden', - marginLeft: 30, - marginRight: 30, -}); - -const DropDownContainer = styled.div({ - display: 'flex', - flexDirection: 'row', - flex: 1, - height: '100%', - alignItems: 'center', - justifyContent: 'flex-end', -}); - -const Container = styled.div({ - display: 'flex', - alignItems: 'center', -}); - -const LogoContainer = styled.div((props) => ({ - padding: props.theme.spacing(11), - marginBottom: props.theme.spacing(16), - borderBottom: `1px solid ${props.theme.colors.elevation3}`, -})); - -const AddressContainer = styled.div((props) => ({ - background: props.theme.colors.elevation2, - borderRadius: 40, - height: 24, - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'center', - padding: '3px 10px 3px 6px', - marginTop: props.theme.spacing(4), - marginRight: props.theme.spacing(2), -})); - -const AccountListContainer = styled(animated.div)((props) => ({ - paddingBottom: 20, - width: '100%', - borderRadius: 12, - height: 214, - marginTop: props.theme.spacing(9.5), - boxShadow: '0px 8px 104px rgba(0, 0, 0, 0.5)', - background: props.theme.colors.elevation2, - '&::-webkit-scrollbar': { - display: 'none', - }, - overflowY: 'auto', -})); - -const TopImage = styled.img({ - aspectRatio: 1, - height: 88, - borderWidth: 10, - borderColor: 'white', -}); - -const FunctionTitle = styled.h1((props) => ({ - ...props.theme.body_bold_l, - color: props.theme.colors.white_0, - marginTop: 16, -})); - -const AccountContainer = styled.button((props) => ({ - background: props.theme.colors.elevation1, - border: `1px solid ${props.theme.colors.elevation3}`, - borderRadius: 8, - width: '100%', - padding: '12px 16px', - display: 'flex', - flexDirection: 'row', - marginTop: props.theme.spacing(4), - ':hover': { - background: props.theme.colors.elevation2, - }, -})); - -const AccountText = styled.h1((props) => ({ - ...props.theme.body_medium_m, - color: props.theme.colors.white_400, - marginTop: 24, -})); - -const DappTitle = styled.h2((props) => ({ - ...props.theme.body_m, - color: props.theme.colors.white_200, - marginTop: 12, - textAlign: 'center', -})); - -const AddressTextTitle = styled.h1((props) => ({ - ...props.theme.body_medium_l, - color: props.theme.colors.white_0, - fontSize: 10, - textAlign: 'center', -})); - -const OuterContainer = styled(animated.div)({ - display: 'flex', - flexDirection: 'column', - marginLeft: 16, - marginRight: 16, -}); - -const ButtonsContainer = styled.div((props) => ({ - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'flex-end', - marginBottom: props.theme.spacing(20), - marginTop: 82, -})); - -const BitcoinDot = styled.div((props) => ({ - borderRadius: 20, - background: props.theme.colors.feedback.caution, - width: 6, - marginRight: props.theme.spacing(3), - height: 6, -})); - -const AccountListRow = styled.div((props) => ({ - paddingTop: props.theme.spacing(8), - paddingLeft: 16, - paddingRight: 16, - ':hover': { - background: props.theme.colors.elevation3, - }, -})); - -const TransparentButtonContainer = styled.div((props) => ({ - marginLeft: props.theme.spacing(2), - marginRight: props.theme.spacing(2), - width: '100%', -})); - -const OrdinalImage = styled.img({ - width: 12, - height: 12, - marginRight: 8, -}); - -function BtcSelectAddressScreen() { - const [loading, setLoading] = useState(false); - const [showAccountList, setShowAccountList] = useState(false); - const navigate = useNavigate(); - const { t } = useTranslation('translation', { keyPrefix: 'SELECT_BTC_ADDRESS_SCREEN' }); - const { selectedAccount, accountsList, ledgerAccountsList, network } = useWalletSelector(); - const { switchAccount } = useWalletReducer(); - const { payload, approveBtcAddressRequest, cancelAddressRequest } = useBtcAddressRequest(); - const springProps = useSpring({ - transform: showAccountList ? 'translateY(0%)' : 'translateY(100%)', - opacity: showAccountList ? 1 : 0, - config: { - tension: 160, - friction: 25, - }, - }); - const styles = useSpring({ - from: { - opacity: 0, - y: 24, - }, - to: { - y: 0, - opacity: 1, - }, - }); - - const confirmCallback = async () => { - setLoading(true); - approveBtcAddressRequest(); - window.close(); - }; - - const cancelCallback = () => { - cancelAddressRequest(); - window.close(); - }; - - const onChangeAccount = () => { - setShowAccountList(true); - }; - - const isAccountSelected = (account: Account) => - account.id === selectedAccount?.id && account.accountType === selectedAccount?.accountType; - - const handleAccountSelect = async (account: Account) => { - await switchAccount(account); - setShowAccountList(false); - }; - - const switchAccountBasedOnRequest = () => { - if (payload.network.type !== network.type) { - navigate('/tx-status', { - state: { - txid: '', - currency: 'BTC', - errorTitle: t('NETWORK_MISMATCH_ERROR_TITLE'), - error: t('NETWORK_MISMATCH_ERROR_DESCRIPTION'), - browserTx: true, - }, - }); - } - }; - - useEffect(() => { - switchAccountBasedOnRequest(); - }, []); - - const networkLedgerAccounts = filterLedgerAccounts(ledgerAccountsList, network.type); - - return ( - <> - - xverse logo - - - - - {t('TITLE')} - - {payload.purposes.map((purpose) => - purpose === AddressPurpose.Payment ? ( - - - {t('BITCOIN_ADDRESS')} - - ) : ( - - - {t('ORDINAL_ADDRESS')} - - ), - )} - - {payload.message} - - {showAccountList ? ( - - {[...networkLedgerAccounts, ...accountsList].map((account) => ( - - - - - ))} - - ) : ( - <> - {t('ACCOUNT')} - - - - Drop Down - - - - - - - - - - )} - - - ); -} - -export default BtcSelectAddressScreen; diff --git a/src/app/screens/connect/addressPurposeBox.tsx b/src/app/screens/connect/addressPurposeBox.tsx new file mode 100644 index 000000000..783d46021 --- /dev/null +++ b/src/app/screens/connect/addressPurposeBox.tsx @@ -0,0 +1,80 @@ +import { getTruncatedAddress } from '@utils/helper'; +import { AddressPurpose } from 'sats-connect'; +import styled from 'styled-components'; + +const AddressBox = styled.div((props) => ({ + padding: `${props.theme.spacing(10)}px ${props.theme.space.m}`, + display: 'flex', + maxHeight: 60, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + backgroundColor: props.theme.colors.elevation6_800, + marginBottom: 1, + ':first-of-type': { + borderTopLeftRadius: props.theme.radius(2), + borderTopRightRadius: props.theme.radius(2), + }, + ':last-of-type': { + borderBottomLeftRadius: props.theme.radius(2), + borderBottomRightRadius: props.theme.radius(2), + }, +})); + +const AddressContainer = styled.div({ + display: 'flex', + alignItems: 'center', +}); + +const AddressImage = styled.img((props) => ({ + width: 20, + height: 20, + marginRight: props.theme.space.xs, +})); + +const AddressTextTitle = styled.h2((props) => ({ + ...props.theme.typography.body_medium_m, + color: props.theme.colors.white_200, + textAlign: 'center', +})); + +const TruncatedAddress = styled.p((props) => ({ + ...props.theme.typography.body_medium_m, + color: props.theme.colors.white_0, + textAlign: 'right', +})); + +const BnsName = styled.p((props) => ({ + ...props.theme.typography.body_medium_m, + color: props.theme.colors.white_0, + textAlign: 'right', +})); + +function AddressPurposeBox({ + purpose, + icon, + title, + address, + bnsName, +}: { + purpose: AddressPurpose; + icon: string; + title: string; + address: string; + bnsName?: string; +}) { + return ( + + + + {title} + +
+ {bnsName ? {bnsName} : null} + {getTruncatedAddress(address)} +
+
+ ); +} + +export default AddressPurposeBox; diff --git a/src/app/screens/authenticationRequest/index.tsx b/src/app/screens/connect/authenticationRequest/index.tsx similarity index 65% rename from src/app/screens/authenticationRequest/index.tsx rename to src/app/screens/connect/authenticationRequest/index.tsx index 0727a19f3..011ecc095 100644 --- a/src/app/screens/authenticationRequest/index.tsx +++ b/src/app/screens/connect/authenticationRequest/index.tsx @@ -1,41 +1,42 @@ +import BitcoinIcon from '@assets/img/dashboard/bitcoin_icon.svg'; +import stxIcon from '@assets/img/dashboard/stx_icon.svg'; import ledgerConnectDefaultIcon from '@assets/img/ledger/ledger_connect_default.svg'; import ledgerConnectStxIcon from '@assets/img/ledger/ledger_import_connect_stx.svg'; -import DappPlaceholderIcon from '@assets/img/webInteractions/authPlaceholder.svg'; import { MESSAGE_SOURCE } from '@common/types/message-types'; import { delay } from '@common/utils/ledger'; -import AccountHeaderComponent from '@components/accountHeader'; import BottomModal from '@components/bottomModal'; import ActionButton from '@components/button'; -import ConfirmScreen from '@components/confirmScreen'; import InfoContainer from '@components/infoContainer'; import LedgerConnectionView from '@components/ledger/connectLedgerView'; import useSeedVault from '@hooks/useSeedVault'; import useWalletSelector from '@hooks/useWalletSelector'; import Transport from '@ledgerhq/hw-transport-webusb'; +import SelectAccount from '@screens/connect/selectAccount'; import { AuthRequest, createAuthResponse, handleLedgerStxJWTAuth, } from '@secretkeylabs/xverse-core'; -import { AddressVersion, publicKeyToAddress, StacksMessageType } from '@stacks/transactions'; +import { AddressVersion, StacksMessageType, publicKeyToAddress } from '@stacks/transactions'; +import { StickyHorizontalSplitButtonContainer } from '@ui-library/common.styled'; import { isHardwareAccount } from '@utils/helper'; import { decodeToken } from 'jsontokens'; -import { useState } from 'react'; +import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useLocation } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; +import { AddressPurpose } from 'sats-connect'; import styled from 'styled-components'; import validUrl from 'valid-url'; +import AddressPurposeBox from '../addressPurposeBox'; +import PermissionsList from '../permissionsList'; -const MainContainer = styled.div({ +const MainContainer = styled.div((props) => ({ display: 'flex', - flex: 1, flexDirection: 'column', - alignItems: 'center', - justifyContent: 'center', - width: '100%', - height: '100%', - overflow: 'hidden', -}); + paddingLeft: props.theme.space.m, + paddingRight: props.theme.space.m, + ...props.theme.scrollbar, +})); const SuccessActionsContainer = styled.div((props) => ({ width: '100%', @@ -48,23 +49,30 @@ const SuccessActionsContainer = styled.div((props) => ({ marginTop: props.theme.spacing(20), })); -const TopImage = styled.img({ - aspectRatio: 1, - height: 88, - borderWidth: 10, - borderColor: 'white', -}); +const TopImage = styled.img((props) => ({ + maxHeight: 48, + maxWidth: 48, + marginTop: props.theme.space.xxl, + alignSelf: 'center', +})); -const FunctionTitle = styled.h1((props) => ({ - ...props.theme.headline_s, +const ImagePlaceholder = styled.div((props) => ({ + marginTop: props.theme.space.xxl, +})); + +const Title = styled.h1((props) => ({ + ...props.theme.typography.headline_xs, color: props.theme.colors.white_0, - marginTop: props.theme.spacing(8), + textAlign: 'center', + marginTop: 12, })); -const DappTitle = styled.h2((props) => ({ - ...props.theme.body_l, +const DappName = styled.h2((props) => ({ + ...props.theme.typography.body_medium_m, color: props.theme.colors.white_400, marginTop: props.theme.spacing(2), + marginBottom: props.theme.spacing(12), + textAlign: 'center', })); const InfoContainerWrapper = styled.div((props) => ({ @@ -72,6 +80,16 @@ const InfoContainerWrapper = styled.div((props) => ({ marginBottom: 0, })); +const AddressesContainer = styled.div((props) => ({ + marginTop: props.theme.space.s, + width: '100%', +})); + +const PermissionsContainer = styled.div((props) => ({ + width: '100%', + paddingBottom: props.theme.space.xxl, +})); + function AuthenticationRequest() { const [loading, setLoading] = useState(false); const [isModalVisible, setIsModalVisible] = useState(false); @@ -82,12 +100,12 @@ function AuthenticationRequest() { const [isTxApproved, setIsTxApproved] = useState(false); const [isTxRejected, setIsTxRejected] = useState(false); const { t } = useTranslation('translation', { keyPrefix: 'AUTH_REQUEST_SCREEN' }); - + const navigate = useNavigate(); const { search } = useLocation(); const params = new URLSearchParams(search); const authRequestToken = params.get('authRequest') ?? ''; const authRequest = decodeToken(authRequestToken) as unknown as AuthRequest; - const { selectedAccount } = useWalletSelector(); + const { selectedAccount, btcAddress, stxAddress } = useWalletSelector(); const { getSeed } = useSeedVault(); const isDisabled = !selectedAccount?.stxAddress; @@ -104,6 +122,9 @@ function AuthenticationRequest() { seedPhrase, selectedAccount?.id ?? 0, authRequest, + { + btcAddress: selectedAccount?.btcAddress, + }, ); chrome.tabs.sendMessage(+(params.get('tabId') ?? '0'), { source: MESSAGE_SOURCE, @@ -133,10 +154,15 @@ function AuthenticationRequest() { window.close(); }; - const getDappLogo = () => - validUrl.isWebUri(authRequest?.payload?.appDetails?.icon) - ? authRequest?.payload?.appDetails?.icon - : DappPlaceholderIcon; + const getDappLogo = useCallback( + () => + validUrl.isWebUri(authRequest?.payload?.appDetails?.icon) ? ( + + ) : ( + + ), + [authRequest], + ); const handleConnectAndConfirm = async () => { if (!selectedAccount) { @@ -191,7 +217,6 @@ function AuthenticationRequest() { }); window.close(); } catch (e) { - console.error(e); setIsTxRejected(true); setIsButtonDisabled(false); } finally { @@ -205,36 +230,55 @@ function AuthenticationRequest() { setCurrentStepIndex(0); }; - return ( - - - - - {t('TITLE')} - {`${t('REQUEST_TOOLTIP')} ${authRequest.payload.appDetails?.name}`} - {isDisabled && ( - - { - await chrome.tabs.create({ - url: chrome.runtime.getURL(`options.html#/add-stx-address-ledger`), - }); + const handleSwitchAccount = () => { + navigate('/account-list?hideListActions=true'); + }; - window.close(); - }} - /> - - )} - + const handleAddStxLedgerAccount = async () => { + await chrome.tabs.create({ + url: chrome.runtime.getURL(`options.html#/add-stx-address-ledger`), + }); + + window.close(); + }; + + return ( + + {getDappLogo()} + {t('TITLE')} + {`${t('REQUEST_TOOLTIP')} ${authRequest.payload.appDetails?.name}`} + + + + + + + + + + + + + {isDisabled && ( + + + + )} setIsModalVisible(false)}> {currentStepIndex === 0 && ( - + ); } diff --git a/src/app/screens/connect/btcSelectAddressScreen/helper.ts b/src/app/screens/connect/btcSelectAddressScreen/helper.ts new file mode 100644 index 000000000..1bf47cc69 --- /dev/null +++ b/src/app/screens/connect/btcSelectAddressScreen/helper.ts @@ -0,0 +1,48 @@ +export interface WebManifest { + icons?: { src: string; sizes?: string }[]; +} + +export async function getAppIconFromWebManifest(url: string): Promise { + try { + // Validate URL format + if (!/^https?:\/\/.*/.test(url)) { + throw new Error('Invalid URL format'); + } + + // Fetch the web manifest + const response = await fetch(`${url}/manifest.json`); + + // Check for successful response + if (!response.ok) { + throw new Error(`Failed to fetch web manifest. Status: ${response.status}`); + } + + const manifest: WebManifest = await response.json(); + // Ensure the manifest contains the 'icons' property + if (!manifest.icons || !Array.isArray(manifest.icons)) { + throw new Error('Web manifest is missing the icons property'); + } + + // Extract the app icons' URLs + const icons = manifest.icons.filter((icon) => icon.sizes === '48x48'); + + return `${url}${icons[0].src}`; + } catch (error: any) { + if (error.message.includes('Failed to fetch web manifest')) { + const response = await fetch(`${url}/manifest.webmanifest`); + if (!response.ok) { + return ''; + } + const manifest: WebManifest = await response.json(); + // Ensure the manifest contains the 'icons' property + if (!manifest.icons || !Array.isArray(manifest.icons)) { + throw new Error('Web manifest is missing the icons property'); + } + + // Extract the app icons' URLs + const icons = manifest.icons.filter((icon) => icon.sizes === '48x48'); + return `${url}/${icons[0].src}`; + } + return ''; + } +} diff --git a/src/app/screens/connect/btcSelectAddressScreen/index.tsx b/src/app/screens/connect/btcSelectAddressScreen/index.tsx new file mode 100644 index 000000000..c1573cb83 --- /dev/null +++ b/src/app/screens/connect/btcSelectAddressScreen/index.tsx @@ -0,0 +1,212 @@ +import BitcoinIcon from '@assets/img/dashboard/bitcoin_icon.svg'; +import stxIcon from '@assets/img/dashboard/stx_icon.svg'; +import OrdinalsIcon from '@assets/img/nftDashboard/white_ordinals_icon.svg'; +import ActionButton from '@components/button'; +import useBtcAddressRequest from '@hooks/useBtcAddressRequest'; +import useWalletSelector from '@hooks/useWalletSelector'; +import { animated, useSpring } from '@react-spring/web'; +import SelectAccount from '@screens/connect/selectAccount'; +import { StickyHorizontalSplitButtonContainer } from '@ui-library/common.styled'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; +import { AddressPurpose } from 'sats-connect'; +import styled from 'styled-components'; +import AddressPurposeBox from '../addressPurposeBox'; +import PermissionsList from '../permissionsList'; +import { getAppIconFromWebManifest } from './helper'; + +const OuterContainer = styled(animated.div)((props) => ({ + display: 'flex', + flexDirection: 'column', + paddingLeft: props.theme.space.m, + paddingRight: props.theme.space.m, + ...props.theme.scrollbar, +})); + +const HeadingContainer = styled.div({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + marginTop: 48, +}); + +const AddressBoxContainer = styled.div((props) => ({ + display: 'flex', + flexDirection: 'column', + width: '100%', + marginTop: props.theme.spacing(12), +})); + +const TopImage = styled.img((props) => ({ + maxHeight: 48, + borderRadius: props.theme.radius(2), + maxWidth: 48, + marginBottom: props.theme.space.m, +})); + +const Title = styled.h1((props) => ({ + ...props.theme.typography.headline_xs, + color: props.theme.colors.white_0, +})); + +const DapURL = styled.h2((props) => ({ + ...props.theme.typography.body_medium_m, + color: props.theme.colors.white_400, + marginTop: props.theme.spacing(2), + textAlign: 'center', +})); + +const RequestMessage = styled.p((props) => ({ + ...props.theme.typography.body_medium_m, + color: props.theme.colors.white_200, + textAlign: 'left', + wordWrap: 'break-word', + marginTop: props.theme.spacing(12), + marginBottom: props.theme.spacing(12), +})); + +const PermissionsContainer = styled.div((props) => ({ + paddingBottom: props.theme.space.xxl, +})); + +function BtcSelectAddressScreen() { + const [loading, setLoading] = useState(false); + const navigate = useNavigate(); + const { t } = useTranslation('translation', { keyPrefix: 'SELECT_BTC_ADDRESS_SCREEN' }); + const { network, btcAddress, ordinalsAddress, stxAddress, selectedAccount } = useWalletSelector(); + const [appIcon, setAppIcon] = useState(''); + const { payload, origin, approveBtcAddressRequest, cancelAddressRequest } = + useBtcAddressRequest(); + const appUrl = useMemo(() => origin.replace(/(^\w+:|^)\/\//, ''), [origin]); + + const styles = useSpring({ + from: { + opacity: 0, + y: 24, + }, + to: { + y: 0, + opacity: 1, + }, + }); + + const confirmCallback = async () => { + setLoading(true); + approveBtcAddressRequest(); + window.close(); + }; + + const cancelCallback = () => { + cancelAddressRequest(); + window.close(); + }; + + useEffect(() => { + // Handle address requests to a network that's not currently active + if (payload.network.type !== network.type) { + navigate('/tx-status', { + state: { + txid: '', + currency: 'BTC', + errorTitle: t('NETWORK_MISMATCH_ERROR_TITLE'), + error: t('NETWORK_MISMATCH_ERROR_DESCRIPTION'), + browserTx: true, + }, + }); + } + // Handle address requests with an unsupported purpose + payload.purposes.forEach((purpose) => { + if ( + purpose !== AddressPurpose.Ordinals && + purpose !== AddressPurpose.Payment && + purpose !== AddressPurpose.Stacks + ) { + navigate('/tx-status', { + state: { + txid: '', + currency: 'BTC', + errorTitle: t('INVALID_PURPOSE_ERROR_TITLE'), + error: t('INVALID_PURPOSE_ERROR_DESCRIPTION'), + browserTx: true, + }, + }); + } + }); + }, []); + + useEffect(() => { + (async () => { + if (origin !== '') { + getAppIconFromWebManifest(origin).then((appIcons) => { + setAppIcon(appIcons); + }); + } + })(); + + return () => { + setAppIcon(''); + }; + }, [origin]); + + const AddressPurposeRow = useCallback((purpose: AddressPurpose) => { + if (purpose === AddressPurpose.Payment) { + return ( + + ); + } + if (purpose === AddressPurpose.Ordinals) { + return ( + + ); + } + if (purpose === AddressPurpose.Stacks) { + return ( + + ); + } + }, []); + + const handleSwitchAccount = () => { + navigate('/account-list?hideListActions=true'); + }; + + return ( + + + {appIcon !== '' ? : null} + {t('TITLE')} + {appUrl} + + {payload.message ? {payload.message.substring(0, 80)} : null} + + {payload.purposes.map(AddressPurposeRow)} + + + + + + + + + ); +} + +export default BtcSelectAddressScreen; diff --git a/src/app/screens/connect/permissionsList.tsx b/src/app/screens/connect/permissionsList.tsx new file mode 100644 index 000000000..4b23658f6 --- /dev/null +++ b/src/app/screens/connect/permissionsList.tsx @@ -0,0 +1,47 @@ +import { Check } from '@phosphor-icons/react'; +import { useTranslation } from 'react-i18next'; +import styled, { useTheme } from 'styled-components'; + +const PermissionsTitle = styled.p((props) => ({ + ...props.theme.typography.body_medium_m, + color: props.theme.colors.white_200, + textAlign: 'left', + marginTop: 24, +})); + +const Permission = styled.div((props) => ({ + ...props.theme.typography.body_medium_m, + color: props.theme.colors.white_0, + marginTop: 12, + display: 'flex', + alignItems: 'center', +})); + +const PermissionIcon = styled.div((props) => ({ + display: 'flex', + marginRight: props.theme.space.xs, +})); + +function PermissionsList() { + const { t } = useTranslation('translation', { keyPrefix: 'SELECT_BTC_ADDRESS_SCREEN' }); + const theme = useTheme(); + return ( + <> + {t('PERMISSIONS_TITLE')} + + + + + {t('PERMISSION_WALLET_BALANCE')} + + + + + + {t('PERMISSION_REQUEST_TX')} + + + ); +} + +export default PermissionsList; diff --git a/src/app/screens/connect/selectAccount.tsx b/src/app/screens/connect/selectAccount.tsx new file mode 100644 index 000000000..d74aa6148 --- /dev/null +++ b/src/app/screens/connect/selectAccount.tsx @@ -0,0 +1,100 @@ +import LedgerBadge from '@assets/img/ledger/ledger_badge.svg'; +import { CaretRight } from '@phosphor-icons/react'; +import { Account } from '@secretkeylabs/xverse-core'; +import { getAccountGradient } from '@utils/gradient'; +import { isHardwareAccount } from '@utils/helper'; +import { useTranslation } from 'react-i18next'; +import styled, { useTheme } from 'styled-components'; + +const AccountInfoContainer = styled.button((props) => ({ + width: '100%', + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + borderRadius: 12, + border: `1px solid ${props.theme.colors.white_850}`, + backgroundColor: props.theme.colors.elevation6_800, + padding: props.theme.space.m, +})); + +const CurrentAccountContainer = styled.div((props) => ({ + display: 'flex', + alignItems: 'center', +})); + +const CurrentAccountTextContainer = styled.div((props) => ({ + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + gap: props.theme.spacing(4), + paddingLeft: props.theme.spacing(4), +})); + +const CurrentSelectedAccountText = styled.h1((props) => ({ + ...props.theme.typography.body_medium_m, + color: props.theme.colors.white_0, + textAlign: 'start', +})); + +interface GradientCircleProps { + firstGradient: string; + secondGradient: string; + thirdGradient: string; +} +const GradientCircle = styled.div((props) => ({ + width: 20, + height: 20, + borderRadius: '50%', + background: `linear-gradient(to bottom,${props.firstGradient}, ${props.secondGradient},${props.thirdGradient} )`, +})); + +const SwitchAccountContainer = styled.div(() => ({ + display: 'flex', + alignItems: 'center', +})); +const SwitchAccountText = styled.p((props) => ({ + ...props.theme.typography.body_medium_m, + color: props.theme.colors.white_0, + marginRight: props.theme.space.xs, + textAlign: 'start', +})); + +type SelectAccountProps = { + account: Account; + handlePressAccount: () => void; +}; + +function SelectAccount({ account, handlePressAccount }: SelectAccountProps) { + const gradient = getAccountGradient(account?.stxAddress || account?.btcAddress!); + // const { t } = useTranslation('translation', { keyPrefix: 'DASHBOARD_SCREEN' }); + const { t } = useTranslation('translation', { keyPrefix: 'SELECT_BTC_ADDRESS_SCREEN' }); + const theme = useTheme(); + const getName = () => + account?.accountName ?? + account?.bnsName ?? + `${t('ACCOUNT_NAME')} ${`${(account?.id ?? 0) + 1}`}`; + + return ( + + + + {account && ( + + {getName()} + {isHardwareAccount(account) && Ledger icon} + + )} + + + {t('CHANGE_ACCOUNT_BUTTON')} + + + + ); +} + +export default SelectAccount; diff --git a/src/assets/img/webInteractions/authPlaceholder.svg b/src/assets/img/webInteractions/authPlaceholder.svg deleted file mode 100644 index 0d5f6780c..000000000 --- a/src/assets/img/webInteractions/authPlaceholder.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/inpage/index.ts b/src/inpage/index.ts index e9b634585..b2cf92e96 100644 --- a/src/inpage/index.ts +++ b/src/inpage/index.ts @@ -5,14 +5,10 @@ import SatsMethodsProvider from './sats.inpage'; import StacksMethodsProvider from './stacks.inpage'; declare global { - interface Window { - XverseProviders: { - StacksProvider: StacksProvider; - BitcoinProvider: BitcoinProvider; - }; + interface XverseProviders { + StacksProvider: StacksProvider; } } - // we inject these in case implementors call the default providers window.StacksProvider = StacksMethodsProvider as StacksProvider; window.BitcoinProvider = SatsMethodsProvider as BitcoinProvider; @@ -20,6 +16,7 @@ window.BitcoinProvider = SatsMethodsProvider as BitcoinProvider; // We also inject the providers in an Xverse object in order to have them exclusively available for Xverse wallet // and not clash with providers from other wallets window.XverseProviders = { + // @ts-ignore StacksProvider: StacksMethodsProvider as StacksProvider, BitcoinProvider: SatsMethodsProvider as BitcoinProvider, }; diff --git a/src/locales/en.json b/src/locales/en.json index 7cb2f7a7f..652787f0e 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -494,10 +494,17 @@ "CANCEL_BUTTON": "Cancel" }, "AUTH_REQUEST_SCREEN": { - "TITLE": "Connection", + "TITLE": "Connection Request", + "ACCOUNT": "Account", + "BITCOIN_ADDRESS": "Bitcoin address", + "ORDINAL_ADDRESS": "Ordinal address", + "STX_ADDRESS": "Stacks address", "REQUEST_TOOLTIP": "Requested by", "CONNECT_BUTTON": "Connect", "CANCEL_BUTTON": "Cancel", + "PERMISSIONS_TITLE": "The app will be able to:", + "PERMISSION_WALLET_BALANCE": "See your wallet balance and activity", + "PERMISSION_REQUEST_TX": "Request transaction signing", "NO_STACKS_AUTH_SUPPORT": { "TITLE": "This wallet does not have a Stacks address.", "LINK": "Create an address here" @@ -1036,14 +1043,21 @@ "DO_NOT_SHOW_MESSAGE": "Do not show this message again" }, "SELECT_BTC_ADDRESS_SCREEN": { - "TITLE": "Bitcoin Address Request", - "ACCOUNT": "Account", - "CONNECT_BUTTON": "Approve", - "CANCEL_BUTTON": "Dismiss", - "BITCOIN_ADDRESS": "Bitcoin address", - "ORDINAL_ADDRESS": "Ordinal address", + "TITLE": "Connection Request", + "ACCOUNT_NAME": "Account", + "CONNECT_BUTTON": "Connect", + "CANCEL_BUTTON": "Cancel", + "BITCOIN_ADDRESS": "Payments address", + "ORDINAL_ADDRESS": "Ordinals address", + "STX_ADDRESS": "Stacks address", + "CHANGE_ACCOUNT_BUTTON": "Change account", "NETWORK_MISMATCH_ERROR_TITLE": "Mismatched Network", - "NETWORK_MISMATCH_ERROR_DESCRIPTION": "The app is requesting your wallet address for a different network. You may have to switch your active network in wallet settings." + "NETWORK_MISMATCH_ERROR_DESCRIPTION": "The app is requesting your wallet address for a different network. You may have to switch your active network in wallet settings.", + "INVALID_PURPOSE_ERROR_TITLE": "Invalid Request", + "INVALID_PURPOSE_ERROR_DESCRIPTION": "The app is requesting a wallet address with an invalid purpose. Please contact the developer of the requesting app for support.", + "PERMISSIONS_TITLE": "The app will be able to:", + "PERMISSION_WALLET_BALANCE": "See your wallet balance and activity", + "PERMISSION_REQUEST_TX": "Request transaction signing" }, "SEND_BRC20": { "BRC20_TOKEN": "BRC-20", From f1455c109b33f91cd2d773c60529d9cd3605fe12 Mon Sep 17 00:00:00 2001 From: Mahmoud Aboelenein Date: Fri, 26 Jan 2024 10:44:18 +0200 Subject: [PATCH 15/20] disable continue button on confirm password (#776) --- src/app/screens/createPassword/index.tsx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/app/screens/createPassword/index.tsx b/src/app/screens/createPassword/index.tsx index 1df095a90..88c82b4b1 100644 --- a/src/app/screens/createPassword/index.tsx +++ b/src/app/screens/createPassword/index.tsx @@ -44,6 +44,7 @@ function CreatePassword(): JSX.Element { const [confirmPassword, setConfirmPassword] = useState(''); const [error, setError] = useState(''); const [currentStepIndex, setCurrentStepIndex] = useState(0); + const [isCreatingWallet, setIsCreatingWallet] = useState(false); const navigate = useNavigate(); const { t } = useTranslation('translation', { keyPrefix: 'CREATE_PASSWORD_SCREEN' }); const { createWallet } = useWalletReducer(); @@ -56,11 +57,17 @@ function CreatePassword(): JSX.Element { const handleConfirmPassword = async () => { if (confirmPassword === password) { - disableWalletExistsGuard?.(); - const seedPhrase = await getSeed(); - await createWallet(seedPhrase); - await changePassword('', password); - navigate('/wallet-success/create', { replace: true }); + try { + setIsCreatingWallet(true); + disableWalletExistsGuard?.(); + const seedPhrase = await getSeed(); + await createWallet(seedPhrase); + await changePassword('', password); + navigate('/wallet-success/create', { replace: true }); + } catch (err) { + setIsCreatingWallet(false); + setError(err as string); + } } else { setError(t('CONFIRM_PASSWORD_MATCH_ERROR')); } @@ -104,6 +111,7 @@ function CreatePassword(): JSX.Element { handleContinue={handleConfirmPassword} handleBack={handleConfirmPasswordBack} passwordError={error} + loading={isCreatingWallet} /> )} From f5ccbfdff09bd41d5890feebf6d54990772d0670 Mon Sep 17 00:00:00 2001 From: Victor Kirov Date: Fri, 26 Jan 2024 11:27:53 +0200 Subject: [PATCH 16/20] Ensure correct ordinal is shown in ordinal detail view (#779) --- src/app/hooks/queries/ordinals/useInscription.ts | 4 ++-- src/app/screens/ordinalDetail/useOrdinalDetail.ts | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/app/hooks/queries/ordinals/useInscription.ts b/src/app/hooks/queries/ordinals/useInscription.ts index 8e41d4c32..b57a2c5f5 100644 --- a/src/app/hooks/queries/ordinals/useInscription.ts +++ b/src/app/hooks/queries/ordinals/useInscription.ts @@ -6,10 +6,10 @@ import { handleRetries, InvalidParamsError } from '@utils/query'; /** * Get inscriptions details by collection id */ -const useAddressInscription = (ordinalId: string, ordinal: Inscription | null) => { +const useAddressInscription = (ordinalId: string, ordinal?: Inscription | null) => { const { ordinalsAddress, network } = useWalletSelector(); const fetchOrdinals = async (): Promise => { - if (ordinal) return ordinal; + if (ordinal && ordinal.id === ordinalId) return ordinal; if (!ordinalsAddress || !ordinalId) { throw new InvalidParamsError('ordinalsAddress and ordinalId are required'); } diff --git a/src/app/screens/ordinalDetail/useOrdinalDetail.ts b/src/app/screens/ordinalDetail/useOrdinalDetail.ts index 9ba917938..35e5de44b 100644 --- a/src/app/screens/ordinalDetail/useOrdinalDetail.ts +++ b/src/app/screens/ordinalDetail/useOrdinalDetail.ts @@ -2,7 +2,6 @@ import { useGetUtxoOrdinalBundle } from '@hooks/queries/ordinals/useAddressRareS import useInscriptionCollectionMarketData from '@hooks/queries/ordinals/useCollectionMarketData'; import useAddressInscription from '@hooks/queries/ordinals/useInscription'; import usePendingOrdinalTxs from '@hooks/queries/usePendingOrdinalTx'; -import useNftDataSelector from '@hooks/stores/useNftDataSelector'; import useOrdinalDataReducer from '@hooks/stores/useOrdinalReducer'; import useSatBundleDataReducer from '@hooks/stores/useSatBundleReducer'; import useTextOrdinalContent from '@hooks/useTextOrdinalContent'; @@ -23,8 +22,7 @@ export default function useOrdinalDetail() { const { ordinalsAddress, network, selectedAccount, hasActivatedRareSatsKey } = useWalletSelector(); const { id } = useParams(); - const { selectedOrdinal } = useNftDataSelector(); - const { data: ordinalData, isLoading } = useAddressInscription(id!, selectedOrdinal); + const { data: ordinalData, isLoading } = useAddressInscription(id!); const { data: collectionMarketData } = useInscriptionCollectionMarketData( ordinalData?.collection_id, ); From 410001a32e3d394def8d5231d407a37e0adc5d56 Mon Sep 17 00:00:00 2001 From: fede erbes Date: Fri, 26 Jan 2024 17:17:58 +0100 Subject: [PATCH 17/20] fix: fetch of brc20 in token management screen (#782) --- src/app/hooks/queries/useAccountBalance.ts | 4 +++- src/app/hooks/queries/useBtcCoinsBalance.ts | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/app/hooks/queries/useAccountBalance.ts b/src/app/hooks/queries/useAccountBalance.ts index 5a1b7d8ad..2e86c7b26 100644 --- a/src/app/hooks/queries/useAccountBalance.ts +++ b/src/app/hooks/queries/useAccountBalance.ts @@ -34,9 +34,11 @@ const useAccountBalance = () => { const fetchBrcCoinsBalances = async (ordinalsAddress: string) => { try { const ordinalsFtBalance = await getOrdinalsFtBalance(network.type, ordinalsAddress); + const tickers = ordinalsFtBalance?.map((o) => o.ticker!) ?? []; const brc20Tokens = await getBrc20Tokens( network.type, - ordinalsFtBalance?.map((o) => o.ticker!) ?? [], + // workaround for brc20 tokens not being returned + tickers.length ? tickers : ['ORDI'], fiatCurrency, ); diff --git a/src/app/hooks/queries/useBtcCoinsBalance.ts b/src/app/hooks/queries/useBtcCoinsBalance.ts index c6fbfadf3..b68362f01 100644 --- a/src/app/hooks/queries/useBtcCoinsBalance.ts +++ b/src/app/hooks/queries/useBtcCoinsBalance.ts @@ -32,9 +32,11 @@ const useBtcCoinBalance = () => { const fetchBrcCoinsBalances = async () => { try { const ordinalsFtBalance = await getOrdinalsFtBalance(network.type, ordinalsAddress); + const tickers = ordinalsFtBalance?.map((o) => o.ticker!) ?? []; const brc20Tokens = await getBrc20Tokens( network.type, - ordinalsFtBalance?.map((o) => o.ticker!) ?? [], + // workaround for brc20 tokens not being returned + tickers.length ? tickers : ['ORDI'], fiatCurrency, ); From 7ae43a5098b054ad69071c3bb3be45857b0a82ec Mon Sep 17 00:00:00 2001 From: Mahmoud Aboelenein Date: Fri, 26 Jan 2024 18:28:32 +0200 Subject: [PATCH 18/20] Connection Screens Revamp Follow-up (#780) authored-by: Mahmoud Aboelenein * resolve ui issues * missing stx-address call out --------- --- .../connect/authenticationRequest/index.tsx | 85 ++++++++++++------- .../connect/btcSelectAddressScreen/index.tsx | 76 +++++++++++------ src/app/screens/connect/selectAccount.tsx | 52 +++++++----- src/locales/en.json | 4 +- 4 files changed, 137 insertions(+), 80 deletions(-) diff --git a/src/app/screens/connect/authenticationRequest/index.tsx b/src/app/screens/connect/authenticationRequest/index.tsx index 011ecc095..42b88ab76 100644 --- a/src/app/screens/connect/authenticationRequest/index.tsx +++ b/src/app/screens/connect/authenticationRequest/index.tsx @@ -6,11 +6,11 @@ import { MESSAGE_SOURCE } from '@common/types/message-types'; import { delay } from '@common/utils/ledger'; import BottomModal from '@components/bottomModal'; import ActionButton from '@components/button'; -import InfoContainer from '@components/infoContainer'; import LedgerConnectionView from '@components/ledger/connectLedgerView'; import useSeedVault from '@hooks/useSeedVault'; import useWalletSelector from '@hooks/useWalletSelector'; import Transport from '@ledgerhq/hw-transport-webusb'; +import { animated, useSpring } from '@react-spring/web'; import SelectAccount from '@screens/connect/selectAccount'; import { AuthRequest, @@ -18,6 +18,7 @@ import { handleLedgerStxJWTAuth, } from '@secretkeylabs/xverse-core'; import { AddressVersion, StacksMessageType, publicKeyToAddress } from '@stacks/transactions'; +import Callout from '@ui-library/callout'; import { StickyHorizontalSplitButtonContainer } from '@ui-library/common.styled'; import { isHardwareAccount } from '@utils/helper'; import { decodeToken } from 'jsontokens'; @@ -30,9 +31,10 @@ import validUrl from 'valid-url'; import AddressPurposeBox from '../addressPurposeBox'; import PermissionsList from '../permissionsList'; -const MainContainer = styled.div((props) => ({ +const MainContainer = styled(animated.div)((props) => ({ display: 'flex', flexDirection: 'column', + flex: 1, paddingLeft: props.theme.space.m, paddingRight: props.theme.space.m, ...props.theme.scrollbar, @@ -76,8 +78,8 @@ const DappName = styled.h2((props) => ({ })); const InfoContainerWrapper = styled.div((props) => ({ - margin: props.theme.spacing(10), - marginBottom: 0, + marginTop: props.theme.spacing(10), + marginBottom: 'auto', })); const AddressesContainer = styled.div((props) => ({ @@ -109,6 +111,17 @@ function AuthenticationRequest() { const { getSeed } = useSeedVault(); const isDisabled = !selectedAccount?.stxAddress; + const styles = useSpring({ + from: { + opacity: 0, + y: 24, + }, + to: { + y: 0, + opacity: 1, + }, + }); + const confirmCallback = async () => { setLoading(true); try { @@ -243,42 +256,52 @@ function AuthenticationRequest() { }; return ( - + {getDappLogo()} {t('TITLE')} {`${t('REQUEST_TOOLTIP')} ${authRequest.payload.appDetails?.name}`} - - - - - - - - - - - - {isDisabled && ( + {!isDisabled ? ( + <> + + + + + + + + + ) : ( - )} + + + + + + setIsModalVisible(false)}> {currentStepIndex === 0 && ( diff --git a/src/app/screens/connect/btcSelectAddressScreen/index.tsx b/src/app/screens/connect/btcSelectAddressScreen/index.tsx index c1573cb83..39858e68a 100644 --- a/src/app/screens/connect/btcSelectAddressScreen/index.tsx +++ b/src/app/screens/connect/btcSelectAddressScreen/index.tsx @@ -4,19 +4,20 @@ import OrdinalsIcon from '@assets/img/nftDashboard/white_ordinals_icon.svg'; import ActionButton from '@components/button'; import useBtcAddressRequest from '@hooks/useBtcAddressRequest'; import useWalletSelector from '@hooks/useWalletSelector'; -import { animated, useSpring } from '@react-spring/web'; +import { animated, useTransition } from '@react-spring/web'; import SelectAccount from '@screens/connect/selectAccount'; import { StickyHorizontalSplitButtonContainer } from '@ui-library/common.styled'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; +import { MoonLoader } from 'react-spinners'; import { AddressPurpose } from 'sats-connect'; import styled from 'styled-components'; import AddressPurposeBox from '../addressPurposeBox'; import PermissionsList from '../permissionsList'; import { getAppIconFromWebManifest } from './helper'; -const OuterContainer = styled(animated.div)((props) => ({ +const OuterContainer = styled.div((props) => ({ display: 'flex', flexDirection: 'column', paddingLeft: props.theme.space.m, @@ -71,25 +72,28 @@ const PermissionsContainer = styled.div((props) => ({ paddingBottom: props.theme.space.xxl, })); +const LoaderContainer = styled.div(() => ({ + justifySelf: 'center', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + flex: 1, +})); + function BtcSelectAddressScreen() { const [loading, setLoading] = useState(false); const navigate = useNavigate(); const { t } = useTranslation('translation', { keyPrefix: 'SELECT_BTC_ADDRESS_SCREEN' }); const { network, btcAddress, ordinalsAddress, stxAddress, selectedAccount } = useWalletSelector(); const [appIcon, setAppIcon] = useState(''); + const [isLoadingIcon, setIsLoadingIcon] = useState(false); const { payload, origin, approveBtcAddressRequest, cancelAddressRequest } = useBtcAddressRequest(); const appUrl = useMemo(() => origin.replace(/(^\w+:|^)\/\//, ''), [origin]); - const styles = useSpring({ - from: { - opacity: 0, - y: 24, - }, - to: { - y: 0, - opacity: 1, - }, + const transition = useTransition(isLoadingIcon, { + from: { opacity: 0, y: 30 }, + enter: { opacity: 1, y: 0 }, }); const confirmCallback = async () => { @@ -139,8 +143,10 @@ function BtcSelectAddressScreen() { useEffect(() => { (async () => { if (origin !== '') { + setIsLoadingIcon(true); getAppIconFromWebManifest(origin).then((appIcons) => { setAppIcon(appIcons); + setIsLoadingIcon(false); }); } })(); @@ -188,23 +194,37 @@ function BtcSelectAddressScreen() { navigate('/account-list?hideListActions=true'); }; - return ( - - - {appIcon !== '' ? : null} - {t('TITLE')} - {appUrl} - - {payload.message ? {payload.message.substring(0, 80)} : null} - - {payload.purposes.map(AddressPurposeRow)} - - - - - - - + return isLoadingIcon ? ( + + + + ) : ( + + {transition((style) => ( + + + {appIcon !== '' ? : null} + {t('TITLE')} + {appUrl} + + {payload.message ? ( + {payload.message.substring(0, 80)} + ) : null} + + {payload.purposes.map(AddressPurposeRow)} + + + + + + + + + ))} ); } diff --git a/src/app/screens/connect/selectAccount.tsx b/src/app/screens/connect/selectAccount.tsx index d74aa6148..8d8b33326 100644 --- a/src/app/screens/connect/selectAccount.tsx +++ b/src/app/screens/connect/selectAccount.tsx @@ -17,17 +17,17 @@ const AccountInfoContainer = styled.button((props) => ({ padding: props.theme.space.m, })); -const CurrentAccountContainer = styled.div((props) => ({ +const CurrentAccountContainer = styled.div({ display: 'flex', alignItems: 'center', -})); +}); const CurrentAccountTextContainer = styled.div((props) => ({ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: props.theme.spacing(4), - paddingLeft: props.theme.spacing(4), + paddingLeft: props.theme.space.xs, })); const CurrentSelectedAccountText = styled.h1((props) => ({ @@ -48,6 +48,11 @@ const GradientCircle = styled.div((props) => ({ background: `linear-gradient(to bottom,${props.firstGradient}, ${props.secondGradient},${props.thirdGradient} )`, })); +const AccountTag = styled.div(() => ({ + display: 'flex', + alignItems: 'center', +})); + const SwitchAccountContainer = styled.div(() => ({ display: 'flex', alignItems: 'center', @@ -66,28 +71,37 @@ type SelectAccountProps = { function SelectAccount({ account, handlePressAccount }: SelectAccountProps) { const gradient = getAccountGradient(account?.stxAddress || account?.btcAddress!); - // const { t } = useTranslation('translation', { keyPrefix: 'DASHBOARD_SCREEN' }); const { t } = useTranslation('translation', { keyPrefix: 'SELECT_BTC_ADDRESS_SCREEN' }); const theme = useTheme(); - const getName = () => - account?.accountName ?? - account?.bnsName ?? - `${t('ACCOUNT_NAME')} ${`${(account?.id ?? 0) + 1}`}`; + const getName = () => { + const maxNameCharacters = isHardwareAccount(account) || account.bnsName ? 12 : 20; + const maxLength = + account?.accountName && account?.accountName?.length > maxNameCharacters ? '...' : ''; + if (account.accountName) { + return `${account?.accountName?.slice(0, maxNameCharacters)}${maxLength}`; + } + if (account.bnsName) { + return `${account.bnsName.slice(0, maxNameCharacters)}${maxLength}`; + } + return `${t('ACCOUNT_NAME')} ${`${(account?.id ?? 0) + 1}`}`; + }; return ( - - {account && ( - - {getName()} - {isHardwareAccount(account) && Ledger icon} - - )} + + + {account && ( + + {getName()} + {isHardwareAccount(account) && Ledger icon} + + )} + {t('CHANGE_ACCOUNT_BUTTON')} diff --git a/src/locales/en.json b/src/locales/en.json index 652787f0e..5e63081bf 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -507,7 +507,7 @@ "PERMISSION_REQUEST_TX": "Request transaction signing", "NO_STACKS_AUTH_SUPPORT": { "TITLE": "This wallet does not have a Stacks address.", - "LINK": "Create an address here" + "LINK": "Create a Stacks address" }, "LEDGER": { "CONNECT": { @@ -1050,7 +1050,7 @@ "BITCOIN_ADDRESS": "Payments address", "ORDINAL_ADDRESS": "Ordinals address", "STX_ADDRESS": "Stacks address", - "CHANGE_ACCOUNT_BUTTON": "Change account", + "CHANGE_ACCOUNT_BUTTON": "Select", "NETWORK_MISMATCH_ERROR_TITLE": "Mismatched Network", "NETWORK_MISMATCH_ERROR_DESCRIPTION": "The app is requesting your wallet address for a different network. You may have to switch your active network in wallet settings.", "INVALID_PURPOSE_ERROR_TITLE": "Invalid Request", From 4cc5a420a0ce9e3aa5c8d816cc53c1a0d9cf5439 Mon Sep 17 00:00:00 2001 From: Tim Man Date: Mon, 29 Jan 2024 21:36:27 +0800 Subject: [PATCH 19/20] fix: reset balance state between change accounts (#783) --- src/app/stores/wallet/reducer.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/app/stores/wallet/reducer.ts b/src/app/stores/wallet/reducer.ts index ec16e2184..0bee1a1d3 100644 --- a/src/app/stores/wallet/reducer.ts +++ b/src/app/stores/wallet/reducer.ts @@ -158,6 +158,15 @@ const walletReducer = ( network: action.network, accountType: action.accountType, accountName: action.accountName, + btcBalance: '', + stxBalance: '', + stxAvailableBalance: '', + coinsList: state.coinsList + ? state.coinsList.map((coin) => ({ ...coin, balance: '0' })) + : [], + brcCoinsList: state.brcCoinsList + ? state.brcCoinsList.map((coin) => ({ ...coin, balance: '0' })) + : [], }; case StoreEncryptedSeedKey: return { From 5474dea2e6749bd053e69d26075e674088901f64 Mon Sep 17 00:00:00 2001 From: Tim Man Date: Tue, 30 Jan 2024 22:47:53 +0800 Subject: [PATCH 20/20] chore: bump core version to fix ledger tr signing --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 99fe27c99..08c07d996 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@phosphor-icons/react": "^2.0.10", "@react-spring/web": "^9.6.1", "@scure/btc-signer": "1.2.1", - "@secretkeylabs/xverse-core": "9.1.0", + "@secretkeylabs/xverse-core": "9.1.2", "@stacks/connect": "7.4.1", "@stacks/stacks-blockchain-api-types": "6.1.1", "@stacks/transactions": "6.9.0", @@ -1735,9 +1735,9 @@ } }, "node_modules/@secretkeylabs/xverse-core": { - "version": "9.1.0", - "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/9.1.0/4cd1e5ec21fe43d21f8c91bb9d7cd5521f8f1169", - "integrity": "sha512-cub6HJ79eAJY6tw7Pi8r1wyuitX8eEQQFHJMnVLX8EKkubCbYkC+DblVmCebH1P9L4I41BXp5XifpN366uwNvQ==", + "version": "9.1.2", + "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/9.1.2/fdd0d4e6f7a2fed163453b17f5e402f7673e323b", + "integrity": "sha512-PtkrO22QQvbt96YCmxJwQqrNtNT9z4oHQam2TqLpFK+dyWL+mB8TI93NWbBxDNxf8vdEjNPtM6NdFU2WwCYe7A==", "license": "ISC", "dependencies": { "@bitcoinerlab/secp256k1": "^1.0.2", @@ -15448,9 +15448,9 @@ } }, "@secretkeylabs/xverse-core": { - "version": "9.1.0", - "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/9.1.0/4cd1e5ec21fe43d21f8c91bb9d7cd5521f8f1169", - "integrity": "sha512-cub6HJ79eAJY6tw7Pi8r1wyuitX8eEQQFHJMnVLX8EKkubCbYkC+DblVmCebH1P9L4I41BXp5XifpN366uwNvQ==", + "version": "9.1.2", + "resolved": "https://npm.pkg.github.com/download/@secretkeylabs/xverse-core/9.1.2/fdd0d4e6f7a2fed163453b17f5e402f7673e323b", + "integrity": "sha512-PtkrO22QQvbt96YCmxJwQqrNtNT9z4oHQam2TqLpFK+dyWL+mB8TI93NWbBxDNxf8vdEjNPtM6NdFU2WwCYe7A==", "requires": { "@bitcoinerlab/secp256k1": "^1.0.2", "@noble/curves": "^1.2.0", diff --git a/package.json b/package.json index efa6f2c34..26b908ac1 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "@phosphor-icons/react": "^2.0.10", "@react-spring/web": "^9.6.1", "@scure/btc-signer": "1.2.1", - "@secretkeylabs/xverse-core": "9.1.0", + "@secretkeylabs/xverse-core": "9.1.2", "@stacks/connect": "7.4.1", "@stacks/stacks-blockchain-api-types": "6.1.1", "@stacks/transactions": "6.9.0",