Skip to content

Commit

Permalink
refactor: removed weird parts of signatories form in multisig creation (
Browse files Browse the repository at this point in the history
  • Loading branch information
johnthecat authored Nov 8, 2024
1 parent 49bb049 commit 5c798f2
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ describe('widgets/CreateWallet/model/form-model', () => {
.set(walletModel.$allWallets, [initiatorWallet, signerWallet]),
});

await allSettled(signatoryModel.events.signatoriesChanged, {
await allSettled(signatoryModel.events.changeSignatory, {
scope,
params: { index: 0, name: signerWallet.name, address: toAddress(signerWallet.accounts[0].accountId) },
});
await allSettled(signatoryModel.events.signatoriesChanged, {
await allSettled(signatoryModel.events.changeSignatory, {
scope,
params: { index: 1, name: 'Alice', address: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' },
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ describe('widgets/CreateWallet/model/form-model', () => {
.set(networkModel.$chains, { '0x00': testChain })
.set(networkModel.$connectionStatuses, { '0x00': ConnectionStatus.CONNECTED })
.set(walletModel.$allWallets, [initiatorWallet, signerWallet, multisigWallet])
.set(signatoryModel.$signatories, new Map([])),
.set(signatoryModel.$signatories, []),
});

await allSettled(signatoryModel.events.signatoriesChanged, {
await allSettled(signatoryModel.events.changeSignatory, {
scope,
params: { index: 0, name: 'test', address: toAddress(signerWallet.accounts[0].accountId) },
});
await allSettled(signatoryModel.events.signatoriesChanged, {
await allSettled(signatoryModel.events.changeSignatory, {
scope,
params: { index: 1, name: 'Alice', address: toAddress(signatoryWallet.accounts[0].accountId) },
});
Expand Down Expand Up @@ -106,15 +106,15 @@ describe('widgets/CreateWallet/model/form-model', () => {
.set(networkModel.$chains, { '0x00': testChain })
.set(networkModel.$connectionStatuses, { '0x00': ConnectionStatus.CONNECTED })
.set(walletModel.$allWallets, [initiatorWallet, signerWallet, multisigWallet])
.set(signatoryModel.$signatories, new Map([])),
.set(signatoryModel.$signatories, []),
});

await allSettled(formModel.$createMultisigForm.fields.chain.onChange, { scope, params: testChain });
await allSettled(signatoryModel.events.signatoriesChanged, {
await allSettled(signatoryModel.events.changeSignatory, {
scope,
params: { index: 0, name: 'test', address: toAddress(signerWallet.accounts[0].accountId) },
});
await allSettled(signatoryModel.events.signatoriesChanged, {
await allSettled(signatoryModel.events.changeSignatory, {
scope,
params: { index: 1, name: 'Alice', address: toAddress(signatoryWallet.accounts[0].accountId) },
});
Expand Down
1 change: 1 addition & 0 deletions src/renderer/widgets/CreateWallet/model/__tests__/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const testApi = {
export const testChain = {
name: 'test-chain',
chainId: '0x00',
assets: [{ assetId: 0 }],
options: [ChainOptions.MULTISIG],
type: ChainType.SUBSTRATE,
} as unknown as Chain;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,59 +13,59 @@ describe('widgets/CreateWallet/model/signatory-model', () => {

test('should correctly add signatories', async () => {
const scope = fork({
values: new Map().set(signatoryModel.$signatories, new Map([])),
values: new Map().set(signatoryModel.$signatories, []),
});

expect(scope.getState(signatoryModel.$signatories).size).toEqual(0);
expect(scope.getState(signatoryModel.$signatories).length).toEqual(0);

await allSettled(signatoryModel.events.signatoriesChanged, {
await allSettled(signatoryModel.events.addSignatory, {
scope,
params: { index: 1, name: 'Alice', address: toAddress(signerWallet.accounts[0].accountId) },
params: { name: 'Alice', address: toAddress(signerWallet.accounts[0].accountId) },
});

await allSettled(signatoryModel.events.signatoriesChanged, {
await allSettled(signatoryModel.events.addSignatory, {
scope,
params: { index: 0, name: 'test', address: toAddress(signerWallet.accounts[0].accountId) },
params: { name: 'test', address: toAddress(signerWallet.accounts[0].accountId) },
});

expect(scope.getState(signatoryModel.$signatories).size).toEqual(2);
expect(scope.getState(signatoryModel.$signatories).length).toEqual(2);
});

test('should correctly delete signatories', async () => {
const scope = fork({
values: new Map().set(signatoryModel.$signatories, new Map([])),
values: new Map().set(signatoryModel.$signatories, []),
});

expect(scope.getState(signatoryModel.$signatories).size).toEqual(0);
expect(scope.getState(signatoryModel.$signatories).length).toEqual(0);

await allSettled(signatoryModel.events.signatoriesChanged, {
await allSettled(signatoryModel.events.changeSignatory, {
scope,
params: { index: 0, name: 'test', address: toAddress(signerWallet.accounts[0].accountId) },
});

expect(scope.getState(signatoryModel.$signatories).size).toEqual(1);
expect(scope.getState(signatoryModel.$signatories).length).toEqual(1);

await allSettled(signatoryModel.events.signatoryDeleted, {
await allSettled(signatoryModel.events.deleteSignatory, {
scope,
params: 0,
});

expect(scope.getState(signatoryModel.$signatories).size).toEqual(0);
expect(scope.getState(signatoryModel.$signatories).length).toEqual(0);
});

test('should have correct value for $ownSignatoryWallets', async () => {
const scope = fork({
values: new Map().set(walletModel.$allWallets, [initiatorWallet, signerWallet]),
});

await allSettled(signatoryModel.events.signatoriesChanged, {
await allSettled(signatoryModel.events.changeSignatory, {
scope,
params: { index: 1, name: 'Alice', address: toAddress(signatoryWallet.accounts[0].accountId) },
});

expect(scope.getState(signatoryModel.$ownedSignatoriesWallets)?.length).toEqual(0);

await allSettled(signatoryModel.events.signatoriesChanged, {
await allSettled(signatoryModel.events.changeSignatory, {
scope,
params: { index: 0, name: 'test', address: toAddress(signerWallet.accounts[0].accountId) },
});
Expand Down
6 changes: 3 additions & 3 deletions src/renderer/widgets/CreateWallet/model/flow-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ const $transaction = combine(
({ apis, chain, remarkTx, signatories, signer, threshold, multisigAccountId }) => {
if (!chain || !remarkTx || !signer) return undefined;

const signatoriesWrapped = Array.from(signatories.values()).map((s) => ({
const signatoriesWrapped = signatories.map((s) => ({
accountId: toAccountId(s.address),
adress: s.address,
}));
Expand Down Expand Up @@ -412,9 +412,9 @@ sample({
step: $step,
hiddenMultisig: formModel.$hiddenMultisig,
},
filter: ({ step }, results) => {
filter: ({ step, hiddenMultisig }, results) => {
const isSubmitStep = isStep(step, Step.SUBMIT);
const isNonNullable = nonNullable(formModel.$hiddenMultisig);
const isNonNullable = nonNullable(hiddenMultisig);
const isSuccessResult = results[0]?.result === ExtrinsicResult.SUCCESS;

return isSubmitStep && isNonNullable && isSuccessResult;
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/widgets/CreateWallet/model/form-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const $multisigAccountId = combine(
const cryptoType = networkUtils.isEthereumBased(chain.options) ? CryptoType.ETHEREUM : CryptoType.SR25519;

return accountUtils.getMultisigAccountId(
Array.from(signatories.values()).map((s) => toAccountId(s.address)),
signatories.map((s) => toAccountId(s.address)),
threshold,
cryptoType,
);
Expand Down Expand Up @@ -113,7 +113,7 @@ const $availableAccounts = combine(
);

sample({
clock: signatoryModel.events.signatoryDeleted,
clock: signatoryModel.events.deleteSignatory,
target: $createMultisigForm.fields.threshold.reset,
});

Expand Down
75 changes: 53 additions & 22 deletions src/renderer/widgets/CreateWallet/model/signatory-model.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,46 @@
import { combine, createEffect, createEvent, createStore, sample } from 'effector';
import { produce } from 'immer';

import { type Wallet } from '@/shared/core';
import { type Address, type Wallet } from '@/shared/core';
import { toAccountId } from '@/shared/lib/utils';
import { walletModel, walletUtils } from '@/entities/wallet';
import { balanceSubModel } from '@/features/balances';
import { type SignatoryInfo } from '../lib/types';

const signatoriesChanged = createEvent<SignatoryInfo>();
const signatoryDeleted = createEvent<number>();
const addSignatory = createEvent<Omit<SignatoryInfo, 'index'>>();
const changeSignatory = createEvent<SignatoryInfo>();
const deleteSignatory = createEvent<number>();
const getSignatoriesBalance = createEvent<Wallet[]>();

const $signatories = createStore<Map<number, Omit<SignatoryInfo, 'index'>>>(new Map([[0, { name: '', address: '' }]]));
const $signatories = createStore<Omit<SignatoryInfo, 'index'>[]>([{ name: '', address: '' }]);
const $hasDuplicateSignatories = combine($signatories, (signatories) => {
const signatoriesArray = Array.from(signatories.values()).map(({ address }) => toAccountId(address));
const existingKeys: Set<Address> = new Set();

return new Set(signatoriesArray).size !== signatoriesArray.length;
for (const signatory of signatories) {
if (signatory.address.length === 0) {
continue;
}

if (existingKeys.has(signatory.address)) {
return true;
}

existingKeys.add(signatory.address);
}

return false;
});

const $hasEmptySignatories = combine($signatories, (signatories) => {
return signatories.map(({ address }) => address).includes('');
});

const $ownedSignatoriesWallets = combine(
{ wallets: walletModel.$wallets, signatories: $signatories },
({ wallets, signatories }) =>
walletUtils.getWalletsFilteredAccounts(wallets, {
walletFn: (w) => !walletUtils.isWatchOnly(w) && !walletUtils.isMultisig(w),
accountFn: (a) => Array.from(signatories.values()).some((s) => toAccountId(s.address) === a.accountId),
accountFn: (a) => signatories.some((s) => toAccountId(s.address) === a.accountId),
}) || [],
);

Expand All @@ -38,28 +56,39 @@ sample({
});

sample({
clock: signatoriesChanged,
clock: addSignatory,
source: $signatories,
fn: (signatories, { index, name, address }) => {
// we need to return new Map to trigger re-render
const newMap = new Map(signatories);
newMap.set(index, { name, address });
fn: (signatories, { name, address }) => {
return produce(signatories, (draft) => {
draft.push({ name, address });
});
},
target: $signatories,
});

return newMap;
sample({
clock: changeSignatory,
source: $signatories,
fn: (signatories, { index, name, address }) => {
return produce(signatories, (draft) => {
if (index >= draft.length) {
draft.push({ name, address });
} else {
draft[index] = { name, address };
}
});
},
target: $signatories,
});

sample({
clock: signatoryDeleted,
clock: deleteSignatory,
source: $signatories,
filter: (signatories, index) => signatories.has(index),
filter: (signatories, index) => signatories.length > index,
fn: (signatories, index) => {
// we need to return new Map to trigger re-render
const newMap = new Map(signatories);
newMap.delete(index);

return newMap;
return produce(signatories, (draft) => {
draft.splice(index, 1);
});
},
target: $signatories,
});
Expand All @@ -68,9 +97,11 @@ export const signatoryModel = {
$signatories,
$ownedSignatoriesWallets,
$hasDuplicateSignatories,
$hasEmptySignatories,
events: {
signatoriesChanged,
signatoryDeleted,
addSignatory,
changeSignatory,
deleteSignatory,
getSignatoriesBalance,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ import { WalletItem } from './components/WalletItem';

export const ConfirmationStep = () => {
const { t } = useI18n();
const signatoriesMap = useUnit(signatoryModel.$signatories);
const signatories = Array.from(signatoriesMap.values());
const signatories = useUnit(signatoryModel.$signatories);
const signerWallet = useUnit(flowModel.$signerWallet);
const signer = useUnit(flowModel.$signer);
const {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useForm } from 'effector-forms';
import { useUnit } from 'effector-react';
import { type FormEvent, useState } from 'react';
import { type FormEvent, useMemo, useState } from 'react';

import { useI18n } from '@/shared/i18n';
import { Alert, Button, InputHint, Select, SmallTitleText } from '@/shared/ui';
Expand Down Expand Up @@ -33,8 +33,7 @@ export const SelectSignatoriesThreshold = () => {
const { t } = useI18n();

const [hasClickedNext, setHasClickedNext] = useState(false);
const signatoriesMap = useUnit(signatoryModel.$signatories);
const signatories = Array.from(signatoriesMap.values());
const signatories = useUnit(signatoryModel.$signatories);
const fakeTx = useUnit(flowModel.$fakeTx);
const {
fields: { threshold, chain },
Expand All @@ -44,17 +43,18 @@ export const SelectSignatoriesThreshold = () => {
const hiddenMultisig = useUnit(formModel.$hiddenMultisig);
const ownedSignatoriesWallets = useUnit(signatoryModel.$ownedSignatoriesWallets);
const hasDuplicateSignatories = useUnit(signatoryModel.$hasDuplicateSignatories);
const thresholdOptions = getThresholdOptions(signatories.length - 1);
const hasEmptySignatories = useUnit(signatoryModel.$hasEmptySignatories);

const thresholdOptions = useMemo(() => getThresholdOptions(signatories.length - 1), [signatories.length]);

const hasOwnedSignatory = !!ownedSignatoriesWallets && ownedSignatoriesWallets?.length > 0;
const hasEnoughSignatories = signatories.length >= MIN_THRESHOLD;
const hasEmptySignatory = signatories.map(({ address }) => address).includes('');
const isThresholdValid = threshold.value >= MIN_THRESHOLD && threshold.value <= signatories.length;
const canSubmit =
hasOwnedSignatory &&
hasEnoughSignatories &&
!multisigAlreadyExists &&
!hasEmptySignatory &&
!hasEmptySignatories &&
isThresholdValid &&
!hasDuplicateSignatories;

Expand Down Expand Up @@ -85,9 +85,9 @@ export const SelectSignatoriesThreshold = () => {
{t('createMultisigAccount.multisigStep', { step: 2 })}{' '}
{t('createMultisigAccount.signatoryThresholdDescription')}
</SmallTitleText>
<div className="flex flex-col gap-y-4 px-5 py-4">
<div className="flex flex-col gap-4 px-5 py-4">
<SelectSignatories />
<div className="flex items-end gap-x-4">
<div className="flex items-end gap-4">
<Alert
active={hasClickedNext && !hasOwnedSignatory && signatories.length > 0}
title={t('createMultisigAccount.noOwnSignatoryTitle')}
Expand All @@ -105,7 +105,7 @@ export const SelectSignatoriesThreshold = () => {
</Alert>

<Alert
active={hasClickedNext && hasEmptySignatory}
active={hasClickedNext && hasEmptySignatories}
title={t('createMultisigAccount.notEmptySignatoryTitle')}
variant="error"
>
Expand All @@ -120,6 +120,7 @@ export const SelectSignatoriesThreshold = () => {
selectedId={threshold.value.toString()}
options={thresholdOptions}
invalid={threshold.hasError()}
disabled={thresholdOptions.length === 0}
position={thresholdOptions.length > 2 ? 'up' : 'down'}
onChange={({ value }) => threshold.onChange(value)}
/>
Expand Down
Loading

0 comments on commit 5c798f2

Please sign in to comment.