Skip to content

Commit

Permalink
feat: withdraw modal
Browse files Browse the repository at this point in the history
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
  • Loading branch information
vladimirvolek committed Dec 16, 2024
1 parent ff71c26 commit 0b87bc2
Show file tree
Hide file tree
Showing 16 changed files with 446 additions and 54 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
41 changes: 19 additions & 22 deletions packages/suite/src/actions/wallet/cardanoStakingActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { BlockchainBlock } from '@trezor/connect';
import {
CARDANO_STAKE_POOL_PREVIEW_URL,
CARDANO_STAKE_POOL_MAINNET_URL,
CARDANO_MAINNET_DREPS,
CARDANO_PREVIEW_DREPS,
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';
Expand All @@ -17,7 +17,7 @@ import {
PendingStakeTx,
PoolsResponse,
CardanoNetwork,
DrepsResponse,
DRepResponse,
} from 'src/types/wallet/cardanoStaking';
import { Account, WalletAccountTransaction } from 'src/types/wallet';
import { Dispatch, GetState } from 'src/types/suite';
Expand All @@ -28,6 +28,7 @@ export type CardanoStakingAction =
| {
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 @@ -121,42 +122,38 @@ export const fetchTrezorData = (network: 'ADA' | 'tADA') => async (dispatch: Dis
network: cardanoNetwork,
});

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

// Fetch dreps for transaction withdrawal
const url_dreps = cardanoNetwork === 'mainnet' ? CARDANO_MAINNET_DREPS : CARDANO_PREVIEW_DREPS;

try {
const responsePools = await fetch(url_pools, { credentials: 'same-origin' });
// 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)
) {
// todo: even if this happens, error will be overridden by this bug
// https://github.com/trezor/trezor-suite/issues/5485
throw new Error('Cardano: fetchTrezorPools: Invalid data format');
}

const responseDreps = await fetch(url_dreps, { credentials: 'same-origin' });
const responseDrepsJson = await responseDreps.json();
// 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 (!responseDrepsJson || !('drep' in responseDrepsJson)) {
// todo: even if this happens, error will be overridden by this bug
// https://github.com/trezor/trezor-suite/issues/5485
throw new Error('Cardano: fetchTrezorDreps: Invalid data format');
if (!responseDRepJson || !('drep' in responseDRepJson)) {
throw new Error('Cardano: fetchTrezorDRep: Invalid data format');
}

dispatch({
type: CARDANO_STAKING.SET_TREZOR_DATA,
trezorPools: responsePoolsJson as PoolsResponse,
trezorDreps: responseDrepsJson as DrepsResponse,
trezorDRep: responseDRepJson as DRepResponse,
network: cardanoNetwork,
});
} catch {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
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 { 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;
withdrawAndAbstain: (trezorDRepHex?: string, accountDRepHex?: string) => void;
withdrawAndDelegate: (trezorDRepHex?: string, accountDRepHex?: string) => void;
};

export const CardanoWithdrawModal = ({
onCancel,
withdrawAndAbstain,
withdrawAndDelegate,
}: Props) => {
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;
const trezorDRepHex = trezorDRep?.drep.hex;
const accountDRepHex = account.misc.staking.drep?.hex;

return (
<NewModal onCancel={onCancel}>
<StyledH2>
<FormattedMessage
id="TR_CARDANO_WITHDRAW_MODAL_TITLE"
defaultMessage="Rewards withdrawal"
/>
</StyledH2>
<Row>
<FormattedMessage
id="TR_CARDANO_WITHDRAW_MODAL_DESCRIPTION"
defaultMessage="To withdraw your rewards, you must participate in on-chain governance within the
Cardano ecosystem. Consider choosing a delegate representative who aligns with our
values, or opt out of governance entirely. We recommend selecting a delegate
representative endorsed by Trezor."
/>
</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={() => withdrawAndDelegate(trezorDRepHex, accountDRepHex)}>
<FormattedMessage
id="TR_CARDANO_WITHDRAW_MODAL_BUTTON_DELEGATE"
defaultMessage="Withdraw And Delegate"
/>
</Button>
<LeftPadding10>
<Button
onClick={() => withdrawAndAbstain(trezorDRepHex, accountDRepHex)}
variant="tertiary"
>
<FormattedMessage
id="TR_CARDANO_WITHDRAW_MODAL_BUTTON_ABSTAIN"
defaultMessage="Withdraw And Abstain"
/>
</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,14 @@ export const UserContextModal = ({
);
case 'review-transaction':
return <TransactionReviewModal {...payload} />;
case 'cardano-withdraw-modal':
return (
<CardanoWithdrawModal
onCancel={onCancel}
withdrawAndAbstain={payload.withdrawAndAbstain}
withdrawAndDelegate={payload.withdrawAndDelegate}
/>
);
case 'coinmarket-buy-terms': {
return (
<CoinmarketTermsModal
Expand Down
Loading

0 comments on commit 0b87bc2

Please sign in to comment.