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

fix(extension): [lw-10782] add proper mappers for missed conway era certs #1234

Merged
merged 3 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const useWalletActivities = ({

const fetchWalletActivities = useCallback(async () => {
fiatCurrency &&
cardanoFiatPrice &&
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is a case when cardanoFiatPrice comes as undefined from useFetchCoinPrice
removing memoize util that is a hoc around mapWalletActivities also helps, but i'm not able to debug why resolver is not reevaluating the key correctly.

getWalletActivities({
fiatCurrency,
cardanoFiatPrice,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ import { inspectTxValues } from '@src/utils/tx-inspection';
import { firstValueFrom } from 'rxjs';
import { getAssetsInformation } from '@src/utils/get-assets-information';
import { MAX_POOLS_COUNT } from '@lace/staking';
import { ActivityStatus, DelegationActivityType, TransactionActivityType } from '@lace/core';
import {
ActivityStatus,
ConwayEraCertificatesTypes,
DelegationActivityType,
TransactionActivityType
} from '@lace/core';
import type { ActivityType } from '@lace/core';
import { formatDate, formatTime } from '@src/utils/format-date';
import { createHistoricalOwnInputResolver, HistoricalOwnInputResolverArgs } from '@src/utils/own-input-resolver';
Expand Down Expand Up @@ -68,10 +73,12 @@ const shouldIncludeFee = (

return !(
type === DelegationActivityType.delegationRegistration ||
type === ConwayEraCertificatesTypes.Registration ||
// Existence of any (new) delegationInfo means that this "de-registration"
// activity is accompanied by a "delegation" activity, which carries the fees.
// However, fees should be shown if de-registration activity is standalone.
(type === DelegationActivityType.delegationDeregistration && !!delegationInfo?.length)
((type === DelegationActivityType.delegationDeregistration || type === ConwayEraCertificatesTypes.Unregistration) &&
!!delegationInfo?.length)
);
};

Expand Down Expand Up @@ -213,14 +220,16 @@ const buildGetActivityDetail =
const deposit =
// since one tx can be split into two (delegation, registration) actions,
// ensure only the registration tx carries the deposit
implicitCoin.deposit && type === DelegationActivityType.delegationRegistration
implicitCoin.deposit &&
(type === DelegationActivityType.delegationRegistration || type === ConwayEraCertificatesTypes.Registration)
? Wallet.util.lovelacesToAdaString(implicitCoin.deposit.toString())
: undefined;
const depositReclaimValue = Wallet.util.calculateDepositReclaim(implicitCoin);
const depositReclaim =
// since one tx can be split into two (delegation, de-registration) actions,
// ensure only the de-registration tx carries the reclaimed deposit
depositReclaimValue && type === DelegationActivityType.delegationDeregistration
depositReclaimValue &&
(type === DelegationActivityType.delegationDeregistration || type === ConwayEraCertificatesTypes.Unregistration)
? Wallet.util.lovelacesToAdaString(depositReclaimValue.toString())
: undefined;
const feeInAda = Wallet.util.lovelacesToAdaString(tx.body.fee.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
ActivityStatus,
AssetActivityItemProps,
AssetActivityListProps,
ConwayEraCertificatesTypes,
DelegationActivityType,
TransactionActivityType
} from '@lace/core';
Expand Down Expand Up @@ -69,21 +70,34 @@ type MappedActivityListProps = Omit<AssetActivityListProps, 'items'> & {
};
export type FetchWalletActivitiesReturn = MappedActivityListProps[];

type extendedDelegationActivityType =
| DelegationActivityType
| ConwayEraCertificatesTypes.Registration
| ConwayEraCertificatesTypes.Unregistration;

type DelegationActivityItemProps = Omit<ExtendedActivityProps, 'type'> & {
type: DelegationActivityType;
type: extendedDelegationActivityType;
};

const isDelegationActivity = (activity: ExtendedActivityProps): activity is DelegationActivityItemProps =>
activity.type in DelegationActivityType;
activity.type in DelegationActivityType ||
activity.type === ConwayEraCertificatesTypes.Registration ||
activity.type === ConwayEraCertificatesTypes.Unregistration;

const getDelegationAmount = (activity: DelegationActivityItemProps) => {
const fee = new BigNumber(Number.parseFloat(activity.fee));

if (activity.type === DelegationActivityType.delegationRegistration) {
if (
activity.type === DelegationActivityType.delegationRegistration ||
activity.type === ConwayEraCertificatesTypes.Registration
) {
return fee.plus(activity.deposit);
}

if (activity.type === DelegationActivityType.delegationDeregistration) {
if (
activity.type === DelegationActivityType.delegationDeregistration ||
activity.type === ConwayEraCertificatesTypes.Unregistration
) {
return new BigNumber(activity.depositReclaim).minus(fee);
}

Expand Down Expand Up @@ -363,7 +377,7 @@ const mapWalletActivities = memoize(
{ cardanoFiatPrice, fiatCurrency, assetId },
{ cardanoCoin, assetDetails }
) =>
`${transactions.history.length}_${transactions.outgoing.inFlight.length}_${assetInfo.size}_${
`${transactions.history.length}_${transactions.outgoing.inFlight.map(({ id }) => id).join('')}_${assetInfo.size}_${
rewardsHistory.all.length
}_${cardanoFiatPrice}_${fiatCurrency.code}_${assetId || ''}_${cardanoCoin?.id}_${assetDetails?.id}_${
addresses[0]?.address
Expand Down
16 changes: 13 additions & 3 deletions apps/browser-extension-wallet/src/utils/tx-inspection.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable complexity */
/* eslint-disable consistent-return */
import {
createTxInspector,
Expand All @@ -8,6 +9,7 @@ import {
sentInspector,
totalAddressOutputsValueInspector
} from '@cardano-sdk/core';
import { certificateInspectorFactory } from '@src/features/dapp/components/confirm-transaction/utils';
import { Wallet } from '@lace/cardano';
import {
ActivityType,
Expand Down Expand Up @@ -54,7 +56,6 @@ const governanceCertificateInspection = (

switch (true) {
case signedCertificateTypenames.includes(CertificateType.RegisterDelegateRepresentative):
// TODO: can we map to Cip30TxType instead?
return ConwayEraCertificatesTypes.RegisterDelegateRepresentative;
case signedCertificateTypenames.includes(CertificateType.UnregisterDelegateRepresentative):
return ConwayEraCertificatesTypes.UnregisterDelegateRepresentative;
Expand Down Expand Up @@ -108,7 +109,7 @@ const getWalletAccounts = (walletAddresses: Wallet.KeyManagement.GroupedAddress[
{ paymentAddresses: [], rewardAccounts: [] }
);

const txIncludesConwayCertificates = (certificates?: Wallet.Cardano.Certificate[]) =>
export const txIncludesConwayCertificates = (certificates?: Wallet.Cardano.Certificate[]): boolean =>
certificates?.length > 0
? certificates.some((certificate) =>
Object.values(ConwayEraCertificatesTypes).includes(
Expand Down Expand Up @@ -158,11 +159,16 @@ export const inspectTxType = async ({
delegation: delegationInspector,
stakeKeyRegistration: stakeKeyRegistrationInspector,
stakeKeyDeregistration: stakeKeyDeregistrationInspector,
conwayEraStakeKeyRegistration: certificateInspectorFactory(CertificateType.Registration),
conwayEraStakeKeyDeregistration: certificateInspectorFactory(CertificateType.Unregistration),
selfTransaction: selfTxInspector(paymentAddresses)
})(tx);

if (txIncludesConwayCertificates(tx.body.certificates)) {
return governanceCertificateInspection(tx.body.certificates);
const inspection = governanceCertificateInspection(tx.body.certificates);
if (inspection) {
return inspection;
}
}

const withRewardsWithdrawal = isTxWithRewardsWithdrawal(
Expand All @@ -179,6 +185,10 @@ export const inspectTxType = async ({
return DelegationActivityType.delegationRegistration;
case inspectionProperties.stakeKeyDeregistration.length > 0:
return DelegationActivityType.delegationDeregistration;
case !!inspectionProperties.conwayEraStakeKeyRegistration:
return ConwayEraCertificatesTypes.Registration;
case !!inspectionProperties.conwayEraStakeKeyDeregistration:
return ConwayEraCertificatesTypes.Unregistration;
// Voting procedures take priority over proposals
// TODO: use proper inspector when available on sdk side (LW-9569)
case tx.body.votingProcedures?.length > 0:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import uniq from 'lodash/uniq';
import flatMap from 'lodash/flatMap';
import { Skeleton } from 'antd';
import { Wallet } from '@lace/cardano';
import type { ActivityType } from '@lace/core';
import {
ActivityType,
ConwayEraCertificatesTypes,
ActivityStatus,
AssetActivityListProps,
DelegationActivityType,
Expand Down Expand Up @@ -83,8 +84,10 @@ interface ActivityDetailProps {
}

const getTypeLabel = (type: ActivityType): TranslationKey => {
if (type === DelegationActivityType.delegationRegistration) return 'core.activityDetails.registration';
if (type === DelegationActivityType.delegationDeregistration) return 'core.activityDetails.deregistration';
if (type === DelegationActivityType.delegationRegistration || type === ConwayEraCertificatesTypes.Registration)
return 'core.activityDetails.registration';
if (type === DelegationActivityType.delegationDeregistration || type === ConwayEraCertificatesTypes.Unregistration)
return 'core.activityDetails.deregistration';
if (type === TransactionActivityType.incoming) return 'core.activityDetails.received';
if (type === TransactionActivityType.outgoing) return 'core.activityDetails.sent';
return `core.activityDetails.${type}`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
import BigNumber from 'bignumber.js';
import { Wallet } from '@lace/cardano';
import { CurrencyInfo, TxDirections } from '@types';
import { inspectTxValues, inspectTxType } from '@src/utils/tx-inspection';
import { inspectTxValues, inspectTxType, txIncludesConwayCertificates } from '@src/utils/tx-inspection';
import { formatDate, formatTime } from '@src/utils/format-date';
import { getTransactionTotalAmount } from '@src/utils/get-transaction-total-amount';
import type { TransformedActivity, TransformedTransactionActivity } from './types';
import { ActivityStatus, DelegationActivityType } from '@lace/core';
import { ActivityStatus, ConwayEraCertificatesTypes, DelegationActivityType } from '@lace/core';
import capitalize from 'lodash/capitalize';
import dayjs from 'dayjs';
import { hasPhase2ValidationFailed } from '@src/utils/phase2-validation';
Expand Down Expand Up @@ -43,7 +43,7 @@ export const getFormattedFiatAmount = ({
return fiatAmount ? `${fiatAmount} ${fiatCurrency.code}` : '-';
};

const splitDelegationTx = (tx: TransformedActivity): TransformedTransactionActivity[] => {
const splitDelegationTx = (tx: TransformedActivity, hasConwayEraCerts: boolean): TransformedTransactionActivity[] => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could consider to refactor that at some point, maybe to hoist that into tx-inspection, so it could return an array of types.
@mchappell wdyt?
maybe we could create a tech debt ticket?

if (tx.deposit) {
return [
{
Expand All @@ -54,7 +54,9 @@ const splitDelegationTx = (tx: TransformedActivity): TransformedTransactionActiv
},
{
...tx,
type: DelegationActivityType.delegationRegistration,
type: hasConwayEraCerts
? ConwayEraCertificatesTypes.Registration
: DelegationActivityType.delegationRegistration,
// Let registration show just the deposit,
// and the other transaction show fee to avoid duplicity
fee: '0'
Expand All @@ -70,7 +72,9 @@ const splitDelegationTx = (tx: TransformedActivity): TransformedTransactionActiv
},
{
...tx,
type: DelegationActivityType.delegationDeregistration,
type: hasConwayEraCerts
? ConwayEraCertificatesTypes.Unregistration
: DelegationActivityType.delegationDeregistration,
// Let de-registration show just the returned deposit,
// and the other transaction show fee to avoid duplicity
fee: '0'
Expand Down Expand Up @@ -239,7 +243,7 @@ export const txTransformer = async ({
});

if (type === DelegationActivityType.delegation) {
return splitDelegationTx(baseTransformedActivity);
return splitDelegationTx(baseTransformedActivity, txIncludesConwayCertificates(tx.body.certificates));
}

return [
Expand Down
22 changes: 18 additions & 4 deletions packages/core/src/ui/components/Activity/AssetActivityItem.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable complexity */
/* eslint-disable unicorn/consistent-function-scoping */
/* eslint-disable react/no-multi-comp */
import React, { useMemo, useRef, useEffect, useCallback } from 'react';
Expand All @@ -10,7 +11,7 @@ import { ReactComponent as PendingIcon } from '../../assets/icons/pending.compon
import { ReactComponent as ErrorIcon } from '../../assets/icons/error.component.svg';
import pluralize from 'pluralize';
import { txIconSize } from '@src/ui/utils/icon-size';
import { DelegationActivityType, TransactionActivityType } from '../ActivityDetail/types';
import { DelegationActivityType, TransactionActivityType, ConwayEraCertificatesTypes } from '../ActivityDetail/types';
import type { ActivityType } from '../ActivityDetail/types';
import styles from './AssetActivityItem.module.scss';
import { ActivityTypeIcon } from '../ActivityDetail/ActivityTypeIcon';
Expand Down Expand Up @@ -93,6 +94,7 @@ const ActivityStatusIcon = ({ status, type }: ActivityStatusIconProps) => {
const negativeBalanceStyling: Set<Partial<ActivityType>> = new Set([
TransactionActivityType.outgoing,
DelegationActivityType.delegationRegistration,
ConwayEraCertificatesTypes.Registration,
TransactionActivityType.self,
DelegationActivityType.delegation
]);
Expand All @@ -115,7 +117,13 @@ export const AssetActivityItem = ({

const getText = useCallback(
(items: number): { text: string; suffix: string } => {
if (type in DelegationActivityType || type === TransactionActivityType.self) return { text: amount, suffix: '' };
if (
type in DelegationActivityType ||
type === ConwayEraCertificatesTypes.Registration ||
type === ConwayEraCertificatesTypes.Unregistration ||
type === TransactionActivityType.self
)
return { text: amount, suffix: '' };

const assetsIdsText = assets
?.slice(0, items)
Expand Down Expand Up @@ -161,7 +169,9 @@ export const AssetActivityItem = ({
const assetsText = useMemo(() => getText(assetsToShow), [getText, assetsToShow]);

const assetAmountContent =
type in DelegationActivityType ? (
type in DelegationActivityType ||
type === ConwayEraCertificatesTypes.Registration ||
type === ConwayEraCertificatesTypes.Unregistration ? (
<p data-testid="tokens-amount" className={styles.description}>
{DELEGATION_ASSET_NUMBER} {t('core.assetActivityItem.entry.token')}
</p>
Expand Down Expand Up @@ -192,7 +202,11 @@ export const AssetActivityItem = ({
</div>
<div data-testid="asset-info" className={styles.info}>
<h6 data-testid="transaction-type" className={styles.title}>
{isPendingTx && type !== TransactionActivityType.self && !(type in DelegationActivityType)
{isPendingTx &&
type !== TransactionActivityType.self &&
!(type in DelegationActivityType) &&
type !== ConwayEraCertificatesTypes.Registration &&
type !== ConwayEraCertificatesTypes.Unregistration
? t('core.assetActivityItem.entry.name.sending')
: t(`core.assetActivityItem.entry.name.${type}` as unknown as CoreTranslationKey)}
</h6>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ const activityTypeIcon: Record<ActivityType, React.FC<React.SVGProps<SVGSVGEleme
[ConwayEraCertificatesTypes.RegisterDelegateRepresentative]: RegisterDelegateRepresentativeIcon,
[ConwayEraCertificatesTypes.UnregisterDelegateRepresentative]: UnregisterDelegateRepresentativeIcon,
[ConwayEraCertificatesTypes.VoteDelegation]: VoteDelegationIcon,
[ConwayEraCertificatesTypes.Registration]: ClipboardCheckOutlineIcon,
[ConwayEraCertificatesTypes.Unregistration]: ClipboardXOutlineComponentIcon,
[TransactionActivityType.rewards]: RewardsIcon,
[TransactionActivityType.incoming]: IncomingIcon,
[TransactionActivityType.outgoing]: OutgoingIcon,
Expand All @@ -73,10 +75,14 @@ export const ActivityTypeIcon = ({ type }: ActivityTypeIconProps): React.ReactEl
const icon = (type && activityTypeIcon[type]) || RefreshOutlinedIcon;
const iconStyle = { fontSize: txIconSize() };

const isDelegationActivity =
type === ConwayEraCertificatesTypes.Unregistration || type === ConwayEraCertificatesTypes.Registration;

const isGovernanceTx =
Object.values(ConwayEraCertificatesTypes).includes(type as unknown as ConwayEraCertificatesTypes) ||
type in ConwayEraGovernanceActions ||
type in Cip1694GovernanceActivityType;
!isDelegationActivity &&
(Object.values(ConwayEraCertificatesTypes).includes(type as unknown as ConwayEraCertificatesTypes) ||
type in ConwayEraGovernanceActions ||
type in Cip1694GovernanceActivityType);

return (
<Flex
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/ui/components/ActivityDetail/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Wallet } from '@lace/cardano';

// supported certificates actions
export enum ConwayEraCertificatesTypes {
'Registration' = Wallet.Cardano.CertificateType.Registration,
'Unregistration' = Wallet.Cardano.CertificateType.Unregistration,
'AuthorizeCommitteeHot' = Wallet.Cardano.CertificateType.AuthorizeCommitteeHot,
'RegisterDelegateRepresentative' = Wallet.Cardano.CertificateType.RegisterDelegateRepresentative,
'ResignCommitteeCold' = Wallet.Cardano.CertificateType.ResignCommitteeCold,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { ActivityStatus, AssetActivityListProps, DelegationActivityType } from '@lace/core';
import { ActivityStatus, AssetActivityListProps, ConwayEraCertificatesTypes, DelegationActivityType } from '@lace/core';
import flatMap from 'lodash/flatMap';

export const hasPendingDelegationTransaction = (walletActivities: AssetActivityListProps[]) =>
flatMap(walletActivities, ({ items }) => items).some(
({ type, status }) => type && type in DelegationActivityType && status === ActivityStatus.PENDING
({ type, status }) =>
type &&
(type in DelegationActivityType ||
type === ConwayEraCertificatesTypes.Registration ||
type === ConwayEraCertificatesTypes.Unregistration) &&
status === ActivityStatus.PENDING
);
2 changes: 2 additions & 0 deletions packages/translation/src/lib/translations/core/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@
"core.assetActivityItem.entry.name.delegation": "Delegation",
"core.assetActivityItem.entry.name.delegationDeregistration": "Stake Key De-Registration",
"core.assetActivityItem.entry.name.delegationRegistration": "Stake Key Registration",
"core.assetActivityItem.entry.name.UnRegistrationCertificate": "Stake Key De-Registration",
"core.assetActivityItem.entry.name.RegistrationCertificate": "Stake Key Registration",
"core.assetActivityItem.entry.name.HardForkInitiationAction": "Hard Fork Initiation Action",
"core.assetActivityItem.entry.name.incoming": "Received",
"core.assetActivityItem.entry.name.InfoAction": "Info Action",
Expand Down