diff --git a/app/api/api.ts b/app/api/api.ts index 4bd9f46c1..a369ddfdb 100644 --- a/app/api/api.ts +++ b/app/api/api.ts @@ -15,8 +15,8 @@ import { CoreNodePoxResponse, CoreNodeInfoResponse, NetworkBlockTimesResponse, -} from '@blockstack/stacks-blockchain-api-types'; -import { AddressTransactionsWithTransfersListResponse } from '@stacks/stacks-blockchain-api-types'; + AddressTransactionsWithTransfersListResponse, +} from '@stacks/stacks-blockchain-api-types'; import packageJson from '../../package.json'; const defaultHeaders = [ diff --git a/app/components/home/delegation-card.tsx b/app/components/home/delegation-card.tsx index 92fbf3ff7..ad2f6b4f8 100644 --- a/app/components/home/delegation-card.tsx +++ b/app/components/home/delegation-card.tsx @@ -89,9 +89,7 @@ export const DelegationCard: FC = () => { - - {stackerInfo?.stackingPercentage ? stackerInfo?.stackingPercentage : '0'}% - + {stackerInfo?.stackingPercentage ?? 0}% diff --git a/app/components/home/transaction-list/mempool-tx-label.ts b/app/components/home/transaction-list/mempool-tx-label.ts index b0edb4aa7..fec9571b3 100644 --- a/app/components/home/transaction-list/mempool-tx-label.ts +++ b/app/components/home/transaction-list/mempool-tx-label.ts @@ -1,4 +1,4 @@ -import { MempoolTransaction } from '@blockstack/stacks-blockchain-api-types'; +import { MempoolTransaction } from '@stacks/stacks-blockchain-api-types'; import { isStackingTx, isDelegateStxTx, isRevokingDelegationTx } from '@utils/tx-utils'; export function getMempoolTxLabel(tx: MempoolTransaction, address: string, contractId: string) { diff --git a/app/components/home/transaction-list/transaction-list-context-menu.ts b/app/components/home/transaction-list/transaction-list-context-menu.ts index 4b54b6e83..422e5dac5 100644 --- a/app/components/home/transaction-list/transaction-list-context-menu.ts +++ b/app/components/home/transaction-list/transaction-list-context-menu.ts @@ -1,4 +1,4 @@ -import { Transaction } from '@blockstack/stacks-blockchain-api-types'; +import { Transaction } from '@stacks/stacks-blockchain-api-types'; import { hasMemo, getRecipientAddress } from '@utils/tx-utils'; import { features } from '../../../constants/index'; diff --git a/app/components/home/transaction-list/transaction-list-empty.tsx b/app/components/home/transaction-list/transaction-list-empty.tsx index f84c47735..02f5da6b3 100644 --- a/app/components/home/transaction-list/transaction-list-empty.tsx +++ b/app/components/home/transaction-list/transaction-list-empty.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import { Text, Flex } from '@stacks/ui'; +import { Text, Flex, Box, color } from '@stacks/ui'; +import { EmptyTxList } from '@components/icons/empty-tx-list'; import { useNavigatorOnline } from '@hooks/use-navigator-online'; import { templateTxBoxProps } from './transaction-list-item-pseudo'; @@ -8,7 +9,16 @@ export const TransactionListEmpty = () => { const { isOnline } = useNavigatorOnline(); return ( - + + + + {isOnline ? `You haven't made any transactions yet` : `Cannot fetch transactions. Ensure you're connected to the internet`} diff --git a/app/components/home/transaction-list/transaction-list-item-mempool.tsx b/app/components/home/transaction-list/transaction-list-item-mempool.tsx index e32e74969..17a52a9ea 100644 --- a/app/components/home/transaction-list/transaction-list-item-mempool.tsx +++ b/app/components/home/transaction-list/transaction-list-item-mempool.tsx @@ -1,7 +1,7 @@ import React, { FC, useRef, useEffect, MutableRefObject } from 'react'; import { useHover, useFocus } from 'use-events'; import { Box, color, Flex, Stack, Text } from '@stacks/ui'; -import { MempoolTransaction } from '@blockstack/stacks-blockchain-api-types'; +import { MempoolTransaction } from '@stacks/stacks-blockchain-api-types'; import { getTxTypeName } from '@stacks/ui-utils'; import { toHumanReadableStx } from '@utils/unit-convert'; diff --git a/app/components/icons/empty-tx-list.tsx b/app/components/icons/empty-tx-list.tsx new file mode 100644 index 000000000..b3510d23e --- /dev/null +++ b/app/components/icons/empty-tx-list.tsx @@ -0,0 +1,1135 @@ +import { color } from '@stacks/ui-utils'; +import React from 'react'; + +export const EmptyTxList: React.FC = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +); diff --git a/app/constants/index.ts b/app/constants/index.ts index bb092308a..dc5b4c278 100644 --- a/app/constants/index.ts +++ b/app/constants/index.ts @@ -62,13 +62,15 @@ export const REVOKE_DELEGATION_TX_SIZE_BYTES = 165; export const STACKING_CONTRACT_CALL_FEE = 260; -export const POOLED_STACKING_TX_SIZE_BYTES = 199; +export const POOLED_STACKING_TX_SIZE_BYTES = 216; export const SUPPORTED_BTC_ADDRESS_FORMATS = ['p2pkh', 'p2sh'] as const; -export const SUPPORTED_LEDGER_VERSION_MAJOR = 0; +export const LATEST_LEDGER_VERSION_MAJOR = 0; -export const SUPPORTED_LEDGER_VERSION_MINOR = 11; +export const LATEST_LEDGER_VERSION_MINOR = 14; + +export const EARLIEST_SUPPORTED_LEDGER_VERSION = '0.11.0'; export const DEFAULT_POLLING_INTERVAL = 10_000; diff --git a/app/hooks/use-mempool.ts b/app/hooks/use-mempool.ts index f9f75d4ac..6d467b510 100644 --- a/app/hooks/use-mempool.ts +++ b/app/hooks/use-mempool.ts @@ -5,7 +5,7 @@ import { useQuery } from 'react-query'; import { selectAddress } from '@store/keys'; import { ApiResource } from '@models'; import { useFetchAccountNonce } from '@hooks/use-fetch-account-nonce'; -import { MempoolTransaction } from '@blockstack/stacks-blockchain-api-types'; +import { MempoolTransaction } from '@stacks/stacks-blockchain-api-types'; import { useApi } from './use-api'; interface UseMempool { diff --git a/app/hooks/use-prepare-ledger.ts b/app/hooks/use-prepare-ledger.ts index 674549413..787cc4015 100644 --- a/app/hooks/use-prepare-ledger.ts +++ b/app/hooks/use-prepare-ledger.ts @@ -1,12 +1,15 @@ import { useEffect, useMemo, useState } from 'react'; import { LedgerError } from '@zondax/ledger-blockstack'; +import compareVersions from 'compare-versions'; import { Observable } from 'rxjs'; import { filter } from 'rxjs/operators'; -import { SUPPORTED_LEDGER_VERSION_MAJOR, SUPPORTED_LEDGER_VERSION_MINOR } from '@constants/index'; +import { EARLIEST_SUPPORTED_LEDGER_VERSION } from '@constants/index'; +import { isTestnet } from '@utils/network-utils'; import type { LedgerMessageEvents } from '../main/register-ledger-listeners'; import { useListenLedgerEffect } from './use-listen-ledger-effect'; import { messages$ } from './use-message-events'; +import { useCheckForUpdates } from './use-check-for-updates'; export enum LedgerConnectStep { Disconnected, @@ -29,22 +32,43 @@ export function usePrepareLedger() { const [step, setStep] = useState(LedgerConnectStep.Disconnected); const [isLocked, setIsLocked] = useState(false); const [appVersion, setAppVersion] = useState(null); + const { isNewerReleaseAvailable } = useCheckForUpdates(); + + const versionSupportsTestnetLedger = useMemo(() => { + if (appVersion === null) return false; + return appVersion.major >= 0 && appVersion.minor > 11; + }, [appVersion]); const isSupportedAppVersion = useMemo(() => { if (appVersion === null) return true; - return ( - appVersion.major === SUPPORTED_LEDGER_VERSION_MAJOR && - appVersion.minor === SUPPORTED_LEDGER_VERSION_MINOR - ); - }, [appVersion]); + if (!versionSupportsTestnetLedger && isTestnet()) return false; + const { major, minor, patch } = appVersion; + const currentVersion = `${major}.${minor}.${patch}`; + return compareVersions.compare(currentVersion, EARLIEST_SUPPORTED_LEDGER_VERSION, '>='); + }, [appVersion, versionSupportsTestnetLedger]); const appVersionErrorText = useMemo(() => { + if (!versionSupportsTestnetLedger && isTestnet()) { + return `Cannot use Ledger on testnet with app version 0.11.0 or lower. Upgrade on Ledger Live.`; + } return ` - Make sure to upgrade your Stacks app to the latest version in Ledger Live. - This version of the Stacks Wallet only works with ${SUPPORTED_LEDGER_VERSION_MAJOR}.${SUPPORTED_LEDGER_VERSION_MINOR}. - Detected version ${String(appVersion?.major)}.${String(appVersion?.minor)} + Make sure to upgrade your Stacks app to the latest version in Ledger Live. ${ + isNewerReleaseAvailable + ? 'You should also upgrade your Stacks Wallet to the latest version.' + : '' + } + This version of the Stacks Wallet works with ${EARLIEST_SUPPORTED_LEDGER_VERSION} onwards. + Detected version ${String(appVersion?.major)}.${String(appVersion?.minor)}.${String( + appVersion?.patch + )} `; - }, [appVersion]); + }, [ + appVersion?.major, + appVersion?.minor, + appVersion?.patch, + isNewerReleaseAvailable, + versionSupportsTestnetLedger, + ]); useListenLedgerEffect(); diff --git a/app/hooks/use-transaction-list.ts b/app/hooks/use-transaction-list.ts index 8800b645a..7e11a4865 100644 --- a/app/hooks/use-transaction-list.ts +++ b/app/hooks/use-transaction-list.ts @@ -1,7 +1,7 @@ import { useCallback, useMemo, useRef } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import * as R from 'ramda'; -import { MempoolTransaction } from '@blockstack/stacks-blockchain-api-types'; +import { MempoolTransaction } from '@stacks/stacks-blockchain-api-types'; import { useSelector } from 'react-redux'; import { RootState } from '@store/index'; import { selectTransactionList } from '@store/transaction'; diff --git a/app/main/ledger-actions.ts b/app/main/ledger-actions.ts index aa7766014..e2b2bea98 100644 --- a/app/main/ledger-actions.ts +++ b/app/main/ledger-actions.ts @@ -1,12 +1,21 @@ import Transport from '@ledgerhq/hw-transport'; +import { AddressVersion } from '@stacks/transactions'; import StacksApp, { LedgerError, ResponseAddress, ResponseSign } from '@zondax/ledger-blockstack'; +const chainIdMap = { + mainnet: AddressVersion.MainnetSingleSig, + testnet: AddressVersion.TestnetSingleSig, +}; + const STX_DERIVATION_PATH = `m/44'/5757'/0'/0/0`; export async function ledgerRequestStxAddress(transport: Transport | null) { if (!transport) throw new Error('No device transport'); const stacksApp = new StacksApp(transport); - const resp = await stacksApp.showAddressAndPubKey(STX_DERIVATION_PATH); + const resp = await stacksApp.showAddressAndPubKey( + STX_DERIVATION_PATH, + chainIdMap[process.env.STX_NETWORK as keyof typeof chainIdMap] + ); if (resp.publicKey) { return { ...resp, publicKey: resp.publicKey.toString('hex') }; } @@ -16,7 +25,10 @@ export async function ledgerRequestStxAddress(transport: Transport | null) { export async function ledgerShowStxAddress(transport: Transport | null) { if (!transport) throw new Error('No device transport'); const stacksApp = new StacksApp(transport); - const resp = await stacksApp.getAddressAndPubKey(STX_DERIVATION_PATH); + const resp = await stacksApp.getAddressAndPubKey( + STX_DERIVATION_PATH, + chainIdMap[process.env.STX_NETWORK as keyof typeof chainIdMap] + ); return resp; } diff --git a/app/modals/components/transaction-error.tsx b/app/modals/components/transaction-error.tsx index 1f86edc6b..3ffd631a7 100644 --- a/app/modals/components/transaction-error.tsx +++ b/app/modals/components/transaction-error.tsx @@ -1,5 +1,5 @@ import React, { FC } from 'react'; -import { PostCoreNodeTransactionsError } from '@blockstack/stacks-blockchain-api-types'; +import { PostCoreNodeTransactionsError } from '@stacks/stacks-blockchain-api-types'; import { TxModalButton, TxModalFooter } from '@modals/send-stx/send-stx-modal-layout'; import { FailedBroadcastError } from '@modals/send-stx/steps/failed-broadcast-error'; diff --git a/app/modals/delegated-stacking/delegated-stacking-modal.tsx b/app/modals/delegated-stacking/delegated-stacking-modal.tsx index bd40a8613..3bb649719 100644 --- a/app/modals/delegated-stacking/delegated-stacking-modal.tsx +++ b/app/modals/delegated-stacking/delegated-stacking-modal.tsx @@ -19,7 +19,7 @@ import { useBroadcastTx } from '@hooks/use-broadcast-tx'; import { ContractCallOptions, StacksTransaction } from '@stacks/transactions'; import { useMempool } from '@hooks/use-mempool'; -import { PostCoreNodeTransactionsError } from '@blockstack/stacks-blockchain-api-types'; +import { PostCoreNodeTransactionsError } from '@stacks/stacks-blockchain-api-types'; import { TxSigningModal } from '@modals/tx-signing-modal/tx-signing-modal'; diff --git a/app/modals/receive-stx/components/reveal-stx-address-ledger.tsx b/app/modals/receive-stx/components/reveal-stx-address-ledger.tsx index 56dedca5f..3f7cba2cd 100644 --- a/app/modals/receive-stx/components/reveal-stx-address-ledger.tsx +++ b/app/modals/receive-stx/components/reveal-stx-address-ledger.tsx @@ -10,9 +10,11 @@ import { RootState } from '@store/index'; import { AddressDisplayer } from './address-displayer'; import { SevereWarning } from '@components/severe-warning'; import { LedgerConnectStep, usePrepareLedger } from '@hooks/use-prepare-ledger'; +import { ErrorLabel } from '@components/error-label'; +import { ErrorText } from '@components/error-text'; export const RevealStxAddressLedger: FC = () => { - const { step, isLocked } = usePrepareLedger(); + const { step, isLocked, isSupportedAppVersion, appVersionErrorText } = usePrepareLedger(); const [address, setAddress] = useState(null); const [success, setSuccess] = useState(false); const [pendingLedgerAction, setPendingLedgerAction] = @@ -82,6 +84,11 @@ export const RevealStxAddressLedger: FC = () => { )} + {!isSupportedAppVersion && ( + + {appVersionErrorText} + + )} {pendingLedgerAction === 'pending' && address && ( diff --git a/app/modals/receive-stx/receive-stx-modal.tsx b/app/modals/receive-stx/receive-stx-modal.tsx index 5303a94a8..b6916c2ec 100644 --- a/app/modals/receive-stx/receive-stx-modal.tsx +++ b/app/modals/receive-stx/receive-stx-modal.tsx @@ -22,10 +22,11 @@ export const ReceiveStxModal: FC<{ isOpen: boolean }> = memo(({ isOpen }) => { return ( Receive STX - {whenWallet({ - ledger: , - software: , - })} + {isOpen && + whenWallet({ + ledger: , + software: , + })}