diff --git a/packages/extension-base/package.json b/packages/extension-base/package.json index 9a4a9d29..7e2f638a 100644 --- a/packages/extension-base/package.json +++ b/packages/extension-base/package.json @@ -30,8 +30,8 @@ "@polkadot/phishing": "^0.23.3", "@polkadot/rpc-provider": "^12.3.1", "@polkadot/types": "^12.3.1", - "@polkadot/ui-keyring": "^3.8.3", - "@polkadot/ui-settings": "^3.8.3", + "@polkadot/ui-keyring": "^3.9.1", + "@polkadot/ui-settings": "^3.9.1", "@polkadot/util": "^13.0.2", "@polkadot/util-crypto": "^13.0.2", "eventemitter3": "^5.0.1", diff --git a/packages/extension-ui/package.json b/packages/extension-ui/package.json index 1b8ff01c..f7fa94e0 100644 --- a/packages/extension-ui/package.json +++ b/packages/extension-ui/package.json @@ -32,12 +32,12 @@ "@polkadot/hw-ledger": "^13.0.2", "@polkadot/keyring": "^13.0.2", "@polkadot/networks": "^13.0.2", - "@polkadot/react-identicon": "^3.8.3", - "@polkadot/react-qr": "^3.8.3", + "@polkadot/react-identicon": "^3.9.1", + "@polkadot/react-qr": "^3.9.1", "@polkadot/types": "^12.3.1", "@polkadot/types-augment": "^12.3.1", - "@polkadot/ui-keyring": "^3.8.3", - "@polkadot/ui-settings": "^3.8.3", + "@polkadot/ui-keyring": "^3.9.1", + "@polkadot/ui-settings": "^3.9.1", "@polkadot/util": "^13.0.2", "@polkadot/util-crypto": "^13.0.2", "file-saver": "^2.0.5", diff --git a/packages/extension-ui/src/Popup/Signing/LedgerSign.tsx b/packages/extension-ui/src/Popup/Signing/LedgerSign.tsx index 14e70815..b38aa0dd 100644 --- a/packages/extension-ui/src/Popup/Signing/LedgerSign.tsx +++ b/packages/extension-ui/src/Popup/Signing/LedgerSign.tsx @@ -1,7 +1,11 @@ // Copyright 2019-2024 @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'; @@ -23,8 +27,9 @@ interface Props { className?: string; error: string | null; genesisHash?: string; - onSignature?: ({ signature }: { signature: HexString }, signedTransaction: HexString) => void; - payload?: SignerPayloadJSON; + onSignature?: ({ signature }: { signature: HexString }, signedTransaction?: HexString) => void; + payloadJson?: SignerPayloadJSON; + payloadExt?: ExtrinsicPayload setError: (value: string | null) => void; } @@ -50,7 +55,7 @@ function getMetadataProof (chain: Chain, payload: SignerPayloadJSON) { }; } -function LedgerSign ({ accountIndex, addressOffset, className, error, genesisHash, onSignature, payload, setError }: Props): React.ReactElement { +function LedgerSign ({ accountIndex, addressOffset, className, error, genesisHash, onSignature, payloadExt, payloadJson, setError }: Props): React.ReactElement { const [isBusy, setIsBusy] = useState(false); const { t } = useTranslation(); const chain = useMetadata(genesisHash); @@ -69,43 +74,61 @@ function LedgerSign ({ accountIndex, addressOffset, className, error, genesisHas const _onSignLedger = useCallback( (): void => { - if (!ledger || !payload || !onSignature || !chain) { + if (!ledger || !payloadJson || !onSignature || !chain || !payloadExt) { + if (!chain) { + setError('No chain information found. You may need to update/upload the metadata.'); + setIsBusy(false); + } + return; } - if (!chain?.definition.rawMetadata) { - setError('No metadata found for this chain. You must upload the metadata to the extension in order to use Ledger.'); - } - - const { raw, txMetadata } = getMetadataProof(chain, payload); - - const metaBuff = Buffer.from(txMetadata); - setError(null); setIsBusy(true); - ledger.signWithMetadata(raw.toU8a(true), accountIndex, addressOffset, { metadata: metaBuff }) - .then((signature) => { - const extrinsic = chain.registry.createType( - 'Extrinsic', - { method: raw.method }, - { version: 4 } - ); - ledger.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); - }); + const currApp = settings.get().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.'); + } + + const { raw, txMetadata } = getMetadataProof(chain, payloadJson); + + const metaBuff = Buffer.from(txMetadata); + + (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, onSignature, payload, setError] + [accountIndex, addressOffset, chain, ledger, onSignature, payloadJson, payloadExt, setError] ); return ( diff --git a/packages/extension-ui/src/Popup/Signing/Request/index.tsx b/packages/extension-ui/src/Popup/Signing/Request/index.tsx index 8a835d63..af6e027c 100644 --- a/packages/extension-ui/src/Popup/Signing/Request/index.tsx +++ b/packages/extension-ui/src/Popup/Signing/Request/index.tsx @@ -117,7 +117,8 @@ export default function Request ({ account: { accountIndex, addressOffset, genes error={error} genesisHash={json.genesisHash} onSignature={_onSignature} - payload={json} + payloadExt={payload} + payloadJson={json} setError={setError} /> )} diff --git a/packages/extension-ui/src/hooks/useLedger.ts b/packages/extension-ui/src/hooks/useLedger.ts index 2619543d..488b8973 100644 --- a/packages/extension-ui/src/hooks/useLedger.ts +++ b/packages/extension-ui/src/hooks/useLedger.ts @@ -10,7 +10,7 @@ import type { HexString } from '@polkadot/util/types'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { LedgerGeneric } from '@polkadot/hw-ledger'; +import { Ledger, LedgerGeneric } from '@polkadot/hw-ledger'; import { knownLedger } from '@polkadot/networks/defaults'; import { settings } from '@polkadot/ui-settings'; import { assert } from '@polkadot/util'; @@ -29,7 +29,7 @@ interface State extends StateBase { error: string | null; isLoading: boolean; isLocked: boolean; - ledger: LedgerGeneric | null; + ledger: LedgerGeneric | Ledger | null; refresh: () => void; warning: string | null; } @@ -47,8 +47,8 @@ function getState (): StateBase { }; } -function retrieveLedger (genesis: string): LedgerGeneric { - let ledger: LedgerGeneric | null = null; +function retrieveLedger (genesis: string): LedgerGeneric | Ledger { + let ledger: LedgerGeneric | Ledger | null = null; const currApp = settings.get().ledgerApp; @@ -72,6 +72,8 @@ function retrieveLedger (genesis: string): LedgerGeneric { ledger = new LedgerGeneric('webusb', def.network, knownLedger['polkadot']); } else if (currApp === 'migration') { ledger = new LedgerGeneric('webusb', def.network, knownLedger[def.network]); + } else if (currApp === 'chainSpecific') { + ledger = new Ledger('webusb', def.network); } else { // This will never get touched since it will always hit the above two. This satisfies the compiler. ledger = new LedgerGeneric('webusb', def.network, knownLedger['polkadot']); @@ -89,6 +91,28 @@ export default function useLedger (genesis?: string | null, accountIndex = 0, ad const [address, setAddress] = useState(null); const { t } = useTranslation(); + const handleGetAddressError = (e: Error, genesis: string) => { + setIsLoading(false); + const { network } = getNetwork(genesis) || { network: 'unknown network' }; + + const warningMessage = e.message.includes('Code: 26628') + ? t('Is your ledger locked?') + : null; + + const errorMessage = e.message.includes('App does not seem to be open') + ? t('App "{{network}}" does not seem to be open', { replace: { network } }) + : e.message; + + setIsLocked(true); + setWarning(warningMessage); + setError(t( + 'Ledger error: {{errorMessage}}', + { replace: { errorMessage } } + )); + console.error(e); + setAddress(null); + }; + const ledger = useMemo(() => { setError(null); setIsLocked(false); @@ -131,31 +155,25 @@ export default function useLedger (genesis?: string | null, accountIndex = 0, ad // Just in case, but this shouldn't be triggered assert(chosenNetwork, t('This network is not available, please report an issue to update the known chains')); - ledger.getAddress(chosenNetwork.ss58Format, false, accountIndex, addressOffset) - .then((res) => { - setIsLoading(false); - setAddress(res.address); - }).catch((e: Error) => { - setIsLoading(false); - const { network } = getNetwork(genesis) || { network: 'unknown network' }; + const currApp = settings.get().ledgerApp; - const warningMessage = e.message.includes('Code: 26628') - ? t('Is your ledger locked?') - : null; - - const errorMessage = e.message.includes('App does not seem to be open') - ? t('App "{{network}}" does not seem to be open', { replace: { network } }) - : e.message; - - setIsLocked(true); - setWarning(warningMessage); - setError(t( - 'Ledger error: {{errorMessage}}', - { replace: { errorMessage } } - )); - console.error(e); - setAddress(null); - }); + if (currApp === 'generic' || currApp === 'migration') { + (ledger as LedgerGeneric).getAddress(chosenNetwork.ss58Format, false, accountIndex, addressOffset) + .then((res) => { + setIsLoading(false); + setAddress(res.address); + }).catch((e: Error) => { + handleGetAddressError(e, genesis); + }); + } else if (currApp === 'chainSpecific') { + (ledger as Ledger).getAddress(false, accountIndex, addressOffset) + .then((res) => { + setIsLoading(false); + setAddress(res.address); + }).catch((e: Error) => { + handleGetAddressError(e, genesis); + }); + } // If the dependency array is exhaustive, with t, the translation function, it // triggers a useless re-render when ledger device is connected. // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/yarn.lock b/yarn.lock index 73199d4b..592248a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -929,8 +929,8 @@ __metadata: "@polkadot/phishing": "npm:^0.23.3" "@polkadot/rpc-provider": "npm:^12.3.1" "@polkadot/types": "npm:^12.3.1" - "@polkadot/ui-keyring": "npm:^3.8.3" - "@polkadot/ui-settings": "npm:^3.8.3" + "@polkadot/ui-keyring": "npm:^3.9.1" + "@polkadot/ui-settings": "npm:^3.9.1" "@polkadot/util": "npm:^13.0.2" "@polkadot/util-crypto": "npm:^13.0.2" eventemitter3: "npm:^5.0.1" @@ -1036,12 +1036,12 @@ __metadata: "@polkadot/hw-ledger": "npm:^13.0.2" "@polkadot/keyring": "npm:^13.0.2" "@polkadot/networks": "npm:^13.0.2" - "@polkadot/react-identicon": "npm:^3.8.3" - "@polkadot/react-qr": "npm:^3.8.3" + "@polkadot/react-identicon": "npm:^3.9.1" + "@polkadot/react-qr": "npm:^3.9.1" "@polkadot/types": "npm:^12.3.1" "@polkadot/types-augment": "npm:^12.3.1" - "@polkadot/ui-keyring": "npm:^3.8.3" - "@polkadot/ui-settings": "npm:^3.8.3" + "@polkadot/ui-keyring": "npm:^3.9.1" + "@polkadot/ui-settings": "npm:^3.9.1" "@polkadot/util": "npm:^13.0.2" "@polkadot/util-crypto": "npm:^13.0.2" "@types/enzyme": "npm:^3.10.18" @@ -1165,13 +1165,13 @@ __metadata: languageName: node linkType: hard -"@polkadot/react-identicon@npm:^3.8.3": - version: 3.8.3 - resolution: "@polkadot/react-identicon@npm:3.8.3" +"@polkadot/react-identicon@npm:^3.9.1": + version: 3.9.1 + resolution: "@polkadot/react-identicon@npm:3.9.1" dependencies: "@polkadot/keyring": "npm:^13.0.2" - "@polkadot/ui-settings": "npm:3.8.3" - "@polkadot/ui-shared": "npm:3.8.3" + "@polkadot/ui-settings": "npm:3.9.1" + "@polkadot/ui-shared": "npm:3.9.1" "@polkadot/util": "npm:^13.0.2" "@polkadot/util-crypto": "npm:^13.0.2" ethereum-blockies-base64: "npm:^1.0.2" @@ -1186,15 +1186,15 @@ __metadata: react: "*" react-dom: "*" react-is: "*" - checksum: 10/6dddfca42732d141acd2e460d25635845287e0e8bfae6c170f72a691c49b1f34d08e1e975d1a2c2216e1814f1928a29982b806d6bc30f3f08ddddad214b91867 + checksum: 10/6a3ad4e129af15e2d6833def626085e392c960211935cf901560f434247560ae6dcfb8d8ecde38b56f317ac3e0d84653ef9c5b68e258c2791467b1339b595bef languageName: node linkType: hard -"@polkadot/react-qr@npm:^3.8.3": - version: 3.8.3 - resolution: "@polkadot/react-qr@npm:3.8.3" +"@polkadot/react-qr@npm:^3.9.1": + version: 3.9.1 + resolution: "@polkadot/react-qr@npm:3.9.1" dependencies: - "@polkadot/ui-settings": "npm:3.8.3" + "@polkadot/ui-settings": "npm:3.9.1" "@polkadot/util": "npm:^13.0.2" "@polkadot/util-crypto": "npm:^13.0.2" "@zxing/browser": "npm:^0.1.5" @@ -1208,7 +1208,7 @@ __metadata: react: "*" react-dom: "*" react-is: "*" - checksum: 10/1afb77b45a4b6bf0cb110dbf405bfd8a1edbabb9e4639e3b34f0f7f0c1c706799f462b1590e18da8d8de369172f075bdbc9800e7d54e9b48041724203294c600 + checksum: 10/f196e19e6d5c11ef7f7824fa67ad41fcde790ad6505113a2e8383c4c79c5c2a8947a619c0513709d9983679ff60af7f8c0811e1a232772fb5438dcbecd442e21 languageName: node linkType: hard @@ -1337,12 +1337,12 @@ __metadata: languageName: node linkType: hard -"@polkadot/ui-keyring@npm:^3.8.3": - version: 3.8.3 - resolution: "@polkadot/ui-keyring@npm:3.8.3" +"@polkadot/ui-keyring@npm:^3.9.1": + version: 3.9.1 + resolution: "@polkadot/ui-keyring@npm:3.9.1" dependencies: "@polkadot/keyring": "npm:^13.0.2" - "@polkadot/ui-settings": "npm:3.8.3" + "@polkadot/ui-settings": "npm:3.9.1" "@polkadot/util": "npm:^13.0.2" "@polkadot/util-crypto": "npm:^13.0.2" mkdirp: "npm:^3.0.1" @@ -1353,13 +1353,13 @@ __metadata: "@polkadot/keyring": "*" "@polkadot/ui-settings": "*" "@polkadot/util": "*" - checksum: 10/d8b3ebf3da39ebbcabe1e0c41c9ed731a14c186c1698c598b4aac6217162c7174453860d06582200b10bc687fc75ec713238ce1687efb4672de989ab728ee99f + checksum: 10/eec438ef2ad9363d77f51d60df9802f1cad3379661883a9412f9ca1af44e31444fc7c04eec1ec7ecf2ae76af1718ffce8e335dcf572d89a6ffc6403bf6c36423 languageName: node linkType: hard -"@polkadot/ui-settings@npm:3.8.3, @polkadot/ui-settings@npm:^3.8.3": - version: 3.8.3 - resolution: "@polkadot/ui-settings@npm:3.8.3" +"@polkadot/ui-settings@npm:3.9.1, @polkadot/ui-settings@npm:^3.9.1": + version: 3.9.1 + resolution: "@polkadot/ui-settings@npm:3.9.1" dependencies: "@polkadot/networks": "npm:^13.0.2" "@polkadot/util": "npm:^13.0.2" @@ -1369,20 +1369,20 @@ __metadata: peerDependencies: "@polkadot/networks": "*" "@polkadot/util": "*" - checksum: 10/be6d9f5111ba94b9e86b3348bd0f7e68008611b54fcc159b3a163d7331b345f1552b2d1ba7b6b763387607110e06eecd799b7ad61ee4d5847056b128a85bfdcf + checksum: 10/d3a28af0233f2ee9a8453501de3dd2fabc46bc98cb4c0e46a79654c87a0a41f3148dd6949b796932a4c12fc974b2b924d9eb0e76f91f7ce9ea27e5012e1e606a languageName: node linkType: hard -"@polkadot/ui-shared@npm:3.8.3": - version: 3.8.3 - resolution: "@polkadot/ui-shared@npm:3.8.3" +"@polkadot/ui-shared@npm:3.9.1": + version: 3.9.1 + resolution: "@polkadot/ui-shared@npm:3.9.1" dependencies: colord: "npm:^2.9.3" tslib: "npm:^2.6.2" peerDependencies: "@polkadot/util": "*" "@polkadot/util-crypto": "*" - checksum: 10/a7cbeed7ccbe2f3212d20155787d3a7f4ec2a5662dfb950d965a1ac8ea05fd380f3b9456147cf2e15dc5adbf0df71eedfd94c0707db44a16f1bf2f84c0ebf01c + checksum: 10/80a3a59ca25ed0e572a2eb85533f54f9714c6fd21f85fcf46775fc57c5d55c048049378c94372260df9d860569197ea38dbf3a24cc8ed1e416f67c69408f3789 languageName: node linkType: hard