Skip to content

Commit

Permalink
Merge pull request #3745 from Emurgo/ruslan/hw-drep-inform
Browse files Browse the repository at this point in the history
informing about pre-cip129 drep id on hardware devices
  • Loading branch information
vsubhuman authored Nov 19, 2024
2 parents 773cb23 + 9b528ab commit 6570183
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,18 @@ const AccordionDetails = styled(MuiAccordionDetails)(({ theme }: any) => ({
type Props = {
title: string;
content: React.ReactNode;
expanded?: boolean,
};

export const Collapsible = ({ title, content }: Props) => {
const [expanded, setExpanded] = React.useState<any>('none');
export const Collapsible = ({ title, content, expanded: startExpanded }: Props) => {
const [expanded, setExpanded] = React.useState<boolean>(startExpanded ?? false);

const handleChange = (panel: string | false) => (_: React.SyntheticEvent, newExpanded: boolean) => {
setExpanded(newExpanded ? panel : false);
const handleChange = (_: React.SyntheticEvent, newExpanded: boolean) => {
setExpanded(newExpanded);
};

return (
<Accordion disableGutters elevation={0} square expanded={expanded === 'panel1'} onChange={handleChange('panel1')}>
<Accordion disableGutters elevation={0} square expanded={expanded} onChange={handleChange}>
<AccordionSummary
aria-controls="panel1d-content"
id="panel1d-header"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { genFormatTokenAmount, genLookupOrFail } from '../../../../../stores/sta
import { Collapsible } from '../../../../components/Collapsible/Collapsible';
import { PasswordInput } from '../../../../components/Input/PasswordInput';
import { DREP_ALWAYS_ABSTAIN, DREP_ALWAYS_NO_CONFIDENCE } from '../../common/constants';
import { dRepNormalize } from '../../../../../api/ada/lib/cardanoCrypto/utils';
import { dRepNormalize, dRepToPreCip129 } from '../../../../../api/ada/lib/cardanoCrypto/utils';
import { useNavigateTo } from '../../common/useNavigateTo';
import { useStrings } from '../../common/useStrings';
import { useGovernance } from '../../module/GovernanceContextProvider';
Expand Down Expand Up @@ -76,9 +76,7 @@ export const DelagationForm = () => {
const strings = useStrings();
const confirmDelegation = async () => {
const response = await checkUserPassword(password);
if (isHardwareWallet) {
signGovernanceTx();
} else if (response?.name === 'WrongPassphraseError') {
if (response?.name === 'WrongPassphraseError') {
setIsIncorectPassword(true);
} else {
signGovernanceTx();
Expand Down Expand Up @@ -115,6 +113,7 @@ export const DelagationForm = () => {

const specifiedDrep = governanceVote.drepID;
const normalizedDrep = maybe(specifiedDrep, dRepNormalize);
const preCip129Drep = maybe(specifiedDrep, dRepToPreCip129);

return (
<Container>
Expand Down Expand Up @@ -143,6 +142,7 @@ export const DelagationForm = () => {
</Typography>
<Box mb="40px">
<Collapsible
expanded={isHardwareWallet}
title={strings.operations}
content={
<TransactionDetails>
Expand All @@ -157,6 +157,11 @@ export const DelagationForm = () => {
Specified as: {specifiedDrep}
</Typography>
) : null}
{isHardwareWallet && (preCip129Drep !== specifiedDrep) ? (
<Typography variant="body1" color="ds.text_gray_medium">
On a Hardware device this DRep ID might be displayed in old format: {preCip129Drep}
</Typography>
) : null}
</>
)} fee={txFee} />
)}
Expand Down
39 changes: 26 additions & 13 deletions packages/yoroi-extension/app/api/ada/lib/cardanoCrypto/utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow

import { RustModule } from './rustLoader';
import { bytesToHex, forceNonNull, hexToBytes, maybe } from '../../../../coreUtils';
import { bytesToHex, fail, forceNonNull, hexToBytes, maybe } from '../../../../coreUtils';
import { base32ToHex, hexToBase32 } from '../storage/bridge/utils';

export function v4PublicToV2(
Expand Down Expand Up @@ -86,6 +86,21 @@ export function dRepToMaybeCredentialHex(s: string): ?string {
})
}

function parseDrep(drep: string): ?{| hash: string, isScript: boolean |} {
const credentialHex = dRepToMaybeCredentialHex(drep);
if (!credentialHex) {
return null;
}
return RustModule.WasmScope(Module => {
const cred = Module.WalletV4.Credential.from_hex(credentialHex);
const isScript = cred.kind() === Module.WalletV4.CredKind.Script;
const hash = isScript ?
forceNonNull(cred.to_scripthash()).to_hex()
: forceNonNull(cred.to_keyhash()).to_hex();
return { hash, isScript };
})
}

export function dRepNormalize(drep: string, kind?: string): string {
function encodeDrepHash(hash: string, isScript: boolean): string {
// cip129 prefix
Expand All @@ -100,19 +115,17 @@ export function dRepNormalize(drep: string, kind?: string): string {
// drep already cip129
return drep;
}
// parse drep
const credentialHex = dRepToMaybeCredentialHex(drep);
if (!credentialHex) {
throw new Error('Failed to normalize drep: ' + drep + ' | kind: ' + String(kind));
return maybe(parseDrep(drep), r => encodeDrepHash(r.hash, r.isScript))
?? fail('Failed to normalize drep: ' + drep + ' | kind: ' + String(kind));
}

export function dRepToPreCip129(drep: string): string {
if ((drep.startsWith('drep1') && drep.length < 58) || drep.startsWith('drep_script1')) {
// drep already pre cip129 compatible
return drep;
}
return RustModule.WasmScope(Module => {
const cred = Module.WalletV4.Credential.from_hex(credentialHex);
const isScript = cred.kind() === Module.WalletV4.CredKind.Script;
const hash = isScript ?
forceNonNull(cred.to_scripthash()).to_hex()
: forceNonNull(cred.to_keyhash()).to_hex();
return encodeDrepHash(hash, isScript);
})
return maybe(parseDrep(drep), r => hexToBase32(r.hash, r.isScript ? 'drep_script' : 'drep'))
?? fail('Failed to normalize drep: ' + drep);
}

export function pubKeyHashToRewardAddress(hex: string, network: number): string {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,21 @@ type InjectedLayoutProps = {|
+renderLayoutComponent: LayoutComponentMap => Node,
|};

type State = {|
govStatusFetched: boolean;
|}

type AllProps = {| ...StoresAndActionsProps, ...InjectedLayoutProps |};
@observer
class StakingPageContent extends Component<AllProps> {
class StakingPageContent extends Component<AllProps, State> {
static contextTypes: {| intl: $npm$ReactIntl$IntlFormat |} = {
intl: intlShape.isRequired,
};

state: State = {
govStatusFetched: false,
};

onClose: void => void = () => {
this.props.actions.dialogs.closeActiveDialog.trigger();
};
Expand All @@ -55,10 +63,14 @@ class StakingPageContent extends Component<AllProps> {
if (wallet == null) {
throw new Error(`${nameof(StakingPageContent)} no public deriver. Should never happen`);
}
// Check governance only for certain network
if (wallet.type !== 'trezor') {
noop(this.props.stores.delegation.checkGovernanceStatus(wallet));
}
this.props.stores.delegation.checkGovernanceStatus(wallet).then(() => {
this.setState({
govStatusFetched: true,
});
return null;
}).catch(e => {
console.error('Failed to fetch governance status', e);
});
if (this.props.stores.delegation.getPoolTransitionConfig(wallet).shouldUpdatePool) {
const poolTransitionInfo = this.props.stores.delegation.getPoolTransitionInfo(wallet);
if (poolTransitionInfo?.suggestedPool) {
Expand Down Expand Up @@ -213,6 +225,16 @@ class StakingPageContent extends Component<AllProps> {

const isParticipatingToGovernance = stores.delegation.governanceStatus?.drepDelegation != null;

const handleRewardsWithdrawal = async () => {
if (!isParticipatingToGovernance) {
this.props.actions.dialogs.open.trigger({
dialog: GovernanceParticipateDialog,
});
return;
}
this.createWithdrawalTx(false) // shouldDeregister=false
};

return (
<Box>
{isWalletWithNoFunds ? (
Expand All @@ -227,17 +249,7 @@ class StakingPageContent extends Component<AllProps> {
dialog: OverviewModal,
})
}
withdrawRewards={
isParticipatingToGovernance === false
? async () => {
this.props.actions.dialogs.open.trigger({
dialog: GovernanceParticipateDialog,
});
}
: isStakeRegistered
? async () => this.createWithdrawalTx(false) // shouldDeregister=false
: undefined
}
withdrawRewards={isStakeRegistered && this.state.govStatusFetched ? handleRewardsWithdrawal : undefined}
unitOfAccount={this.toUnitOfAccount}
getTokenInfo={genLookupOrFail(stores.tokenInfoStore.tokenInfo)}
shouldHideBalance={stores.profile.shouldHideBalance}
Expand Down

0 comments on commit 6570183

Please sign in to comment.