Skip to content

Commit

Permalink
feat: fetch dreps
Browse files Browse the repository at this point in the history
feat: withdraw modal

chore(cardano): enable tagged sets in tx serialization

fix: drep value

feat: modal finalize

feat: messages

fix: review

feat: propagate functions

chore: try txs

feat: tx flow

feat: calculate fee

feat: fee

fix: pass misc

feat: fetch certs

fix: check

fix: certs

fix: split actions

feat: better modal flow

feat: sd

fix: tests

fix: tests

fix: texts
  • Loading branch information
vladimirvolek committed Dec 17, 2024
1 parent 560819f commit c28de20
Show file tree
Hide file tree
Showing 23 changed files with 398 additions and 56 deletions.
16 changes: 16 additions & 0 deletions packages/blockchain-link-types/src/blockfrost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,22 @@ export interface BlockfrostAccountInfo {
total: number;
index: number;
};
misc: {
staking: {
address: string;
isActive: boolean;
rewards: string;
poolId: string | null;
drep: {
drep_id: string;
hex: string;
amount: string;
active: boolean;
active_epoch: number | null;
has_script: boolean;
} | null;
};
};
}

export interface ParseAssetResult {
Expand Down
8 changes: 8 additions & 0 deletions packages/blockchain-link-types/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,14 @@ export interface AccountInfo {
isActive: boolean;
rewards: string;
poolId: string | null;
drep: {
drep_id: string;
hex: string;
amount: string;
active: boolean;
active_epoch: number | null;
has_script: boolean;
} | null;
};
// SOL
owner?: string; // The Solana program owning the account
Expand Down
60 changes: 42 additions & 18 deletions packages/suite/src/actions/wallet/cardanoStakingActions.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,34 @@
import { getUnixTime } from 'date-fns';

import { BlockchainBlock } from '@trezor/connect';
import { CARDANO_STAKE_POOL_PREVIEW_URL, CARDANO_STAKE_POOL_MAINNET_URL } from '@trezor/urls';
import {
CARDANO_STAKE_POOL_PREVIEW_URL,
CARDANO_STAKE_POOL_MAINNET_URL,
CARDANO_MAINNET_DREP,
CARDANO_PREVIEW_DREP,
} from '@trezor/urls';
import { isPending, getAccountTransactions } from '@suite-common/wallet-utils';
import { CARDANO_DEFAULT_TTL_OFFSET } from '@suite-common/wallet-constants';
import { transactionsActions } from '@suite-common/wallet-core';
import { getNetworkOptional } from '@suite-common/wallet-config';

import { CARDANO_STAKING } from 'src/actions/wallet/constants';
import { PendingStakeTx, PoolsResponse, CardanoNetwork } from 'src/types/wallet/cardanoStaking';
import {
PendingStakeTx,
PoolsResponse,
CardanoNetwork,
DRepResponse,
} from 'src/types/wallet/cardanoStaking';
import { Account, WalletAccountTransaction } from 'src/types/wallet';
import { Dispatch, GetState } from 'src/types/suite';

export type CardanoStakingAction =
| { type: typeof CARDANO_STAKING.ADD_PENDING_STAKE_TX; pendingStakeTx: PendingStakeTx }
| { type: typeof CARDANO_STAKING.REMOVE_PENDING_STAKE_TX; accountKey: string }
| {
type: typeof CARDANO_STAKING.SET_TREZOR_POOLS;
type: typeof CARDANO_STAKING.SET_TREZOR_DATA;
trezorPools: PoolsResponse;
trezorDRep: DRepResponse;
network: CardanoNetwork;
}
| { type: typeof CARDANO_STAKING.SET_FETCH_ERROR; error: boolean; network: CardanoNetwork }
Expand Down Expand Up @@ -102,7 +113,7 @@ export const validatePendingStakeTxOnTx =
}
};

