Skip to content

Commit

Permalink
Merge pull request #3732 from Emurgo/feat/trezor-conway
Browse files Browse the repository at this point in the history
Trezor wallet governance actions support
  • Loading branch information
vsubhuman authored Nov 15, 2024
2 parents 40bdc47 + 72e6100 commit 0cc43ac
Show file tree
Hide file tree
Showing 12 changed files with 864 additions and 236 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@ import { FormattedMessage } from 'react-intl';
import globalMessages from '../../../i18n/global-messages';
import { useNavigateTo } from '../../features/governace/common/useNavigateTo';
import { FailedIlustration } from './FailedIlustration';
import LocalizableError from '../../../i18n/LocalizableError';

export const TransactionFailed = () => {
export const TransactionFailed = (props: { error: Error | null }) => {
const navigate = useNavigateTo();
const { error } = props;

return (
<Stack alignItems="center" mt="110px">
<FailedIlustration />
<Typography variant="h5" fontWeight="500" mt={4} mb={1}>
<FormattedMessage {...globalMessages.transactionFailed} />
</Typography>
<Typography variant="body1" mb={2} color="ds.text_gray_low">
<FormattedMessage {...globalMessages.transactionFailedInfo} />
<FormattedMessage {...(
error instanceof LocalizableError ? error : globalMessages.transactionFailedInfo
)}/>
</Typography>
{/* @ts-ignore */}
<Button variant="primary" onClick={() => navigate.selectStatus()}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type Props = {
const GovernanceTransactionFailedPage = (props: Props): any => {
return (
<GovernanceLayout {...props}>
<TransactionFailed />
<TransactionFailed error={props.stores.substores.ada.delegationTransaction.error} />
</GovernanceLayout>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,18 @@ import {
CardanoTxOutputSerializationFormat,
CardanoTxSigningMode,
CardanoTxWitnessType,
CardanoDRepType,
} from 'trezor-connect-flow';
import type { Address, Addressing, Value, } from '../../lib/storage/models/PublicDeriver/interfaces';
import { HaskellShelleyTxSignRequest } from './HaskellShelleyTxSignRequest';
import { Bip44DerivationLevels, } from '../../lib/storage/database/walletTypes/bip44/api/utils';
import { ChainDerivations, } from '../../../../config/numbersConfig';

import { RustModule } from '../../lib/cardanoCrypto/rustLoader';
import { range } from 'lodash';
import { toHexOrBase58 } from '../../lib/storage/bridge/utils';
import blake2b from 'blake2b';
import { derivePublicByAddressing } from '../../lib/cardanoCrypto/deriveByAddressing';
import { bytesToHex, iterateLenGet, iterateLenGetMap, maybe } from '../../../../coreUtils';
import { bytesToHex, iterateLenGet, iterateLenGetMap, maybe, forceNonNull } from '../../../../coreUtils';
import { mergeWitnessSets } from '../utils';

// ==================== TREZOR ==================== //
Expand Down Expand Up @@ -104,7 +104,7 @@ export function createTrezorSignTxPayload(
...request,
certificates: formatTrezorCertificates(
certificates,
range(0, certificates.len()).map(_i => stakingKeyPath),
(_) => stakingKeyPath,
)
};

Expand Down Expand Up @@ -161,35 +161,76 @@ function formatTrezorWithdrawals(
}))
.toArray();
}

function formatTrezorCertificates(
certificates: RustModule.WalletV4.Certificates,
paths: Array<Array<number>>,
getPath: (stakeCredential: RustModule.WalletV4.Credential) => Array<number>,
): Array<CardanoCertificate> {
const result = [];
for (const [cert, path] of iterateLenGet(certificates).zip(paths)) {
if (cert.as_stake_registration() != null) {
for (const cert of iterateLenGet(certificates)) {
const registrationCert = cert.as_stake_registration();
if (registrationCert != null) {
result.push({
type: CardanoCertificateType.STAKE_REGISTRATION,
path,
path: getPath(registrationCert.stake_credential()),
});
continue;
}
if (cert.as_stake_deregistration() != null) {
const deregistrationCert = cert.as_stake_deregistration();
if (deregistrationCert != null) {
result.push({
type: CardanoCertificateType.STAKE_DEREGISTRATION,
path,
path: getPath(deregistrationCert.stake_credential()),
});
continue;
}
const delegationCert = cert.as_stake_delegation();
if (delegationCert != null) {
result.push({
type: CardanoCertificateType.STAKE_DELEGATION,
path: getPath(delegationCert.stake_credential()),
pool: delegationCert.pool_keyhash().to_hex(),
path,
});
continue;
}
const voteDelegation = cert.as_vote_delegation();
if (voteDelegation != null) {
const wasmDrep = voteDelegation.drep();
let dRep;
switch (wasmDrep.kind()) {
case RustModule.WalletV4.DRepKind.KeyHash:
dRep = {
type: CardanoDRepType.KEY_HASH,
keyHash: forceNonNull(voteDelegation.drep().to_key_hash()).to_hex(),
};
break;
case RustModule.WalletV4.DRepKind.ScriptHash:
dRep = {
type: CardanoDRepType.KEY_HASH,
scriptHash: forceNonNull(voteDelegation.drep().to_script_hash()).to_hex(),
};
break;
case RustModule.WalletV4.DRepKind.AlwaysAbstain:
dRep = {
type: CardanoDRepType.ABSTAIN,
};
break;
case RustModule.WalletV4.DRepKind.AlwaysNoConfidence:
dRep = {
type: CardanoDRepType.NO_CONFIDENCE,
};
break;
default:
throw new Error('Trezor: Unsupported dRep kind: ' + wasmDrep.kind());
}
result.push({
type: CardanoCertificateType.VOTE_DELEGATION,
dRep,
path: getPath(voteDelegation.stake_credential()),
});
continue;
}
// TODO: @trezor/connect-web 9.4.2 hasn't supported dRep (de)registration and update
throw new Error(`${nameof(formatTrezorCertificates)} Trezor doesn't support this certificate type`);
}
return result;
Expand Down Expand Up @@ -708,37 +749,7 @@ export function toTrezorSignRequest(
}
return addressing;
};

const result = [];
for (const cert of iterateLenGet(certificates)) {
const registrationCert = cert.as_stake_registration();
if (registrationCert != null) {
result.push({
type: CardanoCertificateType.STAKE_REGISTRATION,
path: getPath(registrationCert.stake_credential()),
});
continue;
}
const deregistrationCert = cert.as_stake_deregistration();
if (deregistrationCert != null) {
result.push({
type: CardanoCertificateType.STAKE_DEREGISTRATION,
path: getPath(deregistrationCert.stake_credential()),
});
continue;
}
const delegationCert = cert.as_stake_delegation();
if (delegationCert != null) {
result.push({
type: CardanoCertificateType.STAKE_DELEGATION,
path: getPath(delegationCert.stake_credential()),
pool: delegationCert.pool_keyhash().to_hex(),
});
continue;
}
throw new Error(`unsupported certificate type`);
}
formattedCertificates = result;
formattedCertificates = formatTrezorCertificates(certificates, getPath);
}

let formattedWithdrawals = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import type { LayoutComponentMap } from '../../../styles/context/layout';
import type { StoresAndActionsProps } from '../../../types/injectedProps.types';
import type { $npm$ReactIntl$IntlFormat } from 'react-intl';
import { MultiToken } from '../../../api/common/lib/MultiToken';
import type { WalletType } from '../../../../chrome/extension/background/types';

// populated by ConfigWebpackPlugin
declare var CONFIG: ConfigType;
Expand Down Expand Up @@ -212,9 +211,7 @@ class StakingPageContent extends Component<AllProps> {
const delegatedUtxo = stores.delegation.getDelegatedUtxoBalance(publicDeriver.publicDeriverId);
const delegatedRewards = stores.delegation.getRewardBalanceOrZero(publicDeriver);

// <TODO:PENDING_REMOVAL> remove special check after governance for trezor is added
const isTrezor = (publicDeriver.type: WalletType) === 'trezor';
const isParticipatingToGovernance = stores.delegation.governanceStatus?.drepDelegation != null || isTrezor;
const isParticipatingToGovernance = stores.delegation.governanceStatus?.drepDelegation != null;

return (
<Box>
Expand Down
6 changes: 6 additions & 0 deletions packages/yoroi-extension/app/domain/TrezorLocalizedError.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ const messages = defineMessages({
defaultMessage:
'!!!Could not sign the transaction. Please ensure the passphrase you entered is the passhprase used to create this wallet.',
},
conwayNotSupportedByFirmwareError: {
id: 'wallet.send.trezor.error.conway.firmware',
defaultMessage: '!!!Conway features not supported by your Trezor firmware version. Please upgrade to 2.8.1 or above.',
},
});

/** Converts error(from API or Trezor API) to LocalizableError */
Expand All @@ -35,6 +39,8 @@ export function convertToLocalizableError(error: Error): LocalizableError {
localizableError = new LocalizableError(messages.noWitnessError);
} else if (/Cancelled/.test(error.message)) {
localizableError = new LocalizableError(messages.signTxError101);
} else if (/Feature Conway not supported by device firmware/.test(error.message)) {
localizableError = new LocalizableError(messages.conwayNotSupportedByFirmwareError);
} else {
// Trezor device related error happend, convert then to LocalizableError
switch (error.message) {
Expand Down
1 change: 1 addition & 0 deletions packages/yoroi-extension/app/i18n/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,7 @@
"wallet.send.trezor.confirmationDialog.info.line.2": "A new tab will appear. Please follow the instructions in the new tab.",
"wallet.send.trezor.confirmationDialog.submit": "Send using Trezor",
"wallet.send.trezor.error.101": "Signing cancelled on Trezor device. Please retry",
"wallet.send.trezor.error.conway.firmware": "Conway features not supported by your Trezor firmware version. Please upgrade to 2.8.1 or above.",
"wallet.send.trezor.error.noWitness": "Could not sign the transaction. Please ensure the passphrase you entered is the passhprase used to create this wallet.",
"wallet.settings.blockchain.explorer.title": "Explorer settings",
"wallet.settings.changePassword.dialog.currentPasswordFieldPlaceholder": "Type current password",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow

import { observable, action, reaction } from 'mobx';
import { observable, action, reaction, runInAction } from 'mobx';
import BigNumber from 'bignumber.js';
import Store from '../base/Store';
import LocalizedRequest from '../lib/LocalizedRequest';
Expand All @@ -27,6 +27,8 @@ export default class AdaDelegationTransactionStore extends Store<StoresMap, Acti
/** tracks if wallet balance changed during confirmation screen */
@observable isStale: boolean = false;

@observable error: ?Error = null;

// eslint-disable-next-line no-restricted-syntax
_updateTxBuilderReaction: void => mixed = reaction(
() => [
Expand Down Expand Up @@ -146,45 +148,51 @@ export default class AdaDelegationTransactionStore extends Store<StoresMap, Acti
this.stores.delegation.disablePoolTransitionState(request.wallet);
return this.stores.wallets.refreshWalletFromRemote(request.wallet.publicDeriverId);
};

if (request.wallet.type === 'ledger') {
await this.stores.substores.ada.wallets.adaSendAndRefresh({
broadcastRequest: {
ledger: {
signRequest: result.signTxRequest,
wallet: request.wallet,
try {
if (request.wallet.type === 'ledger') {
await this.stores.substores.ada.wallets.adaSendAndRefresh({
broadcastRequest: {
ledger: {
signRequest: result.signTxRequest,
wallet: request.wallet,
},
},
},
refreshWallet,
});
return;
}
if (request.wallet.type === 'trezor') {
refreshWallet,
});
return;
}
if (request.wallet.type === 'trezor') {
await this.stores.substores.ada.wallets.adaSendAndRefresh({
broadcastRequest: {
trezor: {
signRequest: result.signTxRequest,
wallet: request.wallet,
},
},
refreshWallet,
});
return;
}
// normal password-based wallet
if (request.password == null) {
throw new Error(`${nameof(this._signTransaction)} missing password for non-hardware signing`);
}
await this.stores.substores.ada.wallets.adaSendAndRefresh({
broadcastRequest: {
trezor: {
signRequest: result.signTxRequest,
normal: {
wallet: request.wallet,
password: request.password,
signRequest: result.signTxRequest,
},
},
refreshWallet,
});
return;
}
// normal password-based wallet
if (request.password == null) {
throw new Error(`${nameof(this._signTransaction)} missing password for non-hardware signing`);
} catch (error) {
runInAction(() => {
this.error = error;
});
throw error;
}
await this.stores.substores.ada.wallets.adaSendAndRefresh({
broadcastRequest: {
normal: {
wallet: request.wallet,
password: request.password,
signRequest: result.signTxRequest,
},
},
refreshWallet,
});
if (request.dialog) this.actions.dialogs.open.trigger({ dialog: request.dialog });
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export const allCategoriesRevamp: Array<SidebarCategoryRevamp> = [
route: '/governance',
icon: governanceIcon,
label: globalMessages.sidebarGovernance,
isVisible: ({ selected }) => selected != null && selected.type !== 'trezor',
isVisible: ({ selected }) => selected != null,
},
{
className: 'settings',
Expand Down
2 changes: 1 addition & 1 deletion packages/yoroi-extension/chrome/manifest-mv2.template.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export default ({
],
content_scripts: [
{
matches: ['*://connect.trezor.io/*/popup.html'],
matches: ['*://connect.trezor.io/*/popup.html*'],
js: ['js/trezor-content-script.js'],
},
],
Expand Down
2 changes: 1 addition & 1 deletion packages/yoroi-extension/chrome/manifest.template.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default ({
],
content_scripts: [
{
matches: ['*://connect.trezor.io/*/popup.html'],
matches: ['*://connect.trezor.io/*/popup.html*'],
js: ['js/trezor-content-script.js'],
},
],
Expand Down
Loading

0 comments on commit 0cc43ac

Please sign in to comment.