Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create simple Arbitrary Txs action #3624

Open
wants to merge 10 commits into
base: feat/arbitrary-txs
Choose a base branch
from
4 changes: 4 additions & 0 deletions amplify/backend/api/colonycdapp/schema/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,10 @@ enum ColonyActionType {
MANAGE_TOKENS
MANAGE_TOKENS_MOTION
MANAGE_TOKENS_MULTISIG
"""
An action related to arbitrary transaction
"""
ARBITRARY_TX
}

"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { CURRENCY_VALUES } from '~frame/v5/pages/UserCryptoToFiatPage/constants.
import { SupportedCurrencies } from '~gql';
import { getCountries } from '~utils/countries.ts';
import { formatText } from '~utils/intl.ts';
import FormInput from '~v5/common/Fields/InputBase/FormInput.tsx';
import FormSelect from '~v5/common/Fields/Select/FormSelect.tsx';

import { FormInput } from '../FormInput.tsx';
import { FormRow } from '../FormRow.tsx';
import { FormSelect } from '../FormSelect.tsx';

import { BANK_DETAILS_FORM_MSG } from './constants.ts';
import { BankDetailsFields, type BankDetailsFormSchema } from './validation.ts';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { CURRENCIES } from '~frame/v5/pages/UserCryptoToFiatPage/constants.ts';
import { type BankDetailsFormValues } from '~frame/v5/pages/UserCryptoToFiatPage/types.ts';
import { Form } from '~shared/Fields/index.ts';
import { formatText } from '~utils/intl.ts';
import FormInput from '~v5/common/Fields/InputBase/FormInput.tsx';
import FormSelect from '~v5/common/Fields/Select/FormSelect.tsx';

import { FormInput } from '../FormInput.tsx';
import { FormRow } from '../FormRow.tsx';
import { FormSelect } from '../FormSelect.tsx';
import ModalFormCTAButtons from '../ModalFormCTAButtons/ModalFormCTAButtons.tsx';
import ModalHeading from '../ModalHeading/ModalHeading.tsx';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React, { type FC } from 'react';

import { Form } from '~shared/Fields/index.ts';
import { formatText } from '~utils/intl.ts';
import FormInput from '~v5/common/Fields/InputBase/FormInput.tsx';

import { FormInput } from '../FormInput.tsx';
import { FormRow } from '../FormRow.tsx';
import ModalFormCTAButtons from '../ModalFormCTAButtons/ModalFormCTAButtons.tsx';
import ModalHeading from '../ModalHeading/ModalHeading.tsx';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { useFormContext } from 'react-hook-form';

import { getCountries } from '~utils/countries.ts';
import { formatText } from '~utils/intl.ts';

import { FormSelect } from '../FormSelect.tsx';
import FormSelect from '~v5/common/Fields/Select/FormSelect.tsx';

import { CONTACT_DETAILS_FORM_MSGS } from './consts.ts';
import { AddressFields, type ContactDetailsFormSchema } from './validation.ts';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import {
type SubdivisionData,
getSubdivisionsByCountryCode,
} from '~utils/subdivisions.ts';

import { FormSelect } from '../FormSelect.tsx';
import FormSelect from '~v5/common/Fields/Select/FormSelect.tsx';

import { CONTACT_DETAILS_FORM_MSGS } from './consts.ts';
import { AddressFields, type ContactDetailsFormSchema } from './validation.ts';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React, { type FC } from 'react';

import { Form } from '~shared/Fields/index.ts';
import { formatText } from '~utils/intl.ts';
import FormInput from '~v5/common/Fields/InputBase/FormInput.tsx';

import { FormInput } from '../FormInput.tsx';
import { FormRow } from '../FormRow.tsx';
import ModalFormCTAButtons from '../ModalFormCTAButtons/ModalFormCTAButtons.tsx';
import ModalHeading from '../ModalHeading/ModalHeading.tsx';
Expand Down
23 changes: 17 additions & 6 deletions src/components/shared/MaskedAddress/MaskedAddress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import clsx from 'clsx';
import React, { type RefObject, forwardRef } from 'react';

import { type Address } from '~types/index.ts';
import { splitAddress, type AddressElements } from '~utils/strings.ts';

import getMaskedAddress from './getMaskedAddress.ts';

