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: ,
+ })}