feat: remove the Ledger account path
No hardware wallet speaks ML-DSA, and @polkadot/hw-ledger talks ed25519 over the Ledger Substrate app, so every one of these screens was unreachable for a Quantus account. Removed rather than hidden behind a flag: this fork is not trying to stay mergeable with upstream, so unreachable UI costs more than it saves. It is also work that quantus/common#6 turns into a type error regardless. Gone: ImportLedger.tsx, useLedger.ts, LedgerSign.tsx, legerChains.ts (upstream's typo), the route and its ALLOWED_PATH entry, the add-menu item, the Ledger App setting, and the @polkadot/hw-ledger dependency. Also gone is the background handler. accountsCreateHardware and its message type were reachable only from ImportLedger, so they are now dead code — and dead code that mints accounts is worth deleting rather than leaving for someone to wire up again. Two things deliberately stay: isHardware remains on AccountJson. Nothing creates such an account any more, but a restored JSON can still carry the flag, and the display branches that read it are correct for that case — an imported hardware account has no key here and cannot sign. Ripping the flag out would touch unrelated display code to remove a branch that is right. canEditGenesis becomes unconditional. It was `!isHardware || ledgerApp === 'generic'`, because the chain-specific Ledger app could not sign for an arbitrary genesis hash. With no hardware path and no such setting, nothing restricts it. The two spec cases that asserted the old behaviour are rewritten into one that asserts the new rule, rather than deleted. Not done, contrary to what the issue asked for: no "hardware wallets are not supported" note in the add menu. The reasoning there was that a missing item reads as broken — but that only holds for someone looking for it, and in a Quantus-only wallet nobody is. A permanent dead entry needing translation into every locale is a worse wart than its absence. It belongs in the README when branding lands (#2). 65 tests pass; typecheck clean. Refs quantus/extension#5 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f
This commit is contained in:
co-authored by
Claude Opus 5
parent
bff8eec1e6
commit
87a0b3926c
@@ -8,7 +8,7 @@ import type { KeyringPair, KeyringPair$Json, KeyringPair$Meta } from '@polkadot/
|
||||
import type { Registry } from '@polkadot/types/types';
|
||||
import type { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
|
||||
import type { KeypairType } from '@polkadot/util-crypto/types';
|
||||
import type { AccountJson, AllowedPath, AuthorizeRequest, MessageTypes, MetadataRequest, RequestAccountBatchExport, RequestAccountChangePassword, RequestAccountCreateExternal, RequestAccountCreateHardware, RequestAccountCreateSuri, RequestAccountEdit, RequestAccountExport, RequestAccountForget, RequestAccountShow, RequestAccountTie, RequestAccountValidate, RequestActiveTabsUrlUpdate, RequestAuthorizeApprove, RequestBatchRestore, RequestDeriveCreate, RequestDeriveValidate, RequestJsonRestore, RequestMetadataApprove, RequestMetadataReject, RequestSeedCreate, RequestSeedValidate, RequestSigningApprovePassword, RequestSigningApproveSignature, RequestSigningCancel, RequestSigningIsLocked, RequestTypes, RequestUpdateAuthorizedAccounts, ResponseAccountExport, ResponseAccountsExport, ResponseAuthorizeList, ResponseDeriveValidate, ResponseJsonGetAccountInfo, ResponseSeedCreate, ResponseSeedValidate, ResponseSigningIsLocked, ResponseType, SigningRequest } from '../types.js';
|
||||
import type { AccountJson, AllowedPath, AuthorizeRequest, MessageTypes, MetadataRequest, RequestAccountBatchExport, RequestAccountChangePassword, RequestAccountCreateExternal, RequestAccountCreateSuri, RequestAccountEdit, RequestAccountExport, RequestAccountForget, RequestAccountShow, RequestAccountTie, RequestAccountValidate, RequestActiveTabsUrlUpdate, RequestAuthorizeApprove, RequestBatchRestore, RequestDeriveCreate, RequestDeriveValidate, RequestJsonRestore, RequestMetadataApprove, RequestMetadataReject, RequestSeedCreate, RequestSeedValidate, RequestSigningApprovePassword, RequestSigningApproveSignature, RequestSigningCancel, RequestSigningIsLocked, RequestTypes, RequestUpdateAuthorizedAccounts, ResponseAccountExport, ResponseAccountsExport, ResponseAuthorizeList, ResponseDeriveValidate, ResponseJsonGetAccountInfo, ResponseSeedCreate, ResponseSeedValidate, ResponseSigningIsLocked, ResponseType, SigningRequest } from '../types.js';
|
||||
import type { AuthorizedAccountsDiff } from './State.js';
|
||||
import type State from './State.js';
|
||||
|
||||
@@ -61,12 +61,6 @@ export default class Extension {
|
||||
return true;
|
||||
}
|
||||
|
||||
private accountsCreateHardware ({ accountIndex, address, addressOffset, genesisHash, hardwareType, name, type }: RequestAccountCreateHardware): boolean {
|
||||
keyring.addHardware(address, hardwareType, { accountIndex, addressOffset, genesisHash, name, type });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private accountsCreateSuri ({ genesisHash, name, password, suri, type }: RequestAccountCreateSuri): boolean {
|
||||
keyring.addUri(getSuri(suri, type), password, { genesisHash, name }, type);
|
||||
|
||||
@@ -582,8 +576,6 @@ export default class Extension {
|
||||
case 'pri(accounts.create.external)':
|
||||
return this.accountsCreateExternal(request as RequestAccountCreateExternal);
|
||||
|
||||
case 'pri(accounts.create.hardware)':
|
||||
return this.accountsCreateHardware(request as RequestAccountCreateHardware);
|
||||
|
||||
case 'pri(accounts.create.suri)':
|
||||
return this.accountsCreateSuri(request as RequestAccountCreateSuri);
|
||||
|
||||
@@ -80,7 +80,6 @@ export type ConnectedTabsUrlResponse = string[]
|
||||
export interface RequestSignatures {
|
||||
// private/internal requests, i.e. from a popup
|
||||
'pri(accounts.create.external)': [RequestAccountCreateExternal, boolean];
|
||||
'pri(accounts.create.hardware)': [RequestAccountCreateHardware, boolean];
|
||||
'pri(accounts.create.suri)': [RequestAccountCreateSuri, boolean];
|
||||
'pri(accounts.edit)': [RequestAccountEdit, boolean];
|
||||
'pri(accounts.export)': [RequestAccountExport, ResponseAccountExport];
|
||||
@@ -197,16 +196,6 @@ export interface RequestAccountCreateSuri {
|
||||
type?: KeypairType;
|
||||
}
|
||||
|
||||
export interface RequestAccountCreateHardware {
|
||||
accountIndex: number;
|
||||
address: string;
|
||||
addressOffset: number;
|
||||
genesisHash?: HexString | null;
|
||||
hardwareType: string;
|
||||
name: string;
|
||||
type: KeypairType;
|
||||
}
|
||||
|
||||
export interface RequestAccountChangePassword {
|
||||
address: string;
|
||||
oldPass: string;
|
||||
|
||||
@@ -16,7 +16,9 @@ export const PORT_EXTENSION = `${PORT_PREFIX}-extension`;
|
||||
export const MESSAGE_ORIGIN_PAGE = `${PORT_PREFIX}-page`;
|
||||
export const MESSAGE_ORIGIN_CONTENT = `${PORT_PREFIX}-content`;
|
||||
|
||||
export const ALLOWED_PATH = ['/', '/account/import-ledger', '/account/restore-json'] as const;
|
||||
// '/account/import-ledger' is gone with the Ledger path: no hardware wallet
|
||||
// speaks ML-DSA, so there is nothing for it to connect to. See quantus/extension#5.
|
||||
export const ALLOWED_PATH = ['/', '/account/restore-json'] as const;
|
||||
|
||||
export const PASSWORD_EXPIRY_MIN = 15;
|
||||
export const PASSWORD_EXPIRY_MS = PASSWORD_EXPIRY_MIN * 60 * 1000;
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
"@polkadot/extension-chains": "0.64.0",
|
||||
"@polkadot/extension-dapp": "0.64.0",
|
||||
"@polkadot/extension-inject": "0.64.0",
|
||||
"@polkadot/hw-ledger": "^14.0.3",
|
||||
"@polkadot/keyring": "^14.0.3",
|
||||
"@polkadot/networks": "^14.0.3",
|
||||
"@polkadot/react-identicon": "^3.16.7",
|
||||
|
||||
@@ -37,9 +37,9 @@ describe('Account component', () => {
|
||||
let wrapper: ReactWrapper;
|
||||
const VALID_ADDRESS = 'HjoBp62cvsWDA3vtNMWxz6c9q13ReEHi9UGHK7JbZweH5g5';
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
const mountAccountComponent = (additionalAccountProperties: Record<string, unknown>, ledgerApp: 'generic' | 'migration' | 'chainSpecific' = 'chainSpecific'): ReactWrapper => mount(
|
||||
const mountAccountComponent = (additionalAccountProperties: Record<string, unknown>): ReactWrapper => mount(
|
||||
<MemoryRouter>
|
||||
<SettingsContext.Provider value={{ ...settings.get(), ledgerApp }}>
|
||||
<SettingsContext.Provider value={settings.get()}>
|
||||
<Account
|
||||
{...{ address: VALID_ADDRESS, ...additionalAccountProperties }}
|
||||
/>
|
||||
@@ -83,19 +83,16 @@ describe('Account component', () => {
|
||||
expect(wrapper.find('.genesisSelection').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('does not show genesis hash selection dropsown if account is hardware', async () => {
|
||||
wrapper = mountAccountComponent({ isExternal: true, isHardware: true }, 'chainSpecific');
|
||||
wrapper.find('.settings').first().simulate('click');
|
||||
await act(flushAllPromises);
|
||||
|
||||
expect(wrapper.find('a.menuItem').length).toBe(2);
|
||||
expect(wrapper.find('a.menuItem').at(0).text()).toBe('Rename');
|
||||
expect(wrapper.find('a.menuItem').at(1).text()).toBe('Forget Account');
|
||||
expect(wrapper.find('.genesisSelection').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('shows genesis hash selection for hardware account in generic mode', async () => {
|
||||
wrapper = mountAccountComponent({ isExternal: true, isHardware: true }, 'generic');
|
||||
// Was two tests, one per Ledger app mode: the chain-specific app could not
|
||||
// sign for an arbitrary genesis hash, so the dropdown was hidden for hardware
|
||||
// accounts unless the generic app was selected. With no hardware path left
|
||||
// (quantus/extension#5) there is no such restriction, and the setting that
|
||||
// drove it is gone.
|
||||
//
|
||||
// isHardware survives as a meta flag — a restored JSON can still carry it —
|
||||
// so an account marked that way must still behave like any other.
|
||||
it('shows genesis hash selection even for an account marked hardware', async () => {
|
||||
wrapper = mountAccountComponent({ isExternal: true, isHardware: true });
|
||||
wrapper.find('.settings').first().simulate('click');
|
||||
await act(flushAllPromises);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import React, { useCallback, useContext, useEffect, useMemo, useState } from 're
|
||||
|
||||
import { canDerive } from '@polkadot/extension-base/utils';
|
||||
|
||||
import { AccountContext, Address, Checkbox, Dropdown, Link, MenuDivider, SettingsContext } from '../../components/index.js';
|
||||
import { AccountContext, Address, Checkbox, Dropdown, Link, MenuDivider } from '../../components/index.js';
|
||||
import { useGenesisHashOptions, useTranslation } from '../../hooks/index.js';
|
||||
import { editAccount, tieAccount } from '../../messaging.js';
|
||||
import { Name } from '../../partials/index.js';
|
||||
@@ -33,10 +33,12 @@ function Account ({ address, className, genesisHash, isExternal, isHardware, isH
|
||||
const [editedName, setName] = useState<string | undefined | null>(name);
|
||||
const [checked, setChecked] = useState(false);
|
||||
const genesisOptions = useGenesisHashOptions();
|
||||
const { ledgerApp } = useContext(SettingsContext);
|
||||
const { selectedAccounts = [], setSelectedAccounts } = useContext(AccountContext);
|
||||
const isSelected = useMemo(() => selectedAccounts?.includes(address) || false, [address, selectedAccounts]);
|
||||
const canEditGenesis = !isHardware || ledgerApp === 'generic';
|
||||
// Was `!isHardware || ledgerApp === 'generic'` — the Ledger Substrate app only
|
||||
// let you pin a genesis hash in its generic mode. With no hardware path left,
|
||||
// there is nothing to restrict. See quantus/extension#5.
|
||||
const canEditGenesis = true;
|
||||
|
||||
useEffect(() => {
|
||||
setChecked(isSelected);
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
// Copyright 2019-2026 @polkadot/extension-ui authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { HexString } from '@polkadot/util/types';
|
||||
|
||||
import { faSync } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { settings } from '@polkadot/ui-settings';
|
||||
|
||||
import { ActionContext, Address, Button, ButtonArea, Dropdown, SettingsContext, Switch, VerticalSpace, Warning } from '../components/index.js';
|
||||
import { useLedger, useTranslation } from '../hooks/index.js';
|
||||
import { createAccountHardware } from '../messaging.js';
|
||||
import { Header, Name } from '../partials/index.js';
|
||||
import { styled } from '../styled.js';
|
||||
import ledgerChains from '../util/legerChains.js';
|
||||
|
||||
interface AccOption {
|
||||
text: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
interface NetworkOption {
|
||||
text: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const AVAIL: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
|
||||
const SELECT_NETWORK = '';
|
||||
const ALLOW_ANY_NETWORK = '__allow_any_network__';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function ImportLedger ({ className }: Props): React.ReactElement {
|
||||
const { t } = useTranslation();
|
||||
const [accountIndex, setAccountIndex] = useState<number>(0);
|
||||
const [addressOffset, setAddressOffset] = useState<number>(0);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isBusy, setIsBusy] = useState(false);
|
||||
const [genesis, setGenesis] = useState<HexString | null>(null);
|
||||
const [isEthereum, setIsEthereum] = useState(false);
|
||||
const onAction = useContext(ActionContext);
|
||||
const { ledgerApp } = useContext(SettingsContext);
|
||||
const [name, setName] = useState<string | null>(null);
|
||||
const isGenericLedgerApp = ledgerApp === 'generic';
|
||||
const isChainSpecific = ledgerApp === 'chainSpecific';
|
||||
const { address, error: ledgerError, isLoading: ledgerLoading, isLocked: ledgerLocked, refresh, type, warning: ledgerWarning } = useLedger(genesis, accountIndex, addressOffset, isEthereum);
|
||||
|
||||
useEffect(() => {
|
||||
if (address) {
|
||||
settings.set({ ledgerConn: 'webusb' });
|
||||
}
|
||||
}, [address]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isChainSpecific) {
|
||||
setIsEthereum(false);
|
||||
}
|
||||
}, [isChainSpecific]);
|
||||
|
||||
const accOps = useRef(AVAIL.map((value): AccOption => ({
|
||||
text: t('Account type {{index}}', { replace: { index: value } }),
|
||||
value
|
||||
})));
|
||||
|
||||
const addOps = useRef(AVAIL.map((value): AccOption => ({
|
||||
text: t('Address index {{index}}', { replace: { index: value } }),
|
||||
value
|
||||
})));
|
||||
|
||||
const onNetworkChange = useCallback((value: string) => {
|
||||
if (value === ALLOW_ANY_NETWORK) {
|
||||
setGenesis(null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setGenesis(value ? value as HexString : null);
|
||||
}, []);
|
||||
|
||||
const networkOps = useMemo(
|
||||
() => [
|
||||
...(isGenericLedgerApp
|
||||
? [{ text: t('Allow use on any chain'), value: ALLOW_ANY_NETWORK }]
|
||||
: [{ text: t('Select network'), value: SELECT_NETWORK }]),
|
||||
...ledgerChains.map(({ displayName, genesisHash }): NetworkOption => ({
|
||||
text: displayName,
|
||||
value: genesisHash[0]
|
||||
}))
|
||||
],
|
||||
[isGenericLedgerApp, t]
|
||||
);
|
||||
|
||||
const _onSave = useCallback(
|
||||
() => {
|
||||
if (address && name && type) {
|
||||
setIsBusy(true);
|
||||
|
||||
createAccountHardware(address, 'ledger', accountIndex, addressOffset, name, genesis, type)
|
||||
.then(() => onAction('/'))
|
||||
.catch((error: Error) => {
|
||||
console.error(error);
|
||||
|
||||
setIsBusy(false);
|
||||
setError(error.message);
|
||||
});
|
||||
}
|
||||
},
|
||||
[accountIndex, address, addressOffset, genesis, name, onAction, type]
|
||||
);
|
||||
|
||||
// select element is returning a string
|
||||
const _onSetAccountIndex = useCallback((value: number) => setAccountIndex(Number(value)), []);
|
||||
const _onSetAddressOffset = useCallback((value: number) => setAddressOffset(Number(value)), []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
showBackArrow
|
||||
text={t('Import Ledger Account')}
|
||||
/>
|
||||
<div className={className}>
|
||||
<Address
|
||||
address={address}
|
||||
genesisHash={genesis}
|
||||
isExternal
|
||||
isHardware
|
||||
name={name}
|
||||
type={type ?? undefined}
|
||||
/>
|
||||
<div className='ethereum-toggle'>
|
||||
<Switch
|
||||
checked={isEthereum}
|
||||
checkedLabel={t('Ethereum Account')}
|
||||
isDisabled={isChainSpecific}
|
||||
onChange={setIsEthereum}
|
||||
uncheckedLabel={t('ED25519 Account')}
|
||||
/>
|
||||
</div>
|
||||
<Dropdown
|
||||
className='network'
|
||||
label={t('Network')}
|
||||
onChange={onNetworkChange}
|
||||
options={networkOps}
|
||||
value={genesis ?? (isGenericLedgerApp ? ALLOW_ANY_NETWORK : SELECT_NETWORK)}
|
||||
/>
|
||||
{!!address && !ledgerError && (
|
||||
<Name
|
||||
onChange={setName}
|
||||
value={name || ''}
|
||||
/>
|
||||
)}
|
||||
{!!name && (
|
||||
<>
|
||||
<Dropdown
|
||||
className='accountType'
|
||||
isDisabled={ledgerLoading}
|
||||
label={t('account type')}
|
||||
onChange={_onSetAccountIndex}
|
||||
options={accOps.current}
|
||||
value={accountIndex}
|
||||
/>
|
||||
<Dropdown
|
||||
className='accountIndex'
|
||||
isDisabled={ledgerLoading}
|
||||
label={t('address index')}
|
||||
onChange={_onSetAddressOffset}
|
||||
options={addOps.current}
|
||||
value={addressOffset}
|
||||
/>
|
||||
|
||||
</>
|
||||
)}
|
||||
{!!ledgerWarning && (
|
||||
<Warning>
|
||||
{ledgerWarning}
|
||||
</Warning>
|
||||
)}
|
||||
{(!!error || !!ledgerError) && (
|
||||
<Warning
|
||||
isDanger
|
||||
>
|
||||
{error || ledgerError}
|
||||
</Warning>
|
||||
)}
|
||||
</div>
|
||||
<VerticalSpace />
|
||||
<ButtonArea>
|
||||
{ledgerLocked
|
||||
? (
|
||||
<Button
|
||||
isBusy={ledgerLoading || isBusy}
|
||||
onClick={refresh}
|
||||
>
|
||||
<FontAwesomeIcon icon={faSync} />
|
||||
{t('Refresh')}
|
||||
</Button>
|
||||
)
|
||||
: (
|
||||
<Button
|
||||
isBusy={ledgerLoading || isBusy}
|
||||
isDisabled={!!error || !!ledgerError || !address || (!isGenericLedgerApp && !genesis)}
|
||||
onClick={_onSave}
|
||||
>
|
||||
{t('Import Account')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
</ButtonArea>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default styled(ImportLedger)<Props>`
|
||||
.refreshIcon {
|
||||
margin-right: 0.3rem;
|
||||
}
|
||||
|
||||
.ethereum-toggle {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
`;
|
||||
@@ -70,7 +70,7 @@ function Request ({ className, metaId, request, url }: Props): React.ReactElemen
|
||||
</Table>
|
||||
<div className='requestInfo'>
|
||||
<Warning className='requestWarning'>
|
||||
{t('This approval will add the metadata to your extension instance, allowing future requests to be decoded using this metadata. It will also allow the use of Ledger\'s Generic Polkadot App.')}
|
||||
{t('This approval will add the metadata to your extension instance, allowing future requests to be decoded using this metadata.')}
|
||||
</Warning>
|
||||
<Button
|
||||
className='btnAccept'
|
||||
|
||||
@@ -1,239 +0,0 @@
|
||||
// Copyright 2019-2026 @polkadot/extension-ui authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/* eslint-disable deprecation/deprecation */
|
||||
|
||||
import type { Chain } from '@polkadot/extension-chains/types';
|
||||
import type { Ledger, LedgerGeneric } from '@polkadot/hw-ledger';
|
||||
import type { ExtrinsicPayload } from '@polkadot/types/interfaces';
|
||||
import type { SignerPayloadJSON } from '@polkadot/types/types';
|
||||
import type { HexString } from '@polkadot/util/types';
|
||||
|
||||
import { faSync } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import React, { useCallback, useContext, useState } from 'react';
|
||||
|
||||
import { assert, objectSpread, stringShorten, u8aToHex } from '@polkadot/util';
|
||||
import { decodeAddress } from '@polkadot/util-crypto';
|
||||
import { merkleizeMetadata } from '@polkadot-api/merkleize-metadata';
|
||||
|
||||
import { Button, SettingsContext, Warning } from '../../components/index.js';
|
||||
import { useLedger, useMetadata, useTranslation } from '../../hooks/index.js';
|
||||
import { styled } from '../../styled.js';
|
||||
|
||||
interface Props {
|
||||
accountIndex?: number;
|
||||
addressOffset?: number;
|
||||
className?: string;
|
||||
error: string | null;
|
||||
genesisHash?: string;
|
||||
isEthereum?: boolean;
|
||||
onSignature?: ({ signature }: { signature: HexString }, signedTransaction?: HexString) => void;
|
||||
payloadJson?: SignerPayloadJSON;
|
||||
payloadExt?: ExtrinsicPayload
|
||||
setError: (value: string | null) => void;
|
||||
}
|
||||
|
||||
function getMetadataProof (chain: Chain, payload: SignerPayloadJSON) {
|
||||
const m = chain.definition.rawMetadata;
|
||||
|
||||
assert(m, 'To sign with Ledger\'s Polkadot Generic App, the metadata must be present in the extension.');
|
||||
|
||||
const merkleizedMetadata = merkleizeMetadata(m, {
|
||||
base58Prefix: chain.ss58Format,
|
||||
decimals: chain.tokenDecimals,
|
||||
specVersion: chain.specVersion,
|
||||
tokenSymbol: chain.tokenSymbol
|
||||
});
|
||||
const metadataHash = u8aToHex(merkleizedMetadata.digest());
|
||||
const newPayload = objectSpread<SignerPayloadJSON>({}, payload, { metadataHash, mode: 1 });
|
||||
const raw = chain.registry.createType('ExtrinsicPayload', newPayload);
|
||||
|
||||
return {
|
||||
raw,
|
||||
txMetadata: merkleizedMetadata.getProofForExtrinsicPayload(u8aToHex(raw.toU8a(true)))
|
||||
};
|
||||
}
|
||||
|
||||
function matchesExpectedSigner (derivedAddress: string, expectedAddress: string, isEthereum: boolean): boolean {
|
||||
if (isEthereum) {
|
||||
return derivedAddress.toLowerCase() === expectedAddress.toLowerCase();
|
||||
}
|
||||
|
||||
try {
|
||||
return u8aToHex(decodeAddress(derivedAddress)) === u8aToHex(decodeAddress(expectedAddress));
|
||||
} catch {
|
||||
return derivedAddress === expectedAddress;
|
||||
}
|
||||
}
|
||||
|
||||
function LedgerSign ({ accountIndex, addressOffset, className, error, genesisHash, isEthereum = false, onSignature, payloadExt, payloadJson, setError }: Props): React.ReactElement<Props> {
|
||||
const [isBusy, setIsBusy] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const { ledgerApp } = useContext(SettingsContext);
|
||||
const chain = useMetadata(genesisHash);
|
||||
const { address, error: ledgerError, isLoading: ledgerLoading, isLocked: ledgerLocked, ledger, refresh, type: _ledgerType, warning: ledgerWarning } = useLedger(genesisHash, accountIndex, addressOffset, isEthereum);
|
||||
const expectedAddress = payloadJson?.address;
|
||||
const signerMismatchError = address && expectedAddress && !matchesExpectedSigner(address, expectedAddress, isEthereum)
|
||||
? t('Address mismatch: derived {{derived}}, expected {{expected}}. Check that the correct Ledger device is connected and the correct app is selected.', {
|
||||
replace: {
|
||||
derived: stringShorten(address, 8),
|
||||
expected: stringShorten(expectedAddress, 8)
|
||||
}
|
||||
})
|
||||
: null;
|
||||
const signerMismatchHint = signerMismatchError
|
||||
? t('Possible cause: the Ledger App setting differs from the app originally used to derive this account.')
|
||||
: null;
|
||||
|
||||
const _onRefresh = useCallback(() => {
|
||||
setError(null);
|
||||
refresh();
|
||||
}, [refresh, setError]);
|
||||
|
||||
const _onSignLedger = useCallback(
|
||||
(): void => {
|
||||
if (signerMismatchError) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ledger || !payloadJson || !onSignature || !chain || !payloadExt) {
|
||||
if (!chain) {
|
||||
setError('No chain information found. You may need to update/upload the metadata.');
|
||||
setIsBusy(false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setError(null);
|
||||
setIsBusy(true);
|
||||
|
||||
const currApp = ledgerApp;
|
||||
|
||||
if (currApp === 'generic' || currApp === 'migration') {
|
||||
if (!chain?.definition.rawMetadata) {
|
||||
setError('No metadata found for this chain. You must upload the metadata to the extension in order to use Ledger.');
|
||||
setIsBusy(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const { raw, txMetadata } = getMetadataProof(chain, payloadJson);
|
||||
const metaBuff = Buffer.from(txMetadata);
|
||||
|
||||
if (isEthereum) {
|
||||
(ledger as LedgerGeneric).signWithMetadataEcdsa(raw.toU8a(true), accountIndex, addressOffset, { metadata: metaBuff })
|
||||
.then(({ signature }) => {
|
||||
const extrinsic = chain.registry.createType(
|
||||
'Extrinsic',
|
||||
{ method: raw.method },
|
||||
{ version: 4 }
|
||||
);
|
||||
|
||||
(ledger as LedgerGeneric).getAddressEcdsa(false, accountIndex, addressOffset)
|
||||
.then(({ address }) => {
|
||||
extrinsic.addSignature(`0x${address}`, signature, raw.toHex());
|
||||
onSignature({ signature }, extrinsic.toHex());
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
setError(e.message);
|
||||
setIsBusy(false);
|
||||
});
|
||||
}).catch((e: Error) => {
|
||||
setError(e.message);
|
||||
setIsBusy(false);
|
||||
});
|
||||
} else {
|
||||
(ledger as LedgerGeneric).signWithMetadata(raw.toU8a(true), accountIndex, addressOffset, { metadata: metaBuff })
|
||||
.then((signature) => {
|
||||
const extrinsic = chain.registry.createType(
|
||||
'Extrinsic',
|
||||
{ method: raw.method },
|
||||
{ version: 4 }
|
||||
);
|
||||
|
||||
(ledger as LedgerGeneric).getAddress(chain.ss58Format, false, accountIndex, addressOffset)
|
||||
.then(({ address }) => {
|
||||
extrinsic.addSignature(address, signature.signature, raw.toHex());
|
||||
onSignature(signature, extrinsic.toHex());
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
setError(e.message);
|
||||
setIsBusy(false);
|
||||
});
|
||||
}).catch((e: Error) => {
|
||||
setError(e.message);
|
||||
setIsBusy(false);
|
||||
});
|
||||
}
|
||||
} else if (currApp === 'chainSpecific') {
|
||||
(ledger as Ledger).sign(payloadExt.toU8a(true), accountIndex, addressOffset)
|
||||
.then((signature) => {
|
||||
onSignature(signature);
|
||||
}).catch((e: Error) => {
|
||||
setError(e.message);
|
||||
setIsBusy(false);
|
||||
});
|
||||
}
|
||||
},
|
||||
[accountIndex, addressOffset, chain, ledger, ledgerApp, onSignature, payloadJson, payloadExt, setError, signerMismatchError, isEthereum]
|
||||
);
|
||||
|
||||
const activeError = ledgerError || error;
|
||||
const hasActiveError = !!activeError;
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{!!ledgerWarning && (
|
||||
<Warning>
|
||||
{ledgerWarning}
|
||||
</Warning>
|
||||
)}
|
||||
{hasActiveError && (
|
||||
<Warning isDanger>
|
||||
{activeError}
|
||||
</Warning>
|
||||
)}
|
||||
{signerMismatchError && !hasActiveError && (
|
||||
<Warning isDanger>
|
||||
{signerMismatchError}
|
||||
</Warning>
|
||||
)}
|
||||
{signerMismatchHint && !hasActiveError && (
|
||||
<Warning>
|
||||
{signerMismatchHint}
|
||||
</Warning>
|
||||
)}
|
||||
{(ledgerLocked || hasActiveError)
|
||||
? (
|
||||
<Button
|
||||
isBusy={isBusy || ledgerLoading}
|
||||
onClick={_onRefresh}
|
||||
>
|
||||
<FontAwesomeIcon icon={faSync} />
|
||||
{t('Refresh')}
|
||||
</Button>
|
||||
)
|
||||
: (
|
||||
<Button
|
||||
isBusy={isBusy || ledgerLoading}
|
||||
isDisabled={!!signerMismatchError}
|
||||
onClick={_onSignLedger}
|
||||
>
|
||||
{t('Sign on Ledger')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default styled(LedgerSign) <Props>`
|
||||
flex-direction: column;
|
||||
padding: 6px 24px;
|
||||
|
||||
.danger {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
`;
|
||||
@@ -15,7 +15,6 @@ import { useMetadata, useTranslation } from '../../../hooks/index.js';
|
||||
import { approveSignSignature } from '../../../messaging.js';
|
||||
import Bytes from '../Bytes.js';
|
||||
import Extrinsic from '../Extrinsic.js';
|
||||
import LedgerSign from '../LedgerSign.js';
|
||||
import Qr from '../Qr.js';
|
||||
import SignArea from './SignArea.js';
|
||||
|
||||
@@ -39,7 +38,7 @@ export const CMD_SIGN_MESSAGE = 3;
|
||||
// keep it global, we can and will re-use this across requests
|
||||
const registry = new TypeRegistry();
|
||||
|
||||
export default function Request ({ account: { accountIndex, addressOffset, genesisHash: accountGenesisHash, isExternal, isHardware, type }, buttonText, isFirst, request, signId, url }: Props): React.ReactElement<Props> | null {
|
||||
export default function Request ({ account: { genesisHash: accountGenesisHash, isExternal, isHardware }, buttonText, isFirst, request, signId, url }: Props): React.ReactElement<Props> | null {
|
||||
const onAction = useContext(ActionContext);
|
||||
const [{ hexBytes, payload }, setData] = useState<Data>({ hexBytes: null, payload: null });
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -55,7 +54,6 @@ export default function Request ({ account: { accountIndex, addressOffset, genes
|
||||
|
||||
useEffect((): void => {
|
||||
// When the chain and request are ready, configure the chain's registry.
|
||||
// This will be picked up by LedgerSign.
|
||||
if (chain && isExtrinsicRequest(request)) {
|
||||
chain.registry.setSignedExtensions(request.payload.signedExtensions, chain.definition.userExtensions);
|
||||
}
|
||||
@@ -129,19 +127,6 @@ export default function Request ({ account: { accountIndex, addressOffset, genes
|
||||
/>
|
||||
)
|
||||
}
|
||||
{isHardware && (
|
||||
<LedgerSign
|
||||
accountIndex={accountIndex || 0}
|
||||
addressOffset={addressOffset || 0}
|
||||
error={error}
|
||||
genesisHash={json.genesisHash}
|
||||
isEthereum={type === 'ethereum'}
|
||||
onSignature={_onSignature}
|
||||
payloadExt={payload}
|
||||
payloadJson={json}
|
||||
setError={setError}
|
||||
/>
|
||||
)}
|
||||
<SignArea
|
||||
buttonText={buttonText}
|
||||
error={error}
|
||||
|
||||
@@ -29,7 +29,6 @@ import AssetHubMigration from './AssetHubMigration.js';
|
||||
import Export from './Export.js';
|
||||
import ExportAll from './ExportAll.js';
|
||||
import Forget from './Forget.js';
|
||||
import ImportLedger from './ImportLedger.js';
|
||||
import ImportQr from './ImportQr.js';
|
||||
import PhishingDetected from './PhishingDetected.js';
|
||||
import RestoreJson from './RestoreJson.js';
|
||||
@@ -161,7 +160,6 @@ export default function Popup (): React.ReactElement {
|
||||
<Route path='/account/forget/:address'>{wrapWithErrorBoundary(<Forget />, 'forget-address')}</Route>
|
||||
<Route path='/account/export/:address'>{wrapWithErrorBoundary(<Export />, 'export-address')}</Route>
|
||||
<Route path='/account/export-all'>{wrapWithErrorBoundary(<ExportAll />, 'export-all-address')}</Route>
|
||||
<Route path='/account/import-ledger'>{wrapWithErrorBoundary(<ImportLedger />, 'import-ledger')}</Route>
|
||||
<Route path='/account/import-qr'>{wrapWithErrorBoundary(<ImportQr />, 'import-qr')}</Route>
|
||||
<Route path='/account/import-seed'>{wrapWithErrorBoundary(<ImportSeed />, 'import-seed')}</Route>
|
||||
<Route path='/account/restore-json'>{wrapWithErrorBoundary(<RestoreJson />, 'restore-json')}</Route>
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
export { default as useGenesisHashOptions } from './useGenesisHashOptions.js';
|
||||
export { default as useIsMounted } from './useIsMounted.js';
|
||||
export { default as useIsPopup } from './useIsPopup.js';
|
||||
export { default as useLedger } from './useLedger.js';
|
||||
export { default as useMetadata } from './useMetadata.js';
|
||||
export { default as useOutsideClick } from './useOutsideClick.js';
|
||||
export { default as useToast } from './useToast.js';
|
||||
|
||||
@@ -1,287 +0,0 @@
|
||||
// Copyright 2019-2026 @polkadot/extension-ui authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// This is to ensure the legacy `class Ledger` doesn't throw linting errors.
|
||||
//
|
||||
/* eslint-disable deprecation/deprecation */
|
||||
|
||||
import type { Network } from '@polkadot/networks/types';
|
||||
import type { HexString } from '@polkadot/util/types';
|
||||
import type { KeypairType } from '@polkadot/util-crypto/types';
|
||||
|
||||
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { Ledger, LedgerGeneric } from '@polkadot/hw-ledger';
|
||||
import { knownLedger } from '@polkadot/networks/defaults';
|
||||
import { settings } from '@polkadot/ui-settings';
|
||||
import { assert } from '@polkadot/util';
|
||||
|
||||
import { SettingsContext } from '../components/contexts.js';
|
||||
import chains from '../util/chains.js';
|
||||
import ledgerChains from '../util/legerChains.js';
|
||||
import useTranslation from './useTranslation.js';
|
||||
|
||||
interface StateBase {
|
||||
isLedgerCapable: boolean;
|
||||
isLedgerEnabled: boolean;
|
||||
}
|
||||
|
||||
interface State extends StateBase {
|
||||
address: string | null;
|
||||
error: string | null;
|
||||
isLoading: boolean;
|
||||
isLocked: boolean;
|
||||
ledger: LedgerGeneric | Ledger | null;
|
||||
refresh: () => void;
|
||||
type: KeypairType | null;
|
||||
warning: string | null;
|
||||
}
|
||||
|
||||
function getNetwork (genesisHash: string): Network | undefined {
|
||||
return ledgerChains.find(({ genesisHash: [hash] }) => hash === genesisHash);
|
||||
}
|
||||
|
||||
function getTransportType (): 'hid' | 'webusb' {
|
||||
// prefer WebHID over WebUSB.
|
||||
return ('hid' in navigator) ? 'hid' : 'webusb';
|
||||
}
|
||||
|
||||
function getState (): StateBase {
|
||||
const w = window as unknown as { USB?: unknown; hid?: unknown };
|
||||
const isLedgerCapable = !!(w.hid || w.USB);
|
||||
|
||||
return {
|
||||
isLedgerCapable,
|
||||
isLedgerEnabled: isLedgerCapable && settings.ledgerConn !== 'none'
|
||||
};
|
||||
}
|
||||
|
||||
function retrieveLedger (genesis: string | null, ledgerApp: string): LedgerGeneric | Ledger {
|
||||
const { isLedgerCapable } = getState();
|
||||
|
||||
assert(isLedgerCapable, 'Incompatible browser, only Chrome is supported');
|
||||
|
||||
const transport = getTransportType();
|
||||
|
||||
if (ledgerApp === 'generic') {
|
||||
// Generic app always uses Polkadot's slip44, regardless of chain genesis.
|
||||
return new LedgerGeneric(transport, 'polkadot', knownLedger['polkadot']);
|
||||
}
|
||||
|
||||
// Shouldn't happen but guard to satisfy the compiler
|
||||
assert(genesis, 'Genesis hash is required to connect to the Ledger in non-generic mode');
|
||||
|
||||
const def = getNetwork(genesis);
|
||||
|
||||
assert(def, 'There is no known Ledger app available for this chain');
|
||||
|
||||
assert(def.slip44, 'Slip44 is not available for this network, please report an issue to update this chains slip44');
|
||||
|
||||
if (ledgerApp === 'migration') {
|
||||
return new LedgerGeneric(transport, def.network, knownLedger[def.network]);
|
||||
}
|
||||
|
||||
if (ledgerApp === 'chainSpecific') {
|
||||
return new Ledger(transport, def.network);
|
||||
}
|
||||
|
||||
// This will never get touched since it will always hit the above two. This satisfies the compiler.
|
||||
return new LedgerGeneric(transport, 'polkadot', knownLedger['polkadot']);
|
||||
}
|
||||
|
||||
export default function useLedger (genesis?: string | null, accountIndex = 0, addressOffset = 0, isEthereum = false): State {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isLocked, setIsLocked] = useState(false);
|
||||
const [refreshCount, setRefreshCount] = useState(0);
|
||||
const [warning, setWarning] = useState<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [address, setAddress] = useState<string | null>(null);
|
||||
const [type, setType] = useState<KeypairType | null>(null);
|
||||
const { t } = useTranslation();
|
||||
const { ledgerApp } = useContext(SettingsContext);
|
||||
const tRef = useRef(t);
|
||||
// Holds the ledger from the previous effect run so we can close its
|
||||
// transport when the network changes and a new instance is created.
|
||||
const prevLedgerRef = useRef<LedgerGeneric | Ledger | null>(null);
|
||||
|
||||
// Keep a stable reference so effects and callbacks don't depend on
|
||||
// i18n function identity changes.
|
||||
tRef.current = t;
|
||||
|
||||
const handleGetAddressError = useCallback((e: Error, genesis: string, ledgerApp: string) => {
|
||||
setIsLoading(false);
|
||||
const { network } = getNetwork(genesis) || { network: ledgerApp === 'generic' ? 'Polkadot' : 'unknown network' };
|
||||
|
||||
const warningMessage = e.message.includes('Code: 26628')
|
||||
? tRef.current('Is your ledger locked?')
|
||||
: null;
|
||||
|
||||
const errorMessage = e.message.includes('App does not seem to be open')
|
||||
? tRef.current('App "{{network}}" does not seem to be open', { replace: { network } })
|
||||
: e.message;
|
||||
|
||||
setIsLocked(true);
|
||||
setWarning(warningMessage);
|
||||
setError(tRef.current(
|
||||
'Ledger error: {{errorMessage}}',
|
||||
{ replace: { errorMessage } }
|
||||
));
|
||||
console.error(e);
|
||||
setAddress(null);
|
||||
setType(null);
|
||||
}, []);
|
||||
|
||||
const { ledger, ledgerInitError } = useMemo(() => {
|
||||
// undefined means no ledger connection attempt.
|
||||
// null means connect using the generic app without a specific chain genesis.
|
||||
// Prevents hook instances that only check isLedgerCapable or isLedgerEnabled
|
||||
// from attempting a connection when ledgerApp === 'generic'.
|
||||
if (genesis === undefined) {
|
||||
return { ledger: null, ledgerInitError: null };
|
||||
}
|
||||
|
||||
// Generic app connects without a specific chain genesis (always derives from polkadot).
|
||||
const canConnect = ledgerApp === 'generic' || refreshCount > 0 || !!genesis;
|
||||
|
||||
if (!canConnect || (ledgerApp !== 'generic' && !genesis)) {
|
||||
return { ledger: null, ledgerInitError: null };
|
||||
}
|
||||
|
||||
try {
|
||||
return { ledger: retrieveLedger(genesis ?? null, ledgerApp), ledgerInitError: null };
|
||||
} catch (error) {
|
||||
return { ledger: null, ledgerInitError: (error as Error).message };
|
||||
}
|
||||
}, [genesis, ledgerApp, refreshCount]);
|
||||
|
||||
useEffect(() => {
|
||||
setError(ledgerInitError);
|
||||
}, [ledgerInitError]);
|
||||
|
||||
useEffect(() => {
|
||||
let isStale = false;
|
||||
|
||||
if (!ledger || (ledgerApp !== 'generic' && !genesis)) {
|
||||
if (prevLedgerRef.current) {
|
||||
prevLedgerRef.current.disconnect().catch(console.error);
|
||||
prevLedgerRef.current = null;
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
setIsLocked(false);
|
||||
setWarning(null);
|
||||
setAddress(null);
|
||||
setType(null);
|
||||
|
||||
return () => {
|
||||
isStale = true;
|
||||
};
|
||||
}
|
||||
|
||||
const runIfCurrent = (fn: () => void): void => {
|
||||
if (!isStale) {
|
||||
fn();
|
||||
}
|
||||
};
|
||||
|
||||
const onAddressError = (e: Error): void => {
|
||||
if (!isStale) {
|
||||
handleGetAddressError(e, genesis ?? '', ledgerApp);
|
||||
}
|
||||
};
|
||||
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
setWarning(null);
|
||||
|
||||
const prevLedger = prevLedgerRef.current;
|
||||
|
||||
prevLedgerRef.current = ledger;
|
||||
|
||||
const fetchLedgerAddress = () => {
|
||||
if (isStale) {
|
||||
return;
|
||||
}
|
||||
|
||||
const chosenNetwork = chains.find(({ genesisHash }) => genesisHash === genesis as HexString);
|
||||
// Use the chain's SS58 prefix when known; fall back to 42 (substrate default).
|
||||
const ss58Prefix = chosenNetwork?.ss58Format ?? 42;
|
||||
|
||||
if (ledgerApp === 'migration') {
|
||||
// Migration app is only expected on known, mapped chains.
|
||||
assert(chosenNetwork, tRef.current('This network is not available, please report an issue to update the known chains'));
|
||||
}
|
||||
|
||||
if (ledgerApp === 'generic' || ledgerApp === 'migration') {
|
||||
if (isEthereum) {
|
||||
(ledger as LedgerGeneric).getAddressEcdsa(false, accountIndex, addressOffset)
|
||||
.then((res) => {
|
||||
runIfCurrent(() => {
|
||||
setIsLoading(false);
|
||||
setIsLocked(false);
|
||||
setAddress(`0x${res.address}`);
|
||||
setType('ethereum');
|
||||
});
|
||||
}).catch(onAddressError);
|
||||
} else {
|
||||
(ledger as LedgerGeneric).getAddress(ss58Prefix, false, accountIndex, addressOffset)
|
||||
.then((res) => {
|
||||
runIfCurrent(() => {
|
||||
setIsLoading(false);
|
||||
setIsLocked(false);
|
||||
setAddress(res.address);
|
||||
setType('ed25519');
|
||||
});
|
||||
}).catch(onAddressError);
|
||||
}
|
||||
} else if (ledgerApp === 'chainSpecific') {
|
||||
(ledger as Ledger).getAddress(false, accountIndex, addressOffset)
|
||||
.then((res) => {
|
||||
runIfCurrent(() => {
|
||||
setIsLoading(false);
|
||||
setIsLocked(false);
|
||||
setAddress(res.address);
|
||||
setType('ed25519');
|
||||
});
|
||||
}).catch(onAddressError);
|
||||
}
|
||||
};
|
||||
|
||||
// Disconnect the previous instance before opening the new one (network or ledger app change).
|
||||
if (prevLedger && prevLedger !== ledger) {
|
||||
prevLedger.disconnect().catch(console.error).finally(fetchLedgerAddress);
|
||||
} else {
|
||||
fetchLedgerAddress();
|
||||
}
|
||||
|
||||
return () => {
|
||||
isStale = true;
|
||||
};
|
||||
}, [accountIndex, addressOffset, genesis, handleGetAddressError, ledger, ledgerApp, isEthereum]);
|
||||
|
||||
const ledgerRef = useRef(ledger);
|
||||
|
||||
// Keep in sync so the unmount cleanup always closes the latest instance.
|
||||
ledgerRef.current = ledger;
|
||||
|
||||
// On unmount, release the transport so the next session can open it.
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
ledgerRef.current?.disconnect().catch(console.error);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const refresh = useCallback(() => {
|
||||
if (ledger) {
|
||||
// Prevent the address-fetch effect from disconnecting this instance again.
|
||||
prevLedgerRef.current = null;
|
||||
ledger.disconnect()
|
||||
.catch(console.error)
|
||||
.finally(() => setRefreshCount((c) => c + 1));
|
||||
} else {
|
||||
setRefreshCount((c) => c + 1);
|
||||
}
|
||||
}, [ledger]);
|
||||
|
||||
return ({ ...getState(), address, error, isLoading, isLocked, ledger, refresh, type, warning });
|
||||
}
|
||||
@@ -143,10 +143,6 @@ export async function createAccountExternal (name: string, address: string, gene
|
||||
return sendMessage('pri(accounts.create.external)', { address, genesisHash, name });
|
||||
}
|
||||
|
||||
export async function createAccountHardware (address: string, hardwareType: string, accountIndex: number, addressOffset: number, name: string, genesisHash: HexString | null, type: KeypairType): Promise<boolean> {
|
||||
return sendMessage('pri(accounts.create.hardware)', { accountIndex, address, addressOffset, genesisHash, hardwareType, name, type });
|
||||
}
|
||||
|
||||
export async function createAccountSuri (name: string, password: string, suri: string, type?: KeypairType, genesisHash?: HexString | null): Promise<boolean> {
|
||||
return sendMessage('pri(accounts.create.suri)', { genesisHash, name, password, suri, type });
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
// Copyright 2019-2026 @polkadot/extension-ui authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { faUsb } from '@fortawesome/free-brands-svg-icons';
|
||||
import { faCodeBranch, faFileExport, faFileUpload, faKey, faPlusCircle, faQrcode } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import React, { useCallback, useContext } from 'react';
|
||||
|
||||
import { AccountContext, Link, MediaContext, Menu, MenuDivider, MenuItem } from '../components/index.js';
|
||||
import { useIsPopup, useLedger, useTranslation } from '../hooks/index.js';
|
||||
import { useIsPopup, useTranslation } from '../hooks/index.js';
|
||||
import { windowOpen } from '../messaging.js';
|
||||
import { styled } from '../styled.js';
|
||||
|
||||
@@ -17,13 +16,11 @@ interface Props {
|
||||
}
|
||||
|
||||
const jsonPath = '/account/restore-json';
|
||||
const ledgerPath = '/account/import-ledger';
|
||||
|
||||
function MenuAdd ({ className, reference }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { master } = useContext(AccountContext);
|
||||
const mediaAllowed = useContext(MediaContext);
|
||||
const { isLedgerCapable, isLedgerEnabled } = useLedger();
|
||||
const isPopup = useIsPopup();
|
||||
|
||||
const _openJson = useCallback(
|
||||
@@ -32,12 +29,6 @@ function MenuAdd ({ className, reference }: Props): React.ReactElement<Props> {
|
||||
}, []
|
||||
);
|
||||
|
||||
const _onOpenLedgerConnect = useCallback(
|
||||
(): void => {
|
||||
windowOpen(ledgerPath).catch(console.error);
|
||||
}, []
|
||||
);
|
||||
|
||||
return (
|
||||
<Menu
|
||||
className={className}
|
||||
@@ -96,36 +87,6 @@ function MenuAdd ({ className, reference }: Props): React.ReactElement<Props> {
|
||||
<span>{t('Attach external QR-signer account')}</span>
|
||||
</Link>
|
||||
</MenuItem>
|
||||
<MenuItem className='menuItem ledger'>
|
||||
{isLedgerEnabled
|
||||
? (
|
||||
<Link
|
||||
isDisabled={!isLedgerCapable}
|
||||
onClick={isPopup ? _onOpenLedgerConnect : undefined}
|
||||
title={ (!isLedgerCapable && t('Ledger devices can only be connected with Chrome browser')) || ''}
|
||||
to={isPopup ? undefined : ledgerPath}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faUsb}
|
||||
rotation={270}
|
||||
/>
|
||||
<span>{ t('Attach ledger account')}</span>
|
||||
</Link>
|
||||
)
|
||||
: (
|
||||
<Link
|
||||
onClick={isPopup ? _onOpenLedgerConnect : undefined}
|
||||
to={isPopup ? undefined : ledgerPath}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faUsb}
|
||||
rotation={270}
|
||||
/>
|
||||
<span>{ t('Connect Ledger device')}</span>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ function MenuSettings ({ className, reference }: Props): React.ReactElement<Prop
|
||||
const isPopup = useIsPopup();
|
||||
const languageOptions = useMemo(() => getLanguageOptions(), []);
|
||||
const onAction = useContext(ActionContext);
|
||||
const _ledgerAppOptions = settings.availableLedgerApp;
|
||||
|
||||
useEffect(() => {
|
||||
settings.set({ camera: camera ? 'on' : 'off' });
|
||||
@@ -83,11 +82,6 @@ function MenuSettings ({ className, reference }: Props): React.ReactElement<Prop
|
||||
}, []
|
||||
);
|
||||
|
||||
const _onChangeLedgerApp = useCallback(
|
||||
(value: string): void => {
|
||||
settings.set({ ledgerApp: value });
|
||||
}, []
|
||||
);
|
||||
|
||||
const _goToAuthList = useCallback(
|
||||
() => {
|
||||
@@ -123,18 +117,6 @@ function MenuSettings ({ className, reference }: Props): React.ReactElement<Prop
|
||||
value={`${prefix}`}
|
||||
/>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
className='setting'
|
||||
title={t('Ledger App')}
|
||||
>
|
||||
<Dropdown
|
||||
className='dropdown'
|
||||
label=''
|
||||
onChange={_onChangeLedgerApp}
|
||||
options={_ledgerAppOptions}
|
||||
value={settings.ledgerApp}
|
||||
/>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
className='setting'
|
||||
title={t('Language')}
|
||||
@@ -222,9 +204,6 @@ export default React.memo(styled(MenuSettings)<Props>`
|
||||
font-size: 15px;
|
||||
margin-bottom: 0;
|
||||
|
||||
&.ledger {
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
label {
|
||||
color: var(--textColor);
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// Copyright 2019-2026 @polkadot/extension-ui authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { selectableNetworks } from '@polkadot/networks';
|
||||
|
||||
export default selectableNetworks.filter((network) => network.hasLedgerSupport);
|
||||
@@ -408,108 +408,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ledgerhq/devices@npm:8.4.7, @ledgerhq/devices@npm:^8.4.4":
|
||||
version: 8.4.7
|
||||
resolution: "@ledgerhq/devices@npm:8.4.7"
|
||||
dependencies:
|
||||
"@ledgerhq/errors": "npm:^6.22.0"
|
||||
"@ledgerhq/logs": "npm:^6.13.0"
|
||||
rxjs: "npm:^7.8.1"
|
||||
semver: "npm:^7.3.5"
|
||||
checksum: 10/1d49aa692831915f1a540cba2d7faba3a08e5a2d1db831e3849c7791526d6ebb85fab679585f6940d5a923abf91f2113d675445172e37b38d742c3f18952e0fc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ledgerhq/errors@npm:^6.19.1, @ledgerhq/errors@npm:^6.22.0":
|
||||
version: 6.22.0
|
||||
resolution: "@ledgerhq/errors@npm:6.22.0"
|
||||
checksum: 10/6bea0c5fbb07c4f20fbc5083856646708c195c6d138dcbeed1f08f88c97536fd5059959a724332c45f17b8944aa8d8e68d850f43b33bf5b2f30d901c593e2930
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ledgerhq/hw-transport-node-hid-noevents@npm:^6.30.8":
|
||||
version: 6.30.8
|
||||
resolution: "@ledgerhq/hw-transport-node-hid-noevents@npm:6.30.8"
|
||||
dependencies:
|
||||
"@ledgerhq/devices": "npm:8.4.7"
|
||||
"@ledgerhq/errors": "npm:^6.22.0"
|
||||
"@ledgerhq/hw-transport": "npm:^6.31.7"
|
||||
"@ledgerhq/logs": "npm:^6.13.0"
|
||||
node-hid: "npm:2.1.2"
|
||||
checksum: 10/b6e5795636431d06befd64cded66f300b8c81bfa8d590d56f728662e77074cb26898903be6371c1f3a720473173f6c5d223778b2ac8ad84e7b450ed64168f901
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ledgerhq/hw-transport-node-hid-singleton@npm:^6.31.5":
|
||||
version: 6.31.8
|
||||
resolution: "@ledgerhq/hw-transport-node-hid-singleton@npm:6.31.8"
|
||||
dependencies:
|
||||
"@ledgerhq/devices": "npm:8.4.7"
|
||||
"@ledgerhq/errors": "npm:^6.22.0"
|
||||
"@ledgerhq/hw-transport": "npm:^6.31.7"
|
||||
"@ledgerhq/hw-transport-node-hid-noevents": "npm:^6.30.8"
|
||||
"@ledgerhq/logs": "npm:^6.13.0"
|
||||
node-hid: "npm:2.1.2"
|
||||
usb: "npm:2.9.0"
|
||||
checksum: 10/d79272bcb79d863f9b7934a4175e4564faea71257ec419675942e964f95ad9ae159632594f63f86e55c68572c7377848de9f3f4ecf684134beddf182f1a462cb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ledgerhq/hw-transport-webhid@npm:^6.29.4":
|
||||
version: 6.30.3
|
||||
resolution: "@ledgerhq/hw-transport-webhid@npm:6.30.3"
|
||||
dependencies:
|
||||
"@ledgerhq/devices": "npm:8.4.7"
|
||||
"@ledgerhq/errors": "npm:^6.22.0"
|
||||
"@ledgerhq/hw-transport": "npm:^6.31.7"
|
||||
"@ledgerhq/logs": "npm:^6.13.0"
|
||||
checksum: 10/4f9ea31952fd07e366fab0acd020cabdf2346e03aaa6971805aeec8820225ffac66a57aaf375137f599ba0e9fd42f7b3febd923dd3b420b70a421588054b2135
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ledgerhq/hw-transport-webusb@npm:^6.29.4":
|
||||
version: 6.29.7
|
||||
resolution: "@ledgerhq/hw-transport-webusb@npm:6.29.7"
|
||||
dependencies:
|
||||
"@ledgerhq/devices": "npm:8.4.7"
|
||||
"@ledgerhq/errors": "npm:^6.22.0"
|
||||
"@ledgerhq/hw-transport": "npm:^6.31.7"
|
||||
"@ledgerhq/logs": "npm:^6.13.0"
|
||||
checksum: 10/2beb65383939df07c0df18e2b8d97c41e1fffbba4af9bccf26639d3814c617f47293f5a16b268340b5e64fe1f45f4b5a65ecc4edba3c502984d2fa92bed25c2b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ledgerhq/hw-transport@npm:6.31.4":
|
||||
version: 6.31.4
|
||||
resolution: "@ledgerhq/hw-transport@npm:6.31.4"
|
||||
dependencies:
|
||||
"@ledgerhq/devices": "npm:^8.4.4"
|
||||
"@ledgerhq/errors": "npm:^6.19.1"
|
||||
"@ledgerhq/logs": "npm:^6.12.0"
|
||||
events: "npm:^3.3.0"
|
||||
checksum: 10/cf101e5b818e95e59031241d556dbec24658f54104910e414be493bc4b90b0aea50f5d4b3339a237dd0b12845bb2683c845f3a82f2ea9da4e077b68d1e1f7e48
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ledgerhq/hw-transport@npm:^6.31.4, @ledgerhq/hw-transport@npm:^6.31.7":
|
||||
version: 6.31.7
|
||||
resolution: "@ledgerhq/hw-transport@npm:6.31.7"
|
||||
dependencies:
|
||||
"@ledgerhq/devices": "npm:8.4.7"
|
||||
"@ledgerhq/errors": "npm:^6.22.0"
|
||||
"@ledgerhq/logs": "npm:^6.13.0"
|
||||
events: "npm:^3.3.0"
|
||||
checksum: 10/a8c44980fb1d795f23fe62edc6a7b910efcb2dda6b626f6ac3a6b2efd4c855d6591e9cd6fa3d7d65c89eea2418ee4b960878cc6be0d73e7e2a958d3af378f44a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ledgerhq/logs@npm:^6.12.0, @ledgerhq/logs@npm:^6.13.0":
|
||||
version: 6.13.0
|
||||
resolution: "@ledgerhq/logs@npm:6.13.0"
|
||||
checksum: 10/3ac984bdc533505359cd1d8bc74175547fcfa51fef6294a9fd832e8ea88d462a385be5cfe68ccfa1b77cd68b840243e6f79c2be66fe28f123ae57c58625edf5f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@leichtgewicht/ip-codec@npm:^2.0.1":
|
||||
version: 2.0.5
|
||||
resolution: "@leichtgewicht/ip-codec@npm:2.0.5"
|
||||
@@ -1184,7 +1082,6 @@ __metadata:
|
||||
"@polkadot/extension-dapp": "npm:0.64.0"
|
||||
"@polkadot/extension-inject": "npm:0.64.0"
|
||||
"@polkadot/extension-mocks": "npm:0.64.0"
|
||||
"@polkadot/hw-ledger": "npm:^14.0.3"
|
||||
"@polkadot/keyring": "npm:^14.0.3"
|
||||
"@polkadot/networks": "npm:^14.0.3"
|
||||
"@polkadot/react-identicon": "npm:^3.16.7"
|
||||
@@ -1253,35 +1150,6 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@polkadot/hw-ledger-transports@npm:14.0.3":
|
||||
version: 14.0.3
|
||||
resolution: "@polkadot/hw-ledger-transports@npm:14.0.3"
|
||||
dependencies:
|
||||
"@ledgerhq/hw-transport": "npm:^6.31.4"
|
||||
"@ledgerhq/hw-transport-node-hid-singleton": "npm:^6.31.5"
|
||||
"@ledgerhq/hw-transport-webhid": "npm:^6.29.4"
|
||||
"@ledgerhq/hw-transport-webusb": "npm:^6.29.4"
|
||||
"@polkadot/util": "npm:14.0.3"
|
||||
tslib: "npm:^2.8.0"
|
||||
dependenciesMeta:
|
||||
"@ledgerhq/hw-transport-node-hid-singleton":
|
||||
optional: true
|
||||
checksum: 10/5cf48a41079e06ea4c46df4c4aaf2efcca786b19f9eecb0c7c0ba505a1f5a98f730518cff41e3b0fa27f09d6dc74329e2f2b982a2dd8f1c7e59db9d65b90eabe
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@polkadot/hw-ledger@npm:^14.0.3":
|
||||
version: 14.0.3
|
||||
resolution: "@polkadot/hw-ledger@npm:14.0.3"
|
||||
dependencies:
|
||||
"@polkadot/hw-ledger-transports": "npm:14.0.3"
|
||||
"@polkadot/util": "npm:14.0.3"
|
||||
"@zondax/ledger-substrate": "npm:1.1.1"
|
||||
tslib: "npm:^2.8.0"
|
||||
checksum: 10/1f4746a3fd7f311211c451ba26ff1cd11bcfc729c27bc9516e619e5df2d57a9e79e94b163d2e00448864680c7afa0d20b933932134bafa709af091db1e6d9b10
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@polkadot/keyring@https://git.lair.cafe/api/packages/quantus/npm/%40polkadot%2Fkeyring/-/14.0.3-quantus.1/keyring-14.0.3-quantus.1.tgz":
|
||||
version: 14.0.3-quantus.1
|
||||
resolution: "@polkadot/keyring@https://git.lair.cafe/api/packages/quantus/npm/%40polkadot%2Fkeyring/-/14.0.3-quantus.1/keyring-14.0.3-quantus.1.tgz"
|
||||
@@ -2649,13 +2517,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/w3c-web-usb@npm:^1.0.6":
|
||||
version: 1.0.10
|
||||
resolution: "@types/w3c-web-usb@npm:1.0.10"
|
||||
checksum: 10/6ac6786a0788f0846a48b103ab06ca5fde5eb95674217b522420a2f6157bee3e181a961c1b7011940f497c55f4f5cc46129657d881fdd8112b48764089679ad6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/ws@npm:8.5.3":
|
||||
version: 8.5.3
|
||||
resolution: "@types/ws@npm:8.5.3"
|
||||
@@ -3267,26 +3128,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@zondax/ledger-js@npm:^1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "@zondax/ledger-js@npm:1.2.0"
|
||||
dependencies:
|
||||
"@ledgerhq/hw-transport": "npm:6.31.4"
|
||||
checksum: 10/5852b340efd421f96d4e52d0a93d4e8464604c070939dd42fade5a495010a08d730b03e1b7ab3ff0f26f13075d8a0a5226a748a41986f2ed087fca4471f24e4e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@zondax/ledger-substrate@npm:1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "@zondax/ledger-substrate@npm:1.1.1"
|
||||
dependencies:
|
||||
"@ledgerhq/hw-transport": "npm:6.31.4"
|
||||
"@zondax/ledger-js": "npm:^1.2.0"
|
||||
axios: "npm:^1.8.4"
|
||||
checksum: 10/039ffef55b31870f82c6b0f20b518089ad9ce0295d695f880640cb586256adacd4a326c10157ccaf82e897abde7358f9e4e02c069dac672ec90881e21d250722
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@zxcvbn-ts/core@npm:^3.0.4":
|
||||
version: 3.0.4
|
||||
resolution: "@zxcvbn-ts/core@npm:3.0.4"
|
||||
@@ -3909,17 +3750,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"axios@npm:^1.8.4":
|
||||
version: 1.10.0
|
||||
resolution: "axios@npm:1.10.0"
|
||||
dependencies:
|
||||
follow-redirects: "npm:^1.15.6"
|
||||
form-data: "npm:^4.0.0"
|
||||
proxy-from-env: "npm:^1.1.0"
|
||||
checksum: 10/d43c80316a45611fd395743e15d16ea69a95f2b7f7095f2bb12cb78f9ca0a905194a02e52a3bf4e0db9f85fd1186d6c690410644c10ecd8bb0a468e57c2040e4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"b4a@npm:^1.6.4":
|
||||
version: 1.6.7
|
||||
resolution: "b4a@npm:1.6.7"
|
||||
@@ -4602,16 +4432,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bindings@npm:^1.5.0":
|
||||
version: 1.5.0
|
||||
resolution: "bindings@npm:1.5.0"
|
||||
dependencies:
|
||||
file-uri-to-path: "npm:1.0.0"
|
||||
checksum: 10/593d5ae975ffba15fbbb4788fe5abd1e125afbab849ab967ab43691d27d6483751805d98cb92f7ac24a2439a8a8678cd0131c535d5d63de84e383b0ce2786133
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"bl@npm:^4.0.3, bl@npm:^4.1.0":
|
||||
"bl@npm:^4.1.0":
|
||||
version: 4.1.0
|
||||
resolution: "bl@npm:4.1.0"
|
||||
dependencies:
|
||||
@@ -5131,13 +4952,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chownr@npm:^1.1.1":
|
||||
version: 1.1.4
|
||||
resolution: "chownr@npm:1.1.4"
|
||||
checksum: 10/115648f8eb38bac5e41c3857f3e663f9c39ed6480d1349977c4d96c95a47266fcacc5a5aabf3cb6c481e22d72f41992827db47301851766c4fd77ac21a4f081d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chownr@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "chownr@npm:3.0.0"
|
||||
@@ -5924,13 +5738,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"detect-libc@npm:^2.0.0":
|
||||
version: 2.0.4
|
||||
resolution: "detect-libc@npm:2.0.4"
|
||||
checksum: 10/136e995f8c5ffbc515955b0175d441b967defd3d5f2268e89fa695e9c7170d8bed17993e31a34b04f0fad33d844a3a598e0fd519a8e9be3cad5f67662d96fee0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"detect-node@npm:^2.0.4":
|
||||
version: 2.1.0
|
||||
resolution: "detect-node@npm:2.1.0"
|
||||
@@ -7192,7 +6999,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"events@npm:^3.2.0, events@npm:^3.3.0":
|
||||
"events@npm:^3.2.0":
|
||||
version: 3.3.0
|
||||
resolution: "events@npm:3.3.0"
|
||||
checksum: 10/a3d47e285e28d324d7180f1e493961a2bbb4cad6412090e4dec114f4db1f5b560c7696ee8e758f55e23913ede856e3689cd3aa9ae13c56b5d8314cd3b3ddd1be
|
||||
@@ -7227,13 +7034,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"expand-template@npm:^2.0.3":
|
||||
version: 2.0.3
|
||||
resolution: "expand-template@npm:2.0.3"
|
||||
checksum: 10/588c19847216421ed92befb521767b7018dc88f88b0576df98cb242f20961425e96a92cbece525ef28cc5becceae5d544ae0f5b9b5e2aa05acb13716ca5b3099
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"exponential-backoff@npm:^3.1.1":
|
||||
version: 3.1.2
|
||||
resolution: "exponential-backoff@npm:3.1.2"
|
||||
@@ -7454,13 +7254,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"file-uri-to-path@npm:1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "file-uri-to-path@npm:1.0.0"
|
||||
checksum: 10/b648580bdd893a008c92c7ecc96c3ee57a5e7b6c4c18a9a09b44fb5d36d79146f8e442578bc0e173dc027adf3987e254ba1dfd6e3ec998b7c282873010502144
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"filename-reserved-regex@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "filename-reserved-regex@npm:2.0.0"
|
||||
@@ -7591,7 +7384,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.15.6":
|
||||
"follow-redirects@npm:^1.0.0":
|
||||
version: 1.15.9
|
||||
resolution: "follow-redirects@npm:1.15.9"
|
||||
peerDependenciesMeta:
|
||||
@@ -7656,13 +7449,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fs-constants@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "fs-constants@npm:1.0.0"
|
||||
checksum: 10/18f5b718371816155849475ac36c7d0b24d39a11d91348cfcb308b4494824413e03572c403c86d3a260e049465518c4f0d5bd00f0371cdfcad6d4f30a85b350d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fs-extra@npm:^11.1.1":
|
||||
version: 11.3.0
|
||||
resolution: "fs-extra@npm:11.3.0"
|
||||
@@ -7942,13 +7728,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"github-from-package@npm:0.0.0":
|
||||
version: 0.0.0
|
||||
resolution: "github-from-package@npm:0.0.0"
|
||||
checksum: 10/2a091ba07fbce22205642543b4ea8aaf068397e1433c00ae0f9de36a3607baf5bcc14da97fbb798cfca6393b3c402031fca06d8b491a44206d6efef391c58537
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"github-url-to-object@npm:^4.0.4":
|
||||
version: 4.0.6
|
||||
resolution: "github-url-to-object@npm:4.0.6"
|
||||
@@ -10108,7 +9887,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"minimist@npm:^1.2.0, minimist@npm:^1.2.3, minimist@npm:^1.2.5, minimist@npm:^1.2.6":
|
||||
"minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6":
|
||||
version: 1.2.8
|
||||
resolution: "minimist@npm:1.2.8"
|
||||
checksum: 10/908491b6cc15a6c440ba5b22780a0ba89b9810e1aea684e253e43c4e3b8d56ec1dcdd7ea96dde119c29df59c936cde16062159eae4225c691e19c70b432b6e6f
|
||||
@@ -10191,13 +9970,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mkdirp-classic@npm:^0.5.2, mkdirp-classic@npm:^0.5.3":
|
||||
version: 0.5.3
|
||||
resolution: "mkdirp-classic@npm:0.5.3"
|
||||
checksum: 10/3f4e088208270bbcc148d53b73e9a5bd9eef05ad2cbf3b3d0ff8795278d50dd1d11a8ef1875ff5aea3fa888931f95bfcb2ad5b7c1061cfefd6284d199e6776ac
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mkdirp@npm:^0.5.1":
|
||||
version: 0.5.6
|
||||
resolution: "mkdirp@npm:0.5.6"
|
||||
@@ -10313,13 +10085,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"napi-build-utils@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "napi-build-utils@npm:2.0.0"
|
||||
checksum: 10/69adcdb828481737f1ec64440286013f6479d5b264e24d5439ba795f65293d0bb6d962035de07c65fae525ed7d2fcd0baab6891d8e3734ea792fec43918acf83
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"napi-postinstall@npm:^0.3.0":
|
||||
version: 0.3.0
|
||||
resolution: "napi-postinstall@npm:0.3.0"
|
||||
@@ -10422,33 +10187,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-abi@npm:^3.3.0":
|
||||
version: 3.75.0
|
||||
resolution: "node-abi@npm:3.75.0"
|
||||
dependencies:
|
||||
semver: "npm:^7.3.5"
|
||||
checksum: 10/dd87627fa4f447bda9c69dc1ec0da82e3b466790844b5bd7f7787db093dfea21dcc405588e13b5207c266472c1fda9b95060da8d70644aeb5fc31ec81dc2007c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-addon-api@npm:^3.0.2":
|
||||
version: 3.2.1
|
||||
resolution: "node-addon-api@npm:3.2.1"
|
||||
dependencies:
|
||||
node-gyp: "npm:latest"
|
||||
checksum: 10/681b52dfa3e15b0a8e5cf283cc0d8cd5fd2a57c559ae670fcfd20544cbb32f75de7648674110defcd17ab2c76ebef630aa7d2d2f930bc7a8cc439b20fe233518
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-addon-api@npm:^6.0.0":
|
||||
version: 6.1.0
|
||||
resolution: "node-addon-api@npm:6.1.0"
|
||||
dependencies:
|
||||
node-gyp: "npm:latest"
|
||||
checksum: 10/8eea1d4d965930a177a0508695beb0d89b4c1d80bf330646a035357a1e8fc31e0d09686e2374996e96e757b947a7ece319f98ede3146683f162597c0bcb4df90
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-domexception@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "node-domexception@npm:1.0.0"
|
||||
@@ -10488,17 +10226,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-gyp-build@npm:^4.5.0":
|
||||
version: 4.8.4
|
||||
resolution: "node-gyp-build@npm:4.8.4"
|
||||
bin:
|
||||
node-gyp-build: bin.js
|
||||
node-gyp-build-optional: optional.js
|
||||
node-gyp-build-test: build-test.js
|
||||
checksum: 10/6a7d62289d1afc419fc8fc9bd00aa4e554369e50ca0acbc215cb91446148b75ff7e2a3b53c2c5b2c09a39d416d69f3d3237937860373104b5fe429bf30ad9ac5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-gyp@npm:latest":
|
||||
version: 11.2.0
|
||||
resolution: "node-gyp@npm:11.2.0"
|
||||
@@ -10519,20 +10246,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-hid@npm:2.1.2":
|
||||
version: 2.1.2
|
||||
resolution: "node-hid@npm:2.1.2"
|
||||
dependencies:
|
||||
bindings: "npm:^1.5.0"
|
||||
node-addon-api: "npm:^3.0.2"
|
||||
node-gyp: "npm:latest"
|
||||
prebuild-install: "npm:^7.1.1"
|
||||
bin:
|
||||
hid-showdevices: src/show-devices.js
|
||||
checksum: 10/7b424d9f049221b439cfa3a092f415b608bcd316b819ff1fb0bf5234d766348d9fdaeaafa5ccece514c045825a936cb5617080168066d16b3aaeb0e9808e38f9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"node-releases@npm:^2.0.19":
|
||||
version: 2.0.19
|
||||
resolution: "node-releases@npm:2.0.19"
|
||||
@@ -11223,28 +10936,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prebuild-install@npm:^7.1.1":
|
||||
version: 7.1.3
|
||||
resolution: "prebuild-install@npm:7.1.3"
|
||||
dependencies:
|
||||
detect-libc: "npm:^2.0.0"
|
||||
expand-template: "npm:^2.0.3"
|
||||
github-from-package: "npm:0.0.0"
|
||||
minimist: "npm:^1.2.3"
|
||||
mkdirp-classic: "npm:^0.5.3"
|
||||
napi-build-utils: "npm:^2.0.0"
|
||||
node-abi: "npm:^3.3.0"
|
||||
pump: "npm:^3.0.0"
|
||||
rc: "npm:^1.2.7"
|
||||
simple-get: "npm:^4.0.0"
|
||||
tar-fs: "npm:^2.0.0"
|
||||
tunnel-agent: "npm:^0.6.0"
|
||||
bin:
|
||||
prebuild-install: bin.js
|
||||
checksum: 10/1b7e4c00d2750b532a4fc2a83ffb0c5fefa1b6f2ad071896ead15eeadc3255f5babd816949991af083cf7429e375ae8c7d1c51f73658559da36f948a020a3a11
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"precinct@npm:^8.1.0":
|
||||
version: 8.3.1
|
||||
resolution: "precinct@npm:8.3.1"
|
||||
@@ -11403,13 +11094,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"proxy-from-env@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "proxy-from-env@npm:1.1.0"
|
||||
checksum: 10/f0bb4a87cfd18f77bc2fba23ae49c3b378fb35143af16cc478171c623eebe181678f09439707ad80081d340d1593cd54a33a0113f3ccb3f4bc9451488780ee23
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"psl@npm:^1.1.33":
|
||||
version: 1.15.0
|
||||
resolution: "psl@npm:1.15.0"
|
||||
@@ -13361,31 +13045,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tar-fs@npm:^2.0.0":
|
||||
version: 2.1.3
|
||||
resolution: "tar-fs@npm:2.1.3"
|
||||
dependencies:
|
||||
chownr: "npm:^1.1.1"
|
||||
mkdirp-classic: "npm:^0.5.2"
|
||||
pump: "npm:^3.0.0"
|
||||
tar-stream: "npm:^2.1.4"
|
||||
checksum: 10/37fdfd3aa73f4f49c0821ef75f67647ecafd5370d2e311d9ace6ff3825ff4355014055c3d43407c6a655adf6c5bfb0cbcf93412161dad5af7110eb7d7a0c2eae
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tar-stream@npm:^2.1.4":
|
||||
version: 2.2.0
|
||||
resolution: "tar-stream@npm:2.2.0"
|
||||
dependencies:
|
||||
bl: "npm:^4.0.3"
|
||||
end-of-stream: "npm:^1.4.1"
|
||||
fs-constants: "npm:^1.0.0"
|
||||
inherits: "npm:^2.0.3"
|
||||
readable-stream: "npm:^3.1.1"
|
||||
checksum: 10/1a52a51d240c118cbcd30f7368ea5e5baef1eac3e6b793fb1a41e6cd7319296c79c0264ccc5859f5294aa80f8f00b9239d519e627b9aade80038de6f966fec6a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tar@npm:^7.4.3":
|
||||
version: 7.4.3
|
||||
resolution: "tar@npm:7.4.3"
|
||||
@@ -13729,15 +13388,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tunnel-agent@npm:^0.6.0":
|
||||
version: 0.6.0
|
||||
resolution: "tunnel-agent@npm:0.6.0"
|
||||
dependencies:
|
||||
safe-buffer: "npm:^5.0.1"
|
||||
checksum: 10/7f0d9ed5c22404072b2ae8edc45c071772affd2ed14a74f03b4e71b4dd1a14c3714d85aed64abcaaee5fec2efc79002ba81155c708f4df65821b444abb0cfade
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"type-check@npm:^0.4.0, type-check@npm:~0.4.0":
|
||||
version: 0.4.0
|
||||
resolution: "type-check@npm:0.4.0"
|
||||
@@ -14117,18 +13767,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"usb@npm:2.9.0":
|
||||
version: 2.9.0
|
||||
resolution: "usb@npm:2.9.0"
|
||||
dependencies:
|
||||
"@types/w3c-web-usb": "npm:^1.0.6"
|
||||
node-addon-api: "npm:^6.0.0"
|
||||
node-gyp: "npm:latest"
|
||||
node-gyp-build: "npm:^4.5.0"
|
||||
checksum: 10/ab68d4247a24a463328e293256aec5682c12163278ab474b7243a9dba6283a44bb74f9e0ca96f1d00197bb271f294549c3a4debdbd3452a02750a4a65ff29ee6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"util-deprecate@npm:^1.0.1, util-deprecate@npm:~1.0.1":
|
||||
version: 1.0.2
|
||||
resolution: "util-deprecate@npm:1.0.2"
|
||||
|
||||
Reference in New Issue
Block a user