Skip to content

Commit

Permalink
feat: implement arbitrary transactions via reputation
Browse files Browse the repository at this point in the history
  • Loading branch information
Nortsova committed Dec 17, 2024
1 parent 76570c4 commit d583d63
Show file tree
Hide file tree
Showing 12 changed files with 255 additions and 14 deletions.
2 changes: 1 addition & 1 deletion docker/colony-cdapp-dev-env-block-ingestor
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FROM colony-cdapp-dev-env/base:latest

ENV BLOCK_INGESTOR_HASH=64e20a1a16df48a72bb5d65924495aa82ef4622e
ENV BLOCK_INGESTOR_HASH=6111c16657f2a3f6c95c6f88bbfc17f7d3fd7a78

# Declare volumes to set up metadata
VOLUME [ "/colonyCDapp/amplify/mock-data" ]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { Id } from '@colony/colony-js';
import { useMemo } from 'react';
import { useWatch } from 'react-hook-form';

import { useColonyContext } from '~context/ColonyContext/ColonyContext.ts';
import { ActionTypes } from '~redux/index.ts';
import { DecisionMethod } from '~types/actions.ts';
import { mapPayload } from '~utils/actions.ts';
import { extractColonyDomains } from '~utils/domains.ts';
import { sanitizeHTML } from '~utils/strings.ts';
import { DECISION_METHOD_FIELD_NAME } from '~v5/common/ActionSidebar/consts.ts';
import useActionFormBaseHook from '~v5/common/ActionSidebar/hooks/useActionFormBaseHook.ts';
import { type ActionFormBaseProps } from '~v5/common/ActionSidebar/types.ts';

