Skip to content

Commit

Permalink
Merge pull request #563 from Concordium/ui-update/validation-transaction
Browse files Browse the repository at this point in the history
UI update/validation transaction
  • Loading branch information
soerenbf authored Nov 8, 2024
2 parents 7b179be + 4ea799e commit 525344a
Show file tree
Hide file tree
Showing 42 changed files with 1,661 additions and 341 deletions.
17 changes: 12 additions & 5 deletions packages/browser-wallet/src/popup/popupX/constants/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,19 @@ export const relativeRoutes = {
/** Configure existing delegator */
update: {
path: 'update',
/** Update validator stake */
stake: { path: 'stake' },
/** Update validator pool settings */
settings: { path: 'settings' },
/** Update validator keys */
keys: { path: 'keys' },
},
openPool: {
path: 'openPool',
},
keys: {
path: 'keys',
/** Submit configure validator transaction */
submit: {
path: 'submit',
config: {
backTitle: i18n.t('x:earn.validator.submit.backTitle'),
},
},
},
/** Delegation section */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import React, { useCallback, useMemo } from 'react';
import React, { useMemo } from 'react';
import {
AccountAddress,
AccountInfoDelegator,
AccountTransactionPayload,
AccountTransactionType,
CcdAmount,
ConfigureDelegationPayload,
DelegationTargetType,
TransactionHash,
} from '@concordium/web-sdk';
import { Navigate, useLocation, Location, useNavigate } from 'react-router-dom';
import { useAtomValue } from 'jotai';
import { useTranslation } from 'react-i18next';
import { useUpdateAtom } from 'jotai/utils';

import Button from '@popup/popupX/shared/Button';
import Page from '@popup/popupX/shared/Page';
Expand All @@ -21,70 +16,12 @@ import Card from '@popup/popupX/shared/Card';
import { ensureDefined } from '@shared/utils/basic-helpers';
import { useBlockChainParametersAboveV0 } from '@popup/shared/BlockChainParametersProvider';
import { secondsToDaysRoundedDown } from '@shared/utils/time-helpers';
import { grpcClientAtom } from '@popup/store/settings';
import { usePrivateKey } from '@popup/shared/utils/account-helpers';
import {
createPendingTransactionFromAccountTransaction,
getDefaultExpiry,
getTransactionAmount,
sendTransaction,
useGetTransactionFee,
} from '@popup/shared/utils/transaction-helpers';
import { addPendingTransactionAtom } from '@popup/store/transactions';
import { useGetTransactionFee, useTransactionSubmit } from '@popup/shared/utils/transaction-helpers';
import { cpStakingCooldown } from '@shared/utils/chain-parameters-helpers';
import { submittedTransactionRoute } from '@popup/popupX/constants/routes';
import Text from '@popup/popupX/shared/Text';
import { useSelectedAccountInfo } from '@popup/shared/AccountInfoListenerContext/AccountInfoListenerContext';

enum TransactionSubmitErrorType {
InsufficientFunds = 'InsufficientFunds',
}

class TransactionSubmitError extends Error {
private constructor(public type: TransactionSubmitErrorType) {
super();
super.name = `TransactionSubmitError.${type}`;
}

public static insufficientFunds(): TransactionSubmitError {
return new TransactionSubmitError(TransactionSubmitErrorType.InsufficientFunds);
}
}

function useTransactionSubmit(sender: AccountAddress.Type, type: AccountTransactionType) {
const grpc = useAtomValue(grpcClientAtom);
const key = usePrivateKey(sender.address);
const addPendingTransaction = useUpdateAtom(addPendingTransactionAtom);

return useCallback(
async (payload: AccountTransactionPayload, cost: CcdAmount.Type) => {
const accountInfo = await grpc.getAccountInfo(sender);
if (
accountInfo.accountAvailableBalance.microCcdAmount <
getTransactionAmount(type, payload) + (cost.microCcdAmount || 0n)
) {
throw TransactionSubmitError.insufficientFunds();
}

const nonce = await grpc.getNextAccountNonce(sender);

const header = {
expiry: getDefaultExpiry(),
sender,
nonce: nonce.nonce,
};
const transaction = { payload, header, type };

const hash = await sendTransaction(grpc, transaction, key!);
const pending = createPendingTransactionFromAccountTransaction(transaction, hash, cost.microCcdAmount);
await addPendingTransaction(pending);

return hash;
},
[key]
);
}

export type DelegationResultLocationState = {
payload: ConfigureDelegationPayload;
type: 'register' | 'change' | 'remove';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.validator-commissions {
.page__top {
margin-bottom: 0 !important;
}

&,
&__form {
gap: rem(16px);
}

&__form {
display: flex;
flex-direction: column;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import React, { useCallback, useMemo } from 'react';
import Page from '@popup/popupX/shared/Page';
import { useTranslation } from 'react-i18next';
import Text from '@popup/popupX/shared/Text';
import Button from '@popup/popupX/shared/Button';
import { ChainParameters, ChainParametersV0, CommissionRange, CommissionRates } from '@concordium/web-sdk';
import Form, { useForm } from '@popup/popupX/shared/Form';
import FormSlider from '@popup/popupX/shared/Form/Slider/Slider';
import { PropsOf } from 'wallet-common-helpers';
import { isRange } from '../util';

const COMMISSION_STEP = 0.001;

type Props = {
initial?: CommissionRates;
onSubmit(values: CommissionRates): void;
chainParams: Exclude<ChainParameters, ChainParametersV0>;
};

export default function Commissions({ initial, onSubmit, chainParams }: Props) {
const { t } = useTranslation('x', { keyPrefix: 'earn.validator.commissions' });
const { bakingCommissionRange, finalizationCommissionRange, transactionCommissionRange } = chainParams;
const defaultValues: CommissionRates = useMemo(
() => ({
bakingCommission: (initial?.bakingCommission ?? bakingCommissionRange.min) * 100,
transactionCommission: (initial?.transactionCommission ?? transactionCommissionRange.min) * 100,
finalizationCommission: (initial?.finalizationCommission ?? finalizationCommissionRange.min) * 100,
}),
[initial, chainParams]
);
const form = useForm({ defaultValues });

const commissionRules = useCallback(
(range: CommissionRange): PropsOf<typeof FormSlider>['rules'] => {
const min = range.min * 100;
const max = range.max * 100;
return {
min: { value: min, message: t('error.min', { min }) },
max: { value: max, message: t('error.max', { max }) },
required: t('error.required'),
};
},
[t]
);

const handleSubmit = useCallback(
(values: CommissionRates) => {
const fractions: CommissionRates = {
transactionCommission: values.transactionCommission / 100,
bakingCommission: values.bakingCommission / 100,
finalizationCommission: values.finalizationCommission / 100,
};

onSubmit(fractions);
},
[onSubmit]
);

return (
<Page className="validator-commissions">
<Page.Top heading={t('title')} />
<Text.Capture>{t('desciption')}</Text.Capture>
<Form formMethods={form} onSubmit={handleSubmit} className="validator-commissions__form">
{(f) => (
<>
{isRange(transactionCommissionRange) && (
<FormSlider
control={f.control}
name="transactionCommission"
label={t('fieldTransactionFee.label')}
min={transactionCommissionRange.min * 100}
max={transactionCommissionRange.max * 100}
rules={commissionRules(transactionCommissionRange)}
step={COMMISSION_STEP}
unit="%"
/>
)}
{isRange(bakingCommissionRange) && (
<FormSlider
control={f.control}
name="bakingCommission"
label={t('fieldBlockReward.label')}
min={bakingCommissionRange.min * 100}
max={bakingCommissionRange.max * 100}
rules={commissionRules(bakingCommissionRange)}
step={COMMISSION_STEP}
unit="%"
/>
)}
{isRange(finalizationCommissionRange) && (
<FormSlider
control={f.control}
name="finalizationCommission"
label={t('fieldFinalizationReward.label')}
min={finalizationCommissionRange.min * 100}
max={finalizationCommissionRange.max * 100}
rules={commissionRules(finalizationCommissionRange)}
step={COMMISSION_STEP}
unit="%"
/>
)}
</>
)}
</Form>
<Page.Footer>
<Button.Main label={t('buttonContinue')} onClick={form.handleSubmit(handleSubmit)} />
</Page.Footer>
</Page>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Commissions';
Original file line number Diff line number Diff line change
@@ -1,47 +1,29 @@
.validator-keys-container {
.capture__main_small {
color: $color-white;
word-wrap: break-word;
.validator-keys {
gap: rem(16px);

.page__top {
margin-bottom: 0 !important;
}

.validator-keys {
&__title {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: rem(12px);
&--expanded {
.validator-keys__expand svg {
transform: rotate(-90deg);
}
}

&__card {
display: flex;
flex-direction: column;
border-radius: rem(12px);
padding: rem(16px);
margin-top: rem(16px);
background-color: rgba($color-grey-3, 0.3);

&_row:not(:last-child) {
padding-bottom: rem(8px);
margin-bottom: rem(8px);
border-bottom: 1px solid $color-grey-3;
}

&_row {
display: flex;
flex-direction: column;

.capture__main_small:first-child {
color: rgba($color-mineral-3, 0.5);
margin-bottom: rem(6px);
}
}
&:not(.validator-keys--expanded) {
.card-x .details .capture__main_small {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}

&__expand {
margin-bottom: rem(8px);

&__export {
display: flex;
align-items: center;
gap: rem(8px);
margin-top: rem(16px);
svg {
transform: rotate(90deg);
}
}
}
Loading

0 comments on commit 525344a

Please sign in to comment.