interface Props {
/*
Expand Down Expand Up @@ -39,7 +40,11 @@ const MaskedAddress = forwardRef(
{ address, mask = '...', full = false, dataTest, className }: Props,
ref: RefObject<any>,
) => {
const cutAddress: AddressElements = splitAddress(address);
const { result, cutAddress } = getMaskedAddress({
address,
isFull: full,
mask,
});

return (
<span
Expand All @@ -48,10 +53,16 @@ const MaskedAddress = forwardRef(
ref={ref}
data-test={dataTest}
>
{cutAddress.header}
{cutAddress.start}
{full ? <span className="mx-1">{cutAddress.middle}</span> : mask}
{cutAddress.end}
{cutAddress ? (
<>
{cutAddress.header}
{cutAddress.start}
{full ? <span className="mx-1">{cutAddress.middle}</span> : mask}
{cutAddress.end}
</>
) : (
<>{result}</>
)}
</span>
);
},
Expand Down
29 changes: 29 additions & 0 deletions src/components/shared/MaskedAddress/getMaskedAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { isAddress } from 'ethers/lib/utils';

import { splitAddress, type AddressElements } from '~utils/strings.ts';

interface GetMaskedAddressParams {
address: string;
isFull?: boolean;
mask?: string;
}
interface GetMaskedAddressResult {
result: string;
cutAddress: AddressElements | null;
}
const getMaskedAddress = ({
address,
isFull = false,
mask = '...',
}: GetMaskedAddressParams): GetMaskedAddressResult => {
const isValidAddress = isAddress(address);
if (!isValidAddress) return { result: address, cutAddress: null };

const cutAddress: AddressElements = splitAddress(address);

const result = `${cutAddress.header}${cutAddress.start}${isFull ? cutAddress.middle : mask}${cutAddress.end}`;

return { result, cutAddress };
};

export default getMaskedAddress;
12 changes: 12 additions & 0 deletions src/components/v5/common/ActionSidebar/hooks/useActionsList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,18 @@ const useActionsList = () => {
// },
],
},
{
key: '6',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arrenv Is there a preference regarding which group arbitrary transaction should show under?

image

isAccordion: true,
title: { id: 'actions.transactions' },
options: [
{
label: { id: 'actions.arbitraryTxs' },
value: Action.ArbitraryTxs,
isNew: true,
},
],
},
];
if (!isStagedExpenditureEnabled) {
const stagedPaymentIndex = actionsListOptions[0].options.findIndex(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useFormContext } from 'react-hook-form';
import { Action } from '~constants/actions.ts';

import { ACTION_TYPE_FIELD_NAME } from '../consts.ts';
import ArbitraryTxsForm from '../partials/forms/ArbitraryTxsForm/index.ts';
import BatchPaymentForm from '../partials/forms/BatchPaymentForm/index.ts';
import CreateDecisionForm from '../partials/forms/CreateDecisionForm/index.ts';
import CreateNewTeamForm from '../partials/forms/CreateNewTeamForm/index.ts';
Expand Down Expand Up @@ -47,6 +48,7 @@ const useSidebarActionForm = () => {
[Action.ManagePermissions]: ManagePermissionsForm,
[Action.ManageVerifiedMembers]: ManageVerifiedMembersForm,
[Action.ManageReputation]: ManageReputationForm,
[Action.ArbitraryTxs]: ArbitraryTxsForm,
}),
[],
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';
import { Action } from '~constants/actions.ts';
import { useActiveActionType } from '~v5/common/ActionSidebar/hooks/useActiveActionType.ts';

import ArbitraryTxsDescription from './partials/ArbitraryTxsDescription.tsx';
import CreateDecisionDescription from './partials/CreateDecisionDescription.tsx';
import CreateNewDomainDescription from './partials/CreateNewDomainDescription.tsx';
import EditColonyDetailsDescription from './partials/EditColonyDetailsDescription.tsx';
Expand Down Expand Up @@ -62,6 +63,8 @@ const ActionSidebarDescription = () => {
return <SplitPaymentDescription />;
case Action.StagedPayment:
return <StagedPaymentsDescription />;
case Action.ArbitraryTxs:
return <ArbitraryTxsDescription />;
default:
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';

import { ColonyActionType } from '~gql';

import CurrentUser from './CurrentUser.tsx';

const displayName =
'v5.common.ActionsSidebar.partials.ActionSidebarDescription.partials.ArbitraryTxsDescription';

export const ArbitraryTxsDescription = () => {
return (
<FormattedMessage
id="action.title"
values={{
actionType: ColonyActionType.ArbitraryTx,
initiator: <CurrentUser />,
}}
/>
);
};

ArbitraryTxsDescription.displayName = displayName;
export default ArbitraryTxsDescription;
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Tooltip from '~shared/Extensions/Tooltip/Tooltip.tsx';
import { type User } from '~types/graphql.ts';
import { formatText } from '~utils/intl.ts';
import { splitWalletAddress } from '~utils/splitWalletAddress.ts';
import AvatarWithAddress from '~v5/common/AvatarWithAddress/index.ts';
import SearchSelect from '~v5/shared/SearchSelect/SearchSelect.tsx';
import UserAvatar from '~v5/shared/UserAvatar/index.ts';
import UserInfoPopover from '~v5/shared/UserInfoPopover/UserInfoPopover.tsx';
Expand Down Expand Up @@ -108,43 +109,16 @@ const UserSelect: FC<UserSelectProps> = ({
disabled={disabled}
>
{selectedUser || field.value ? (
<>
{isUserAddressValid ? (
<>
<UserAvatar
userName={
selectedUser?.profile?.displayName?.toString() ?? undefined
}
userAddress={userWalletAddress}
userAvatarSrc={selectedUser?.profile?.avatar ?? undefined}
size={20}
/>
<p
className={clsx('ml-2 truncate text-md font-medium', {
'text-warning-400': !selectedUser?.isVerified,
'text-gray-900': selectedUser?.isVerified,
})}
>
{formatText(userName || '')}
</p>
{selectedUser?.isVerified && (
<CircleWavyCheck
size={14}
className="ml-1 flex-shrink-0 text-blue-400"
/>
)}
</>
) : (
<div className="flex items-center gap-1 text-negative-400">
<WarningCircle size={16} />
<span className="text-md">
{formatText({
id: 'actionSidebar.addressError',
})}
</span>
</div>
)}
</>
<AvatarWithAddress
userName={
selectedUser?.profile?.displayName?.toString() ?? undefined
}
address={userWalletAddress}
userAvatarSrc={selectedUser?.profile?.avatar ?? undefined}
size={20}
isVerified={selectedUser?.isVerified}
title={formatText(userName || '')}
/>
) : (
formatText({ id: 'actionSidebar.selectMember' })
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { type FC } from 'react';

import CreatedIn from '~v5/common/ActionSidebar/partials/CreatedIn/index.ts';
import DecisionMethodField from '~v5/common/ActionSidebar/partials/DecisionMethodField/index.ts';
import Description from '~v5/common/ActionSidebar/partials/Description/index.ts';
import { useIsFieldDisabled } from '~v5/common/ActionSidebar/partials/hooks.ts';
import { type ActionFormBaseProps } from '~v5/common/ActionSidebar/types.ts';

import { useCreateArbitraryTxs } from './hooks.ts';
import ArbitraryTransactionsTable from './partials/ArbitraryTransactionsTable/ArbitraryTransactionsTable.tsx';

const displayName = 'v5.common.ActionSidebar.partials.ArbitraryTxsForm';

const ArbitraryTxsForm: FC<ActionFormBaseProps> = ({ getFormOptions }) => {
const isFieldDisabled = useIsFieldDisabled();

useCreateArbitraryTxs(getFormOptions);

return (
<>
<DecisionMethodField disabled={isFieldDisabled} />
<CreatedIn />
<Description />
<ArbitraryTransactionsTable name="transactions" />
</>
);
};

ArbitraryTxsForm.displayName = displayName;

export default ArbitraryTxsForm;
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { array, type InferType, object, string } from 'yup';

import { ACTION_BASE_VALIDATION_SCHEMA } from '~v5/common/ActionSidebar/consts.ts';

export const validationSchema = object()
.shape({
title: string()
.trim()
.required(() => 'Please enter a title.'),
decisionMethod: string().defined(),
transactions: array()
.of(
object()
.shape({
contractAddress: string().defined(),
jsonAbi: string().defined(),
method: string().defined(),
amount: string().defined(),
to: string().defined(),
})
.defined(),
)
.required(),
})
.defined()
.concat(ACTION_BASE_VALIDATION_SCHEMA);

export type CreateArbitraryTxsFormValues = InferType<typeof validationSchema>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Id } from '@colony/colony-js';
import { useCallback, useMemo } from 'react';

import { ActionTypes } from '~redux/index.ts';
import { mapPayload } from '~utils/actions.ts';
import { sanitizeHTML } from '~utils/strings.ts';
import useActionFormBaseHook from '~v5/common/ActionSidebar/hooks/useActionFormBaseHook.ts';
import { type ActionFormBaseProps } from '~v5/common/ActionSidebar/types.ts';

import { validationSchema } from './consts.ts';

export const useCreateArbitraryTxs = (
getFormOptions: ActionFormBaseProps['getFormOptions'],
) => {
useActionFormBaseHook({
actionType: ActionTypes.CREATE_ARBITRARY_TRANSACTION,
validationSchema,
getFormOptions,
defaultValues: useMemo(
() => ({
createdIn: Id.RootDomain,
}),
[],
),
// eslint-disable-next-line react-hooks/exhaustive-deps
transform: useCallback(
mapPayload((payload) => {
const safeDescription = sanitizeHTML(payload.description || '');

return {
decisionMethod: payload.decisionMethod,
description: safeDescription,
customActionTitle: payload.title,
transactions: payload.transactions,
};
}),
[],
),
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ArbitraryTxsForm.tsx';
Loading
Loading