Skip to content

Commit

Permalink
feat: calculate the colony creation salt and validate it
Browse files Browse the repository at this point in the history
  • Loading branch information
bassgeta committed Dec 23, 2024
1 parent 9f9904a commit cea3572
Show file tree
Hide file tree
Showing 4 changed files with 1,868 additions and 3,071 deletions.
16 changes: 9 additions & 7 deletions apps/main-chain/src/handlers/colonies/colonyAdded.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import { tryFetchGraphqlQuery } from '~utils/graphql';
import { createUniqueColony } from './helpers/createUniqueColony';
import { output } from '@joincolony/utils';
import rpcProvider from '~provider';
import networkClient from '~networkClient';
import { getTransactionSignerAddress } from '~utils/transactions';
import { getColonyCreationSalt } from './helpers/validateCreationSalt';

export default async (event: ContractEvent): Promise<void> => {
const { transactionHash, args, blockNumber } = event;
Expand Down Expand Up @@ -71,19 +74,18 @@ export default async (event: ContractEvent): Promise<void> => {
0,
ContractEventsSignatures.ColonyRoleSet.indexOf('('),
);

const {
args: { user: colonyFounderAddress },
} = events.find((event) => event?.name === ColonyRoleSetEventName) ?? {
args: { user: '' },
};

let signerAddress = '';
let generatedColonySalt = '';

// We don't really want to block colony creation if there's something wrong with salt calculation
try {
const transaction = await rpcProvider
.getProviderInstance()
.getTransaction(transactionHash);
signerAddress = transaction.from;
generatedColonySalt =
(await getColonyCreationSalt(blockNumber, transactionHash)) || '';
} catch (error) {
// Most likely there was an error retrieving this transaction
}
Expand All @@ -99,7 +101,7 @@ export default async (event: ContractEvent): Promise<void> => {
initiatorAddress: utils.getAddress(colonyFounderAddress),
colonyCreateEvent: {
blockNumber,
signerAddress,
creationSalt: generatedColonySalt,
},
});
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { verbose } from '@joincolony/utils';
import { utils } from 'ethers';
import rpcProvider from '~provider';
import networkClient from '~networkClient';
import { getTransactionSignerAddress } from '~utils/transactions';

const ContractCreationEvents = {
Create3ProxyContractCreation: 'Create3ProxyContractCreation(address,bytes32)',
};

export const getColonyCreationSalt = async (
blockNumber: number,
transactionHash: string,
): Promise<string | null> => {
const create3ProxyLogs = await rpcProvider.getProviderInstance().getLogs({
fromBlock: blockNumber,
toBlock: blockNumber,
topics: [utils.id(ContractCreationEvents.Create3ProxyContractCreation)],
});

if (create3ProxyLogs.length === 0) {
verbose(`Couldn't fetch colony proxy contract creation events`);
return null;
}

const create3ProxySalt = create3ProxyLogs[0].topics[2];

if (!create3ProxySalt) {
verbose(
`The Create3ProxyContractCreation log doesn't have the salt data: ${JSON.stringify(create3ProxyLogs[0], null, 2)}`,
);
return null;
}

const transaction = await rpcProvider
.getProviderInstance()
.getTransaction(transactionHash);

const signerAddress = getTransactionSignerAddress(transaction);

if (!signerAddress) {
verbose(
`Couldn't find the signer for transaction with txHash: ${transactionHash}`,
);
return null;
}

const generatedColonySalt = await networkClient.getColonyCreationSalt({
blockTag: blockNumber,
from: signerAddress,
});

const guardedSalt = utils.keccak256(
utils.defaultAbiCoder.encode(
['bytes32', 'bytes32'],
[
utils.hexZeroPad(networkClient.address, 32),
generatedColonySalt, // Actual salt
],
),
);

if (guardedSalt !== create3ProxySalt) {
verbose(
`The network salt doesn't match the salt used when creating the colony!`,
);
return null;
}

return generatedColonySalt;
};
23 changes: 23 additions & 0 deletions apps/main-chain/src/utils/transactions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { utils, Transaction } from 'ethers';

const MetatransactionInterface = new utils.Interface([
'function executeMetaTransaction(address userAddress, bytes memory payload, bytes32 sigR, bytes32 sigS, uint8 sigV) external payable returns (bytes memory)',
]);

export const getTransactionSignerAddress = (
transaction: Transaction,
): string | undefined => {
let signerAddress = transaction.from;

try {
const metaTx = MetatransactionInterface.parseTransaction({
data: transaction.data,
value: transaction.value,
});
signerAddress = metaTx.args[0];
} catch (error) {
// if it's an error, it just means it's not a metatransaction
}

return signerAddress;
};
Loading

0 comments on commit cea3572

Please sign in to comment.