Expand All @@ -15,10 +19,18 @@ export const useCreateArbitraryTxs = (
) => {
const {
colony: { colonyAddress },
colony,
} = useColonyContext();

const decisionMethod: DecisionMethod | undefined = useWatch({
name: DECISION_METHOD_FIELD_NAME,
});

useActionFormBaseHook({
actionType: ActionTypes.CREATE_ARBITRARY_TRANSACTION,
actionType:
decisionMethod === DecisionMethod.Permissions
? ActionTypes.CREATE_ARBITRARY_TRANSACTION
: ActionTypes.MOTION_ARBITRARY_TRANSACTION,
validationSchema,
getFormOptions,
defaultValues: useMemo(
Expand All @@ -30,13 +42,21 @@ export const useCreateArbitraryTxs = (
transform: mapPayload((payload) => {
const safeDescription = sanitizeHTML(payload.description || '');

return {
decisionMethod: payload.decisionMethod,
const commonPayload = {
annotationMessage: safeDescription,
customActionTitle: payload.title,
transactions: payload.transactions,
colonyAddress,
};

if (payload.decisionMethod === DecisionMethod.Reputation) {
return {
...commonPayload,
colonyDomains: extractColonyDomains(colony.domains),
};
}

return commonPayload;
}),
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,13 @@ export const useArbitraryTxsTableColumns = ({
size: isMobile ? 100 : 67,
}),
],
[columnHelper, isMobile, openTransactionModal, isError],
[
columnHelper,
isMobile,
openTransactionModal,
hasNoDecisionMethods,
isError,
],
);

return columns;
Expand Down
2 changes: 2 additions & 0 deletions src/components/v5/common/CompletedAction/CompletedAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ const CompletedAction = ({ action }: CompletedActionProps) => {
case ColonyActionType.ColonyEditMultisig:
return <EditColonyDetails action={action} />;
case ColonyActionType.MakeArbitraryTransaction:
case ColonyActionType.MakeArbitraryTransactionsMotion:
return <ArbitraryTransaction action={action} />;
/**
* @deprecated
Expand Down Expand Up @@ -149,6 +150,7 @@ const CompletedAction = ({ action }: CompletedActionProps) => {
case ColonyActionType.ReleaseStagedPaymentsMotion:
case ColonyActionType.EditExpenditureMotion:
case ColonyActionType.FundExpenditureMotion:
case ColonyActionType.MakeArbitraryTransactionsMotion:
// @NOTE: Enabling expenditure-related motions above temporarily (action UI will be missing)
return <Motions transactionId={action.transactionHash} />;
// @todo: reorganize folder structure after all of the advanced payments will be ready
Expand Down
3 changes: 3 additions & 0 deletions src/redux/actionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ export enum ActionTypes {
MOTION_MANAGE_TOKENS = 'MOTION_MANAGE_TOKENS',
MOTION_MANAGE_TOKENS_SUCCESS = 'MOTION_MANAGE_TOKENS_SUCCESS',
MOTION_MANAGE_TOKENS_ERROR = 'MOTION_MANAGE_TOKENS_ERROR',
MOTION_ARBITRARY_TRANSACTION = 'MOTION_ARBITRARY_TRANSACTION',
MOTION_ARBITRARY_TRANSACTION_SUCCESS = 'MOTION_ARBITRARY_TRANSACTION_SUCCESS',
MOTION_ARBITRARY_TRANSACTION_ERROR = 'MOTION_ARBITRARY_TRANSACTION_ERROR',

// Multisig
MULTISIG_VOTE = 'MULTISIG_VOTE',
Expand Down
File renamed without changes.
2 changes: 2 additions & 0 deletions src/redux/sagas/actions/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { all, call } from 'redux-saga/effects';

import arbitraryTxActionSaga from './arbitraryTx.ts';
import createDomainActionSaga from './createDomain.ts';
import editColonyActionSaga from './editColony.ts';
import editDomainActionSaga from './editDomain.ts';
Expand Down Expand Up @@ -33,5 +34,6 @@ export default function* actionsSagas() {
call(manageExistingSafesActionSaga),
call(initiateSafeTransactionSaga),
call(manageVerifiedMembersActionSaga),
call(arbitraryTxActionSaga),
]);
}
7 changes: 0 additions & 7 deletions src/redux/sagas/arbitraryTxs/index.ts

This file was deleted.

195 changes: 195 additions & 0 deletions src/redux/sagas/motions/arbitraryTx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import { ClientType, Id } from '@colony/colony-js';
import { AddressZero } from '@ethersproject/constants';
import { BigNumber } from 'ethers';
import { Interface } from 'ethers/lib/utils';
import { call, fork, put, takeEvery } from 'redux-saga/effects';

import { type ColonyManager } from '~context/index.ts';
import { ActionTypes } from '~redux/actionTypes.ts';
import { type AllActions, type Action } from '~redux/types/actions/index.ts';
import { TRANSACTION_METHODS } from '~types/transactions.ts';

import {
createTransaction,
createTransactionChannels,
getTxChannel,
waitForTxResult,
} from '../transactions/index.ts';
import {
putError,
takeFrom,
getColonyManager,
uploadAnnotation,
initiateTransaction,
createActionMetadataInDB,
getChildIndexLocal,
} from '../utils/index.ts';

function* arbitraryTxMotion({
payload: {
colonyAddress,
annotationMessage,
customActionTitle,
colonyDomains,
transactions,
},
meta: { id: metaId, setTxHash },
meta,
}: Action<ActionTypes.MOTION_ARBITRARY_TRANSACTION>) {
let txChannel;
try {
const colonyManager: ColonyManager = yield getColonyManager();

const contractAddresses: string[] = [];
const methodsBytes: string[] = [];

transactions.forEach(({ contractAddress, ...item }) => {
try {
const encodedFunction = new Interface(item.jsonAbi).encodeFunctionData(
item.method,
item.args?.map((arg) => arg.value),
);
contractAddresses.push(contractAddress);
methodsBytes.push(encodedFunction);
} catch (e) {
console.error(e);
}
});

const colonyClient = yield colonyManager.getClient(
ClientType.ColonyClient,
colonyAddress,
);

const encodedAction = colonyClient.interface.encodeFunctionData(
'makeArbitraryTransactions',
[contractAddresses, methodsBytes, true],
);

txChannel = yield call(getTxChannel, metaId);

// setup batch ids and channels
const batchKey = TRANSACTION_METHODS.CreateMotion;

const { createMotion, annotateRootMotion } =
yield createTransactionChannels(metaId, [
'createMotion',
'annotateRootMotion',
]);

// eslint-disable-next-line no-inner-declarations
function* getCreateMotionParams() {
const rootDomain = colonyDomains.find((domain) =>
BigNumber.from(domain.nativeId).eq(Id.RootDomain),
);

if (!rootDomain) {
throw new Error('Cannot find rootDomain in colony domains');
}

const childSkillIndex = yield call(getChildIndexLocal, {
networkClient: colonyClient.networkClient,
parentDomainNativeId: rootDomain.nativeId,
parentDomainSkillId: rootDomain.nativeSkillId,
domainNativeId: rootDomain.nativeId,
domainSkillId: rootDomain.nativeSkillId,
});
const { skillId } = yield call(
[colonyClient, colonyClient.getDomain],
Id.RootDomain,
);

const { key, value, branchMask, siblings } = yield call(
colonyClient.getReputation,
skillId,
AddressZero,
);

return {
context: ClientType.VotingReputationClient,
methodName: 'createMotion',
identifier: colonyAddress,
params: [
Id.RootDomain,
childSkillIndex,
AddressZero,
encodedAction,
key,
value,
branchMask,
siblings,
],
group: {
key: batchKey,
id: metaId,
index: 0,
},
ready: false,
};
}

const transactionParams = yield getCreateMotionParams();
// create transactions
yield fork(createTransaction, createMotion.id, transactionParams);

if (annotationMessage) {
yield fork(createTransaction, annotateRootMotion.id, {
context: ClientType.ColonyClient,
methodName: 'annotateTransaction',
identifier: colonyAddress,
params: [],
group: {
key: batchKey,
id: metaId,
index: 1,
},
ready: false,
});
}

yield takeFrom(createMotion.channel, ActionTypes.TRANSACTION_CREATED);
if (annotationMessage) {
yield takeFrom(
annotateRootMotion.channel,
ActionTypes.TRANSACTION_CREATED,
);
}

yield initiateTransaction(createMotion.id);

const {
payload: {
receipt: { transactionHash: txHash },
},
} = yield waitForTxResult(createMotion.channel);

yield createActionMetadataInDB(txHash, customActionTitle);

if (annotationMessage) {
yield uploadAnnotation({
txChannel: annotateRootMotion,
message: annotationMessage,
txHash,
});
}

setTxHash?.(txHash);

yield put<AllActions>({
type: ActionTypes.MOTION_ARBITRARY_TRANSACTION_SUCCESS,
meta,
});
} catch (caughtError) {
yield putError(
ActionTypes.MOTION_ARBITRARY_TRANSACTION_ERROR,
caughtError,
meta,
);
} finally {
txChannel.close();
}
}

export default function* arbitraryTxMotionSaga() {
yield takeEvery(ActionTypes.MOTION_ARBITRARY_TRANSACTION, arbitraryTxMotion);
}
2 changes: 2 additions & 0 deletions src/redux/sagas/motions/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { all, call } from 'redux-saga/effects';

import arbitraryTxMotionSaga from './arbitraryTx.ts';
import claimAllMotionRewardsSaga from './claimAllMotionRewards.ts';
import claimMotionRewardsSaga from './claimMotionRewards.ts';
// import escalateMotionSaga from './escalateMotion';
Expand Down Expand Up @@ -55,5 +56,6 @@ export default function* actionsSagas() {
call(manageReputationMotionSaga),
call(initiateSafeTransactionMotionSaga),
call(manageTokensMotionSaga),
call(arbitraryTxMotionSaga),
]);
}
2 changes: 0 additions & 2 deletions src/redux/sagas/setupUserContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { ActionTypes } from '../actionTypes.ts';
import { type AllActions } from '../types/actions/index.ts';

import actionsSagas from './actions/index.ts';
import arbitraryTxActionSaga from './arbitraryTxs/arbitraryTx.ts';
import colonyFinishCreateSaga from './colony/colonyFinishCreate.ts';
import colonySagas, { colonyCreateSaga } from './colony/index.ts';
import decisionsSagas from './decisions/index.ts';
Expand Down Expand Up @@ -61,7 +60,6 @@ function* setupContextDependentSagas() {
// call(vestingSagas),
call(setupUsersSagas),
call(expendituresSagas),
call(arbitraryTxActionSaga),
call(setupTransactionsSaga),
/**
* We've loaded all the context sagas, so we can proceed with redering
Expand Down
20 changes: 20 additions & 0 deletions src/redux/types/actions/motion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
type Address,
type ManageVerifiedMembersOperation,
} from '~types/index.ts';
import { type AddTransactionTableModel } from '~v5/common/ActionSidebar/partials/forms/ArbitraryTxsForm/types.ts';

import { type OneTxPaymentPayload } from './colonyActions.ts';
import {
Expand Down Expand Up @@ -470,4 +471,23 @@ export type MotionActionTypes =
| ActionTypeWithMeta<
ActionTypes.MOTION_MANAGE_TOKENS_SUCCESS,
MetaWithSetter<object>
>
| UniqueActionType<
ActionTypes.MOTION_ARBITRARY_TRANSACTION,
{
customActionTitle: string;
colonyAddress: Address;
colonyName?: string;
colonyDomains: Domain[];
colonyRoles: ColonyRoleFragment[];
annotationMessage?: string;

transactions: AddTransactionTableModel[];
},
MetaWithSetter<object>
>
| ErrorActionType<ActionTypes.MOTION_ARBITRARY_TRANSACTION_ERROR, object>
| ActionTypeWithMeta<
ActionTypes.MOTION_ARBITRARY_TRANSACTION_SUCCESS,
MetaWithSetter<object>
>;

0 comments on commit d583d63

Please sign in to comment.