Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable "Chain Specific App" setting #1445

Merged
merged 5 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 45 additions & 27 deletions packages/extension-ui/src/Popup/Signing/LedgerSign.tsx
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -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;
}

Expand All @@ -50,7 +55,7 @@ function getMetadataProof (chain: Chain, payload: SignerPayloadJSON) {
};
}

function LedgerSign ({ accountIndex, addressOffset, className, error, genesisHash, onSignature, payload, setError }: Props): React.ReactElement<Props> {
function LedgerSign ({ accountIndex, addressOffset, className, error, genesisHash, onSignature, payloadExt, payloadJson, setError }: Props): React.ReactElement<Props> {
const [isBusy, setIsBusy] = useState(false);
const { t } = useTranslation();
const chain = useMetadata(genesisHash);
Expand All @@ -69,43 +74,56 @@ function LedgerSign ({ accountIndex, addressOffset, className, error, genesisHas

const _onSignLedger = useCallback(
(): void => {
if (!ledger || !payload || !onSignature || !chain) {
if (!ledger || !payloadJson || !onSignature || !chain || !payloadExt) {
TarikGul marked this conversation as resolved.
Show resolved Hide resolved
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 { raw, txMetadata } = getMetadataProof(chain, payloadJson);

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') {
(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 (
Expand Down
3 changes: 2 additions & 1 deletion packages/extension-ui/src/Popup/Signing/Request/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
/>
)}
Expand Down
76 changes: 47 additions & 29 deletions packages/extension-ui/src/hooks/useLedger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
}
Expand All @@ -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;

Expand All @@ -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']);
Expand All @@ -89,6 +91,28 @@ export default function useLedger (genesis?: string | null, accountIndex = 0, ad
const [address, setAddress] = useState<string | null>(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);
Expand Down Expand Up @@ -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 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 currApp = settings.get().ledgerApp;

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
Expand Down