Skip to content

Commit

Permalink
Merge pull request #571 from Concordium/x-send-funds
Browse files Browse the repository at this point in the history
Hook up SendFunds page
  • Loading branch information
soerenbf authored Nov 21, 2024
2 parents cf1e4f5 + 4068ba3 commit 94cdd5c
Show file tree
Hide file tree
Showing 27 changed files with 568 additions and 386 deletions.
13 changes: 5 additions & 8 deletions packages/browser-wallet/src/popup/popupX/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,8 @@ export const relativeRoutes = {
hideBackArrow: true,
showAccountSelector: true,
},
send: {
path: 'send',
confirmation: {
path: 'confirmation',
config: {
backTitle: 'to Send Funds form',
},
},
sendFunds: {
path: 'account/:account/send-funds',
},
receive: {
path: 'receive',
Expand Down Expand Up @@ -347,6 +341,9 @@ export const transactionDetailsRoute = (account: AccountAddress.Type, tx: Transa
export const submittedTransactionRoute = (tx: TransactionHash.Type) =>
generatePath(absoluteRoutes.home.submittedTransaction.path, { transactionHash: TransactionHash.toHexString(tx) });

export const sendFundsRoute = (account: AccountAddress.Type) =>
generatePath(absoluteRoutes.home.sendFunds.path, { account: account.address });

/**
* Given two absolute routes, returns the relative route between them.
* Note: fromPath should be a prefix of toPath.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default function DelegationResult() {
};
const nav = useNavigate();
const { t } = useTranslation('x', { keyPrefix: 'earn.delegator' });
const getCost = useGetTransactionFee(AccountTransactionType.ConfigureDelegation);
const getCost = useGetTransactionFee();
const accountInfo = ensureDefined(useSelectedAccountInfo(), 'No account selected');

const parametersV1 = useBlockChainParametersAboveV0();
Expand Down Expand Up @@ -75,7 +75,7 @@ export default function DelegationResult() {
return <Navigate to=".." />;
}

const fee = getCost(state.payload);
const fee = getCost(AccountTransactionType.ConfigureDelegation, state.payload);
const submit = async () => {
if (fee === undefined) {
throw Error('Fee could not be calculated');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default function DelegatorStake({ title, target, initialValues, existingV
const [highStakeWarning, setHighStakeWarning] = useState(false);

const values = form.watch();
const getCost = useGetTransactionFee(AccountTransactionType.ConfigureDelegation);
const getCost = useGetTransactionFee();
const fee = useMemo(() => {
let payload: ConfigureDelegationPayload;
try {
Expand All @@ -131,7 +131,7 @@ export default function DelegatorStake({ title, target, initialValues, existingV
existingValues
);
}
return getCost(payload);
return getCost(AccountTransactionType.ConfigureDelegation, payload);
}, [target, values, getCost]);

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export type DelegationTypeForm = {
};

/** The form values for delegator stake configuration step */
export type DelegatorStakeForm = AmountForm & {
export type DelegatorStakeForm = Omit<AmountForm, 'token'> & {
/** Whether to add rewards to the stake or not */
redelegate: boolean;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export default function ValidationResult() {
};
const nav = useNavigate();
const { t } = useTranslation('x', { keyPrefix: 'earn.validator' });
const getCost = useGetTransactionFee(AccountTransactionType.ConfigureBaker);
const getCost = useGetTransactionFee();
const accountInfo = ensureDefined(useSelectedAccountInfo(), 'No account selected');
const [error, setError] = useState<Error>();

Expand Down Expand Up @@ -84,7 +84,7 @@ export default function ValidationResult() {
return null;
}

const fee = getCost(state.payload);
const fee = getCost(AccountTransactionType.ConfigureBaker, state.payload);
const submit = async () => {
if (fee === undefined) {
throw Error('Fee could not be calculated');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ export default function ValidatorStake({ title, initialValues, existingValues, o
const [highStakeWarning, setHighStakeWarning] = useState(false);

const values = form.watch();
const getCost = useGetTransactionFee(AccountTransactionType.ConfigureBaker);
const getCost = useGetTransactionFee();
const fee = useMemo(() => {
if (existingValues === undefined) {
return getCost(PAYLOAD_MAX);
return getCost(AccountTransactionType.ConfigureBaker, PAYLOAD_MAX);
}

try {
Expand All @@ -106,7 +106,7 @@ export default function ValidatorStake({ title, initialValues, existingValues, o
}

const payload: ConfigureBakerPayload = { stake, restakeEarnings: restake };
return getCost(payload);
return getCost(AccountTransactionType.ConfigureBaker, payload);
} catch {
// We failed to parse the amount
return undefined;
Expand All @@ -118,7 +118,7 @@ export default function ValidatorStake({ title, initialValues, existingValues, o
return undefined; // We know the cost as we don't depend on values set later in the flow
}

return getCost(PAYLOAD_MIN);
return getCost(AccountTransactionType.ConfigureBaker, PAYLOAD_MIN);
}, [getCost, existingValues]);

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
OpenStatus,
OpenStatusText,
} from '@concordium/web-sdk';
import { AmountForm } from '@popup/popupX/shared/Form/TokenAmount';
import { formatCcdAmount, parseCcdAmount } from '@popup/popupX/shared/utils/helpers';
import i18n from '@popup/shell/i18n';

Expand Down Expand Up @@ -45,8 +46,7 @@ export function showCommissionRate(fraction: number): string {
export const isRange = (range: CommissionRange) => range.min !== range.max;

/** The form data for specifying validator stake */
export type ValidatorStakeForm = { amount: string; restake: boolean };

export type ValidatorStakeForm = Omit<AmountForm, 'token'> & { restake: boolean };
export type ValidatorFormUpdateStake = { stake: ValidatorStakeForm };
export type ValidatorFormUpdateKeys = { keys: GenerateBakerKeysOutput };
export type ValidatorFormUpdateSettings = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { ReactNode, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { generatePath, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useAtomValue } from 'jotai';
import { displayAsCcd } from 'wallet-common-helpers';
Expand Down Expand Up @@ -118,7 +118,7 @@ function MainPageConfirmedAccount({ credential }: MainPageConfirmedAccountProps)
const { t } = useTranslation('x', { keyPrefix: 'mainPage' });

const nav = useNavigate();
const navToSend = () => nav(relativeRoutes.home.send.path);
const navToSend = () => nav(generatePath(absoluteRoutes.home.sendFunds.path, { account: credential.address }));
const navToReceive = () => nav(relativeRoutes.home.receive.path);
const navToTransactionLog = () =>
nav(relativeRoutes.home.transactionLog.path.replace(':account', credential.address));
Expand Down
145 changes: 145 additions & 0 deletions packages/browser-wallet/src/popup/popupX/pages/SendFunds/Confirm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import {
AccountAddress,
AccountTransactionType,
CIS2,
CIS2Contract,
CcdAmount,
Energy,
SimpleTransferPayload,
TransactionHash,
} from '@concordium/web-sdk';
import { useAsyncMemo } from 'wallet-common-helpers';
import { useAtomValue } from 'jotai';
import { useNavigate } from 'react-router-dom';

import Page from '@popup/popupX/shared/Page';
import Text from '@popup/popupX/shared/Text';
import Arrow from '@assets/svgX/arrow-right.svg';
import Card from '@popup/popupX/shared/Card';
import {
displayNameAndSplitAddress,
displaySplitAddress,
useSelectedCredential,
} from '@popup/shared/utils/account-helpers';
import { AmountReceiveForm } from '@popup/popupX/shared/Form/TokenAmount/View';
import { ensureDefined } from '@shared/utils/basic-helpers';
import { CCD_METADATA } from '@shared/constants/token-metadata';
import { formatCcdAmount, parseCcdAmount, parseTokenAmount } from '@popup/popupX/shared/utils/helpers';
import { useTransactionSubmit } from '@popup/shared/utils/transaction-helpers';
import Button from '@popup/popupX/shared/Button';
import { grpcClientAtom } from '@popup/store/settings';
import { logError } from '@shared/utils/log-helpers';
import { submittedTransactionRoute } from '@popup/popupX/constants/routes';

import { CIS2_TRANSFER_NRG_OFFSET, showToken, useTokenMetadata } from './util';
import { UpdateContractSubmittedLocationState } from '../SubmittedTransaction/SubmittedTransaction';

type Props = {
sender: AccountAddress.Type;
values: AmountReceiveForm;
fee: CcdAmount.Type;
};

export default function SendFundsConfirm({ values, fee, sender }: Props) {
const { t } = useTranslation('x', { keyPrefix: 'sendFunds' });
const credential = ensureDefined(useSelectedCredential(), 'Expected selected account to be available');
const tokenMetadata = useTokenMetadata(values.token, sender);
const nav = useNavigate();
const tokenName = useMemo(() => {
if (values.token.tokenType === 'ccd') return CCD_METADATA.name;
if (tokenMetadata === undefined || values.token.tokenType === undefined) return undefined;

return showToken(tokenMetadata, values.token.tokenAddress);
}, [tokenMetadata, values.token]);
const receiver = AccountAddress.fromBase58(values.receiver);
const submitTransaction = useTransactionSubmit(
sender,
values.token.tokenType === 'ccd' ? AccountTransactionType.Transfer : AccountTransactionType.Update
);
const grpcClient = useAtomValue(grpcClientAtom);
const contractClient = useAsyncMemo(
async () => {
if (values.token.tokenType !== 'cis2') {
return undefined;
}
return CIS2Contract.create(grpcClient, values.token.tokenAddress.contract);
},
logError,
[values.token, grpcClient]
);

const payload = useAsyncMemo(
async () => {
if (values.token.tokenType === 'cis2') {
if (contractClient === undefined) return undefined; // We wait for the client to be ready
if (tokenMetadata === undefined) throw new Error('No metadata for token');

const transfer: CIS2.Transfer = {
from: sender,
to: receiver,
tokenId: values.token.tokenAddress.id,
tokenAmount: parseTokenAmount(values.amount, tokenMetadata?.decimals),
};
const result = await contractClient.dryRun.transfer(sender, transfer);
return contractClient.createTransfer(
{ energy: Energy.create(result.usedEnergy.value + CIS2_TRANSFER_NRG_OFFSET) },
transfer
).payload;
}
if (values.token.tokenType === 'ccd') {
const p: SimpleTransferPayload = {
amount: parseCcdAmount(values.amount),
toAddress: receiver,
};
return p;
}

return undefined;
},
logError,
[values.token, sender, values.receiver, contractClient]
);

const submit = async () => {
if (payload === undefined || tokenName === undefined) {
throw Error('Payload could not be created...');
}

const tx = await submitTransaction(payload, fee);
const state: UpdateContractSubmittedLocationState = { type: 'cis2.transfer', amount: values.amount, tokenName };
nav(submittedTransactionRoute(TransactionHash.fromHexString(tx)), {
state,
});
};

return (
<Page className="send-funds-container">
<Page.Top heading={t('confirmation.title')} />

<Card className="send-funds-confirm__card" type="transparent">
<div className="send-funds-confirm__card_destination">
<Text.MainMedium>{displayNameAndSplitAddress(credential)}</Text.MainMedium>
<Arrow />
<Text.MainMedium>{displaySplitAddress(values.receiver)}</Text.MainMedium>
</div>
<Text.Capture>
{t('amount')} ({tokenName}
):
</Text.Capture>
<Text.HeadingLarge>{values.amount}</Text.HeadingLarge>
<Text.Capture>{t('estimatedFee', { fee: formatCcdAmount(fee) })}</Text.Capture>
</Card>

<Page.Footer>
<Button.Main
className="button-main"
onClick={submit}
label={t('sendFunds')}
disabled={payload === undefined}
/>
</Page.Footer>
</Page>
);
}

This file was deleted.

Loading

0 comments on commit 94cdd5c

Please sign in to comment.