Omniscia Evergon Labs Audit

ReceiveAllGatheredFundsFacet Manual Review Findings

ReceiveAllGatheredFundsFacet Manual Review Findings

RAG-01M: Incorrect State Transition

Description:

The referenced state transition is considered insecure as it will attempt to increase the state by one which might not always advanced to the desired post-receive state.

Impact:

The receive mechanism is incompatible with the type(uint256).max state due to an overflow error and generally relies on an assumption that can be trivially breached.

Example:

packages/contracts/contracts/internalFacets/receivePhaseFacets/doReceiveFacets/receiveAllGatheredFunds/ReceiveAllGatheredFundsFacet.sol
21function doReceive(uint256 campaignId, address account) external onlyInternalDelegateCall {
22 FungibleAndSemiFungiblePurchaseStorage.IdInfo memory info = FungibleAndSemiFungiblePurchaseStorage.layout().infoForId[
23 campaignId
24 ];
25 IERC20(info.fundingCurrency).safeTransfer(account, info.totalFundsGathered);
26
27 // The change of state here is extremely important, maybe it should be a different facet.
28 // The importance comes from the fact that if state does not change, receive can happen over and over again!
29 uint256 currentState = IStateFacet(address(this)).getStateOfId(campaignId);
30 IStateFacet(address(this)).changeState(campaignId, currentState, currentState + 1);
31
32 emit DoReceiveExecuted(campaignId, account);
33}

Recommendation:

We advise the system to reserve the 8 top bits of the state IDs by restricting state configurations to the uint248 data type, permitting special states such as buy-backs and post-receives to be represented as the maximum value of the uint256 data type and downward through constant declarations.

Alleviation (71cda4ccfdcfa25fb96a4565f1f8143b350dd246):

The system utilizes a storage-configured post-receive state in the latest implementation, permitting the Evergon Labs team to define a specific state that is considered to represent the "post-receive" state of a campaign thus addressing this exhibit.

RAG-02M: Inexistent Validation of Caller Authorization

Description:

The ReceiveAllGatheredFundsFacet::doReceive function does not apply any validation to its caller and the ReceiveSkeleton and ReceiveSkeletonNID contracts that integrate with it do not apply any either.

Impact:

Any caller can claim the raised funds of a campaign as long as it has achieved the post-funding state mandated by the SingleStateReceiveFacet::checkReceiveState function.

Example:

packages/contracts/contracts/internalFacets/receivePhaseFacets/doReceiveFacets/receiveAllGatheredFunds/ReceiveAllGatheredFundsFacet.sol
21function doReceive(uint256 campaignId, address account) external onlyInternalDelegateCall {
22 FungibleAndSemiFungiblePurchaseStorage.IdInfo memory info = FungibleAndSemiFungiblePurchaseStorage.layout().infoForId[
23 campaignId
24 ];
25 IERC20(info.fundingCurrency).safeTransfer(account, info.totalFundsGathered);
26
27 // The change of state here is extremely important, maybe it should be a different facet.
28 // The importance comes from the fact that if state does not change, receive can happen over and over again!
29 uint256 currentState = IStateFacet(address(this)).getStateOfId(campaignId);
30 IStateFacet(address(this)).changeState(campaignId, currentState, currentState + 1);
31
32 emit DoReceiveExecuted(campaignId, account);
33}

Recommendation:

We advise proper caller authorization to be imposed on either the ReceiveAllGatheredFundsFacet implementation or its parent callers, ensuring that the correct party acquires all raised funds of a particular campaignId.

Alleviation (71cda4ccfdcfa25fb96a4565f1f8143b350dd246):

All top-level callers of the ReceiveAllGatheredFundsFacet::doReceive function have been updated to impose proper access control, alleviating this exhibit as a result.