Project

One World
NFTDeFi
15,000 USDC
View results
Submission Details
Severity: high
Invalid

No protection against first depositor attack

Summary

Let's take a look at how the distribution of awards to DAO participants works. Specifically, what rewards a user receives when joining.

User's rewards are updated when the saveProfit function is called.
When a user joins Dao, the order of calling internal functions is as follows:
1)saveProfit

2)_update

function getUnsaved(address account) internal view returns (uint256 profit) {
// @audit shareOf account = 0 because update called after save
return ((totalProfit - lastProfit[account]) * shareOf(account)) / ACCURACY;
}
function saveProfit(address account) internal returns (uint256 profit) {
uint256 unsaved = getUnsaved(account);
lastProfit[account] = totalProfit;
profit = savedProfit[account] + unsaved;
savedProfit[account] = profit;
}
function _update(
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts
) internal virtual override(ERC1155Upgradeable) {
if (from != address(0)) saveProfit(from);
if (to != address(0)) saveProfit(to);
super._update(from, to, ids, amounts);
}

That is, the user's NFT balances are updated after their reward has been calculated. I.e. at the moment when the rewards are calculated in saveProfit shareOf(user) = 0 => saved = 0, but lastProfit will be updated to the current totalProfit.

In short, the user only gets his rewards for the sendProfit called after the user joins DAO, and does not get rewards for the total Profit distributed before that.

This opens up the possibility of sandwiching the first sendProfit from any person if there are no members in the DAO yet (which is very likely, since it costs money to join the DAO and no Profit yet). In order to take the entire first sendProfit - he would need the following sequence of transactions.

  1. mint at least 1 share

  2. send profit

Then he can call claimProfit at any time - the whole first sendProfit will be his.

Vulnerability Details

In my opinion, the root cause of this problem is a slightly inefficient system of award distribution. This system strictly depends on the order in which transactions are called, which can be easily manipulated. Thus, a large share holder will not receive any rewards until the next sendProfit after his mint is called, while a participant with 1 share will receive the entire first sendProfit.

Impact

Since the protocol allows frontrunning send profit, as stated in the H-01 Cyfrin Audit Report - it needs to at least defend against the first deposit attack.

I would like to emphasise the difference between this error and the error described in H-01. Error H-01 talks about frontrunning in general case and manipulation of profit, but not full loss of it and suggests as mitigation to add delay on reward distribution when joining DAO.

This error also uses sendProfit frontrunning, but speaks about the specific case of the first sendProfit frontrunning when the number of DAO shares = 0 (totalSupply = 0)

In this case, the intended behaviour of the protocol is to send the entire sendProfit to the creator address. However, because of the frontrunning, all profit will go to the first depositor, regardless of his number of shares >= 1.

Since the attack is very easy to execute, doesn't cost much and can result in a significant loss of funds - high

Tools Used

Manual Review

Recommendations

As is done in many contracts like ERC4626 - add protection against first depositor attack. The easiest way to do it is to print fictitious shares (just initialise totalSupply not with 0 number, but conditionally with 10). In this case the attacker will get not the whole profit, but only profit / 10.

Also, if we are talking about a more global change - you can change the reward distribution system to Masterchef Staking Algorithm - it will take into account time-weighted contribution of each user to DAO.

Updates

Lead Judging Commences

0xbrivan2 Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

bengalcatbalu Submitter
about 1 year ago
0xbrivan2 Lead Judge
about 1 year ago
0xbrivan2 Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!