export const fetchTrezorPools = (network: 'ADA' | 'tADA') => async (dispatch: Dispatch) => {
export const fetchTrezorData = (network: 'ADA' | 'tADA') => async (dispatch: Dispatch) => {
const cardanoNetwork = network === 'ADA' ? 'mainnet' : 'preview';

dispatch({
Expand All @@ -111,25 +122,38 @@ export const fetchTrezorPools = (network: 'ADA' | 'tADA') => async (dispatch: Di
network: cardanoNetwork,
});

// Fetch ID of Trezor stake pool that will be used in delegation transaction
const url =
cardanoNetwork === 'mainnet'
? CARDANO_STAKE_POOL_MAINNET_URL
: CARDANO_STAKE_POOL_PREVIEW_URL;

try {
const response = await fetch(url, { credentials: 'same-origin' });
const responseJson = await response.json();

if (!responseJson || !('next' in responseJson) || !('pools' in responseJson)) {
// todo: even if this happens, error will be overridden by this bug
// https://github.com/trezor/trezor-suite/issues/5485
// Fetch ID of Trezor stake pool that will be used in delegation transaction
const urlPools =
cardanoNetwork === 'mainnet'
? CARDANO_STAKE_POOL_MAINNET_URL
: CARDANO_STAKE_POOL_PREVIEW_URL;

const responsePools = await fetch(urlPools, { credentials: 'same-origin' });
const responsePoolsJson = await responsePools.json();

if (
!responsePoolsJson ||
!('next' in responsePoolsJson) ||
!('pools' in responsePoolsJson)
) {
throw new Error('Cardano: fetchTrezorPools: Invalid data format');
}

// Fetch DRep for transaction withdrawal
const urlDRep = cardanoNetwork === 'mainnet' ? CARDANO_MAINNET_DREP : CARDANO_PREVIEW_DREP;

const responseDRep = await fetch(urlDRep, { credentials: 'same-origin' });
const responseDRepJson = await responseDRep.json();

if (!responseDRepJson || !('drep' in responseDRepJson)) {
throw new Error('Cardano: fetchTrezorDRep: Invalid data format');
}

dispatch({
type: CARDANO_STAKING.SET_TREZOR_POOLS,
trezorPools: responseJson as PoolsResponse,
type: CARDANO_STAKING.SET_TREZOR_DATA,
trezorPools: responsePoolsJson as PoolsResponse,
trezorDRep: responseDRepJson as DRepResponse,
network: cardanoNetwork,
});
} catch {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export const ADD_PENDING_STAKE_TX = '@cardano-staking/set-pending-stake-tx';
export const REMOVE_PENDING_STAKE_TX = '@cardano-staking/remove-pending-stake-tx';
export const IS_LOADING = '@cardano-staking/is-loading';
export const SET_TREZOR_POOLS = '@cardano-staking/set-trezor-pools';
export const SET_TREZOR_DATA = '@cardano-staking/set-trezor-data';
export const SET_FETCH_LOADING = '@cardano-staking/set-fetch-loading';
export const SET_FETCH_ERROR = '@cardano-staking/set-fetch-error';
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import { FormattedMessage } from 'react-intl';

import styled from 'styled-components';

import { Button, H2, Icon, Link, NewModal } from '@trezor/components';
import { borders, typography } from '@trezor/theme';

import { useSelector } from 'src/hooks/suite/useSelector';
import { useCardanoStaking } from 'src/hooks/wallet/useCardanoStaking';

import { HiddenPlaceholder } from '../../HiddenPlaceholder';

// eslint-disable-next-line local-rules/no-override-ds-component
export const StyledH2 = styled(H2)`
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 5px;
`;

export const Row = styled.div`
display: flex;
width: 100%;
margin-top: 24px;
`;

export const Line = styled.div`
display: flex;
width: 100%;
`;

export const LeftPadding10 = styled.div`
padding-left: 10px;
`;

export const Column = styled.div`
display: flex;
flex-direction: column;
justify-content: space-between;
max-width: 100%;
`;

export const Title = styled.div`
display: flex;
color: ${({ theme }) => theme.legacy.TYPE_DARK_GREY};
${typography.highlight}
align-items: center;
margin-bottom: 16px;
`;

export const Actions = styled.div`
display: flex;
align-items: center;
width: 100%;
margin-top: 24px;
justify-content: center;
`;

export const Text = styled.div`
color: ${({ theme }) => theme.legacy.TYPE_LIGHT_GREY};
margin-bottom: 8px;
margin-top: 8px;
${typography.hint}
`;

export const Content = styled.div`
display: flex;
overflow: hidden;
padding-left: 10px;
margin-left: -10px;
width: 100%;
`;

export const Value = styled.div`
${typography.hint}
color: ${({ theme }) => theme.legacy.TYPE_DARK_GREY};
font-variant-numeric: tabular-nums slashed-zero;
width: fit-content;
background: ${({ theme }) => theme.legacy.BG_LIGHT_GREY};
border: 1px solid ${({ theme }) => theme.legacy.STROKE_GREY};
border-radius: ${borders.radii.xs};
word-break: break-all;
padding: 10px;
display: flex;
`;

type Props = {
onCancel: () => void;
};

export const CardanoWithdrawModal = ({ onCancel }: Props) => {
const { voteAbstain, voteDelegate } = useCardanoStaking();
const account = useSelector(state => state.wallet.selectedAccount.account);
if (!account || account.networkType !== 'cardano') {
throw Error('CardanoWithdrawModal used for other network');
}

const cardanoNetwork = account.symbol === 'ada' ? 'mainnet' : 'preview';
const { trezorDRep } = useSelector(state => state.wallet.cardanoStaking[cardanoNetwork]);
const trezorDRepBech32 = trezorDRep?.drep.bech32;

return (
<NewModal onCancel={onCancel}>
<StyledH2>
<FormattedMessage
id="TR_CARDANO_WITHDRAW_MODAL_TITLE"
defaultMessage="Withdraw your rewards"
/>
</StyledH2>
<Row>
<FormattedMessage
id="TR_CARDANO_WITHDRAW_MODAL_DESCRIPTION"
defaultMessage="When withdrawing your rewards, you can choose to support the Cardano ecosystem by delegating your community voting rights. This helps strengthen the network’s resilience, sustainability, and community-driven governance. If you prefer, you can easily opt out of governance."
/>
</Row>
<Row>
<Content>
<Column>
<Title>
<FormattedMessage
id="TR_CARDANO_WITHDRAW_MODAL_SUB_TITLE"
defaultMessage="Delegate Representative (DRep)"
/>
</Title>
<HiddenPlaceholder>
<Value>
{trezorDRepBech32}
<Link href={`https://gov.tools/drep_directory/${trezorDRepBech32}`}>
<LeftPadding10>
<Icon name="link" size={16} />
</LeftPadding10>
</Link>
</Value>
</HiddenPlaceholder>
</Column>
</Content>
</Row>
<Row>
<Button onClick={() => voteDelegate()}>
<FormattedMessage
id="TR_CARDANO_WITHDRAW_MODAL_BUTTON_DELEGATE"
defaultMessage="Delegate"
/>
</Button>
<LeftPadding10>
<Button onClick={() => voteAbstain()} variant="tertiary">
<FormattedMessage
id="TR_CARDANO_WITHDRAW_MODAL_BUTTON_ABSTAIN"
defaultMessage="Opt Out"
/>
</Button>
</LeftPadding10>
</Row>
</NewModal>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import type { ReduxModalProps } from '../ReduxModal';
import { EverstakeModal } from './UnstakeModal/EverstakeModal';
import { PassphraseMismatchModal } from './PassphraseMismatchModal';
import { FirmwareRevisionOptOutModal } from './FirmwareRevisionOptOutModal';
import { CardanoWithdrawModal } from '../CardanoWithdrawModal';

/** Modals opened as a result of user action */
export const UserContextModal = ({
Expand Down Expand Up @@ -105,6 +106,8 @@ export const UserContextModal = ({
);
case 'review-transaction':
return <TransactionReviewModal {...payload} />;
case 'cardano-withdraw-modal':
return <CardanoWithdrawModal onCancel={onCancel} />;
case 'coinmarket-buy-terms': {
return (
<CoinmarketTermsModal
Expand Down
Loading

0 comments on commit c28de20

Please sign in to comment.