Skip to content

Commit

Permalink
feat: flexible final approve (#2888)
Browse files Browse the repository at this point in the history
  • Loading branch information
sokolova-an authored Dec 30, 2024
1 parent b4194b1 commit 13398fb
Show file tree
Hide file tree
Showing 15 changed files with 133 additions and 131 deletions.
61 changes: 59 additions & 2 deletions src/renderer/entities/multisig/model/multisigs-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { GraphQLClient } from 'graphql-request';
import { uniq } from 'lodash';
import { interval } from 'patronum';

import { storageService } from '@/shared/api/storage';
import {
type Account,
AccountType,
type Chain,
type ChainId,
ExternalType,
Expand All @@ -16,6 +18,7 @@ import {
type MultisigWallet,
type NoID,
NotificationType,
type ProxiedAccount,
type ProxyAccount,
SigningType,
WalletType,
Expand Down Expand Up @@ -106,11 +109,11 @@ const getMultisigsFx = createEffect(
// filter out multisigs that already exists
.filter((multisigResult) => nullable(multisigAccounts.find((a) => a.accountId === multisigResult.accountId)))
.map(({ threshold, accountId, signatories }): GetMultisigResponse => {
// TODO: run a proxy worker for new multisiig since we don't have these proxies at the moment
const proxiesList = proxies[accountId];

const proxy = nonNullable(proxiesList)
? // TODO check if it's a pure proxy
(proxiesList.find((p) => p.chainId === chain.chainId && p.proxyType === 'Any') ?? null)
? (proxiesList.find((p) => p.chainId === chain.chainId && p.proxyType === 'Any') ?? null)
: null;

// TODO check if there's a multisig with no proxy and only one ongoing operation 'create pure proxy' - build flexible shell
Expand Down Expand Up @@ -275,6 +278,60 @@ sample({
target: notificationModel.events.notificationsAdded,
});

// Bond flexible multisig with proxy
const $flexibleWithProxy = createStore<FlexibleMultisigWallet | null>(null);

sample({
clock: walletModel.events.walletCreatedDone,
source: walletModel.$wallets,
filter: (_, { accounts }) => {
const account = accounts.at(0);

return nonNullable(account) && account.type === AccountType.PROXIED && account.proxyType === 'Any';
},
fn: (wallets, { accounts }) => {
const account = accounts.at(0)! as ProxiedAccount;

const proxiedWallet = walletUtils.getWalletFilteredAccounts(wallets, {
walletFn: (w) => walletUtils.isFlexibleMultisig(w),
accountFn: (a) => a.accountId === account.proxyAccountId,
}) as FlexibleMultisigWallet | null;

if (!proxiedWallet) return null;

// Proxy accountId or entire account?
return {
...proxiedWallet,
accounts: proxiedWallet.accounts.map((acc) => ({ ...acc, proxyAccountId: account.accountId })),
};
},
target: $flexibleWithProxy,
});

type UpdateAccounts = { walletId: number; accounts: Account[] };
const updateAccountsFx = createEffect(async ({ walletId, accounts }: UpdateAccounts): Promise<UpdateAccounts> => {
await storageService.accounts.updateAll(accounts);

return { walletId, accounts };
});

sample({
clock: $flexibleWithProxy,
filter: nonNullable,
fn: (flexibleWithProxy) => {
return {
walletId: flexibleWithProxy!.id,
accounts: flexibleWithProxy!.accounts,
};
},
target: updateAccountsFx,
});

sample({
clock: updateAccountsFx.doneData,
target: walletModel.events.updateAccounts,
});

export const multisigsModel = {
events: {
subscribe,
Expand Down
13 changes: 12 additions & 1 deletion src/renderer/entities/operations/model/operations-model.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
import { createEvent, restore } from 'effector';
import { createGate } from 'effector-react';

import { type MultisigEvent, type MultisigTransaction } from '@/shared/core';

const flow = createGate<{ transactions: MultisigTransaction[]; events: MultisigEvent[] }>({
defaultState: { transactions: [], events: [] },
});
const $multisigTransactions = flow.state.map(({ transactions }) => transactions);

const changeFilteredTxs = createEvent<MultisigTransaction[]>();
const $filteredTxs = restore<MultisigTransaction[]>(changeFilteredTxs, []).reset($multisigTransactions);

export const operationsModel = {
$multisigTransactions: flow.state.map(({ transactions }) => transactions),
$multisigTransactions,
$multisigEvents: flow.state.map(({ events }) => events),

$filteredTxs,

events: {
changeFilteredTxs,
},

gate: {
flow,
},
Expand Down
10 changes: 10 additions & 0 deletions src/renderer/entities/transaction/lib/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ export const isManageProxyTransaction = (transaction?: Transaction | DecodedTran
return ManageProxyTypes.includes(transaction.type);
};

export const isProxyTypeTransaction = (transaction?: Transaction | DecodedTransaction): boolean => {
return (
isProxyTransaction(transaction) ||
isAddProxyTransaction(transaction) ||
isRemoveProxyTransaction(transaction) ||
isCreatePureProxyTransaction(transaction) ||
isRemovePureProxyTransaction(transaction)
);
};

export const isAddProxyTransaction = (transaction?: Transaction | DecodedTransaction): boolean => {
return transaction?.type === TransactionType.ADD_PROXY;
};
Expand Down
6 changes: 5 additions & 1 deletion src/renderer/entities/transaction/lib/transactionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
type Wallet,
WrapperKind,
} from '@/shared/core';
import { type TxMetadata, createTxMetadata, dictionary, toAccountId } from '@/shared/lib/utils';
import { type TxMetadata, createTxMetadata, dictionary, nullable, toAccountId } from '@/shared/lib/utils';
import { walletUtils } from '@/entities/wallet';

import { LEAVE_SOME_SPACE_MULTIPLIER } from './common/constants';
Expand Down Expand Up @@ -181,6 +181,8 @@ function getTxWrappers({ wallet, ...params }: TxWrappersParams): TxWrapper[] {
return getMultisigWrapper(params);
}

// TODO add flexible multisig wrapper

if (walletUtils.isProxied(wallet)) {
return getProxyWrapper(params);
}
Expand Down Expand Up @@ -236,6 +238,8 @@ function getProxyWrapper({ wallets, account, signatories = [] }: Omit<TxWrappers
return acc;
}, []);

if (nullable(proxiesMap.at(0))) return [];

const wrapper: ProxyTxWrapper = {
kind: WrapperKind.PROXY,
proxyAccount: proxiesMap[0].account,
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/entities/wallet/lib/wallet-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function isSingleShard(wallet?: Wallet): wallet is SingleShardWallet {
return wallet?.type === WalletType.SINGLE_PARITY_SIGNER;
}

function isFlexibleMultisig(wallet?: Wallet): wallet is FlexibleMultisigWallet {
function isFlexibleMultisig(wallet?: Wallet | null): wallet is FlexibleMultisigWallet {
return wallet?.type === WalletType.FLEXIBLE_MULTISIG;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,10 @@ type GetDepositParams = {
};

const getProxyDepositFx = createEffect(async ({ api, asset }: GetDepositParams): Promise<BN> => {
const minDeposit = await balanceService.getExistentialDeposit(api, asset);
const existentialDeposit = await balanceService.getExistentialDeposit(api, asset);
const proxyDeposit = new BN(proxyService.getProxyDeposit(api, '0', 1));

return BN.max(minDeposit, proxyDeposit);
return proxyDeposit.add(existentialDeposit);
});

sample({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { type ChainId, type MultisigTransaction, TransactionType } from '@/share
import { useI18n } from '@/shared/i18n';
import { Button, MultiSelect } from '@/shared/ui';
import { type DropdownOption, type DropdownResult } from '@/shared/ui/types';
import { operationsModel } from '@/entities/operations';
import { TransferTypes, XcmTypes, findCoreBatchAll } from '@/entities/transaction';
import { UNKNOWN_TYPE } from '../lib/constants';
import { getStatusOptions, getTransactionOptions } from '../lib/utils';
Expand All @@ -31,10 +32,9 @@ const mapValues = (result: DropdownResult) => result.value;

type Props = {
txs: MultisigTransactionDS[];
onChange: (filteredTxs: MultisigTransaction[]) => void;
};

export const OperationsFilter = ({ txs, onChange }: Props) => {
export const OperationsFilter = ({ txs }: Props) => {
const { t } = useI18n();

const [availableChains, setAvailableChains] = useState<{ chainId: ChainId; name: string }[]>([]);
Expand All @@ -57,7 +57,7 @@ export const OperationsFilter = ({ txs, onChange }: Props) => {

useEffect(() => {
setFiltersOptions(getAvailableFiltersOptions(txs));
onChange(txs);
operationsModel.events.changeFilteredTxs(txs);
}, [txs, availableChains]);

const getFilterableTxType = (tx: MultisigTransaction): TransactionType | typeof UNKNOWN_TYPE => {
Expand Down Expand Up @@ -131,12 +131,12 @@ export const OperationsFilter = ({ txs, onChange }: Props) => {
setSelectedOptions(newSelectedOptions);

const filteredTxs = txs.filter((tx) => filterTx(tx, newSelectedOptions));
onChange(filteredTxs);
operationsModel.events.changeFilteredTxs(filteredTxs);
};

const clearFilters = () => {
setSelectedOptions(EmptySelected);
onChange(txs);
operationsModel.events.changeFilteredTxs(txs);
};

const filtersSelected =
Expand Down
58 changes: 20 additions & 38 deletions src/renderer/features/proxies/model/proxies-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
type ProxiedWallet,
type ProxyAccount,
type ProxyDeposits,
type ProxyGroup,
type Wallet,
type WalletsMap,
} from '@/shared/core';
Expand All @@ -41,12 +40,10 @@ import { proxiesUtils } from '../lib/proxies-utils';

const workerStarted = createEvent();
const connected = createEvent<ChainId>();
const proxiedWalletsCreated = createEvent<ProxiedWalletsParams>();
const proxiedWalletsCreated = createEvent<Omit<ProxiedWalletsParams, 'wallets'>>();
const proxiedAccountsRemoved = createEvent<ProxiedAccount[]>();
const depositsReceived = createEvent<ProxyDeposits>();

const walletsAdded = createEvent<Wallet[]>();

const $endpoint = createStore<Endpoint<any> | null>(null);
const $deposits = createStore<ProxyDeposits[]>([]);

Expand Down Expand Up @@ -99,10 +96,11 @@ const getProxiesFx = createEffect(
const chainProxies = proxies.filter((p) => p.chainId === chainId);

const walletsMap = keyBy(wallets, 'id');
const filteredAccounts = accounts.filter((a) => !walletsMap[a.walletId].isHidden);

const accountsForProxy = keyBy(accounts, 'accountId');
const accountsForProxy = keyBy(filteredAccounts, 'accountId');
const accountsForProxied = keyBy(
accounts.filter((a) => proxiesUtils.isProxiedAvailable(walletsMap[a.walletId])),
filteredAccounts.filter((a) => proxiesUtils.isProxiedAvailable(walletsMap[a.walletId])),
'accountId',
);

Expand Down Expand Up @@ -152,15 +150,25 @@ const disconnectFx = createEffect(async ({ chainId, endpoint }: { chainId: Chain
type ProxiedWalletsParams = {
proxiedAccounts: PartialProxiedAccount[];
chains: Record<ChainId, Chain>;
wallets: Wallet[];
};

const createProxiedWalletsFx = createEffect(async ({ proxiedAccounts, chains }: ProxiedWalletsParams) => {
const createProxiedWalletsFx = createEffect(async ({ proxiedAccounts, chains, wallets }: ProxiedWalletsParams) => {
return proxiedAccounts.map((proxied) => {
const walletName = proxyUtils.getProxiedName(proxied, chains[proxied.chainId].addressPrefix);

const proxyWallet = walletUtils.getWalletFilteredAccounts(wallets, {
walletFn: (w) => walletUtils.isFlexibleMultisig(w),
accountFn: (a) => accountUtils.isChainIdMatch(a, proxied.chainId) && a.accountId === proxied.proxyAccountId,
});

const isHidden = walletUtils.isFlexibleMultisig(proxyWallet);

const wallet: Omit<NoID<ProxiedWallet>, 'accounts' | 'isActive'> = {
name: walletName,
type: WalletType.PROXIED,
signingType: SigningType.WATCH_ONLY,
isHidden,
};

const isEthereumChain = networkUtils.isEthereumBased(chains[proxied.chainId].options);
Expand Down Expand Up @@ -212,7 +220,7 @@ sample({
source: {
chains: networkModel.$chains,
proxies: proxyModel.$proxies,
wallets: walletModel.$wallets,
wallets: walletModel.$allWallets,
endpoint: $endpoint,
},
filter: ({ endpoint }) => Boolean(endpoint),
Expand All @@ -238,8 +246,8 @@ spread({
proxiesToAdd: proxyModel.events.proxiesAdded,
proxiedAccountsToRemove: proxiedAccountsRemoved,
proxiedAccountsToAdd: attach({
source: networkModel.$chains,
mapParams: (proxiedAccounts: ProxiedAccount[], chains) => ({ proxiedAccounts, chains }),
source: { chains: networkModel.$chains, wallets: walletModel.$wallets },
mapParams: (proxiedAccounts: ProxiedAccount[], { chains, wallets }) => ({ proxiedAccounts, chains, wallets }),
effect: createProxiedWalletsFx,
}),
deposits: depositsReceived,
Expand Down Expand Up @@ -279,34 +287,6 @@ sample({
target: series(walletModel.events.proxiedCreated),
});

sample({
clock: walletsAdded,
source: {
groups: proxyModel.$proxyGroups,
deposits: $deposits,
},
filter: ({ deposits }) => deposits.length > 0,
fn: ({ groups, deposits }, wallets) => {
const initial: { toAdd: NoID<ProxyGroup>[]; toUpdate: NoID<ProxyGroup>[] } = {
toAdd: [],
toUpdate: [],
};

return deposits.reduce((acc, deposit) => {
const { toAdd, toUpdate } = proxyUtils.createProxyGroups(wallets, groups, deposit);

return {
toAdd: acc.toAdd.concat(toAdd),
toUpdate: acc.toUpdate.concat(toUpdate),
};
}, initial);
},
target: spread({
toAdd: proxyModel.events.proxyGroupsAdded,
toUpdate: proxyModel.events.proxyGroupsUpdated,
}),
});

sample({
clock: depositsReceived,
source: {
Expand Down Expand Up @@ -358,6 +338,8 @@ sample({

sample({
clock: proxiedWalletsCreated,
source: walletModel.$wallets,
fn: (wallets, params) => ({ ...params, wallets }),
target: createProxiedWalletsFx,
});

Expand Down
4 changes: 2 additions & 2 deletions src/renderer/features/proxy-add-pure/model/form-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -479,13 +479,13 @@ sample({
filter: (_, account) => Boolean(account),
fn: ({ wallet, wallets }, account): Record<string, boolean> => {
if (!wallet) return { isMultisig: false, isProxy: false };
if (walletUtils.isRegularMultisig(wallet)) return { isMultisig: true, isProxy: false };
if (walletUtils.isMultisig(wallet)) return { isMultisig: true, isProxy: false };
if (!walletUtils.isProxied(wallet)) return { isMultisig: false, isProxy: false };

const accountWallet = walletUtils.getWalletById(wallets, account!.walletId);

return {
isMultisig: walletUtils.isRegularMultisig(accountWallet),
isMultisig: walletUtils.isMultisig(accountWallet),
isProxy: true,
};
},
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/features/proxy-remove-pure/model/form-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,13 +271,13 @@ sample({
filter: (_, account) => Boolean(account),
fn: ({ wallet, wallets }, account): Record<string, boolean> => {
if (!wallet) return { isMultisig: false, isProxy: false };
if (walletUtils.isRegularMultisig(wallet)) return { isMultisig: true, isProxy: false };
if (walletUtils.isMultisig(wallet)) return { isMultisig: true, isProxy: false };
if (!walletUtils.isProxied(wallet)) return { isMultisig: false, isProxy: false };

const accountWallet = walletUtils.getWalletById(wallets, account!.walletId);

return {
isMultisig: walletUtils.isRegularMultisig(accountWallet),
isMultisig: walletUtils.isMultisig(accountWallet),
isProxy: true,
};
},
Expand Down
Loading

0 comments on commit 13398fb

Please sign in to comment.