Open howlbot-integration[bot] opened 2 months ago
For more info and context, please check comments at this
https://github.com/code-423n4/2024-07-traitforge-validation/issues/1014
If I understand it correctly then it’s not an issue. I don’t see why myself or anybody else will do this. Especially using a sequencer based tx system like base. This is not an issue to me, not sure abt everybody else
@TForge1 Thank you for your feedback. In this scenario, the sequencer is irrelevant. This is on EVM level and applicable on all chains.
To summarize this for better assessment:
Scenario:
Impact: Developers may lose rewards.
Root cause: The issue occurs because the contract updates the user's rewardDebt after transferring funds to the developer.
Note from the warden: This scenario is considered likely because developers are expected to want to support and participate in the protocol actively.
For clearer picture of the technical flow, here is a diagram:
sequenceDiagram
participant DeveloperContract
participant DevFund
participant NFTContract
DeveloperContract->>DevFund: claim() rewards
activate DevFund
DevFund->>DeveloperContract: transfer rewards
activate DeveloperContract
Note over DeveloperContract: receive() function triggered
DeveloperContract->>NFTContract: mintWithBudget()
NFTContract->>DevFund: receive() (minting fees)
DevFund->>DevFund: Update totalRewardDebt
deactivate DeveloperContract
DevFund->>DevFund: Update developer's info.rewardDebt
deactivate DevFund
Note over DevFund: Developer's info.rewardDebt now higher than expected
@LyuboslavVeliev Please confirm or correct me if I'm wrong
koolexcrypto marked the issue as primary issue
ok fair enough. I guess it's an issue then. not sure if ill do this but if I do then it can be an issue.
@koolexcrypto Thank you for summarization. I just want to point out that #1061 describes a different scenario that is unlikely to happen. If you just look at his issue I don't think you would have validated it because it says that when the dev claims his rewards, then he distributes it back to the DevFund
. Why would a dev do that? "As a result the dev loses rewards" - yeah he loses the rewards that he distributed back to the DevFund
which means he doesn't want to receive rewards so there is no issue overall. Please correct me if I am wrong but I don't think it should be duped with this one since the likelihood of his issue is almost zero. Even though he found the root cause of the issue he doesn't demonstrate how it can be bad for the protocol.
@koolexcrypto, so we are assuming that the protocol would have a receive()
function to mint NFTs automatically? Why would that be a likely thing to happen? It was never mentioned that they would be minting NFTs and reenter into their own contract, that can't possibly be more than a QA. It classifies as a few different things - out of scope and user error, not only is it user error but it is actually owner error and owners are supposed to be much more knowledgeable than users.
DevFund
- a contract that is in-scope.ok fair enough. I guess it's an issue then. not sure if ill do this but if I do then it can be an issue.
There is a middle solution, implement a sweep function under restrictive condition, for instance, when there is no NFT to be nuked any longer. This way, rug-pull is not possible. You could also add to it a timeframe that has to pass first as a delay before sweeping even if all NFTs are nuked.
koolexcrypto marked the issue as satisfactory
No further changes on the dups. Looked at it already.
koolexcrypto marked the issue as selected for report
Lines of code
https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/DevFund/DevFund.sol#L69 https://github.com/code-423n4/2024-07-traitforge/blob/main/contracts/DevFund/DevFund.sol#L74
Vulnerability details
Summary
A developer may lose rewards if the
receive
function is triggered during the reward claim process fromDevFund
, resulting in theirinfo.rewardDebt
being higher than expected. This scenario can occur when a developer claims their rewards and then immediately mints an NFT with the funds, but the airdrop hasn’t yet started, they stand to lose those rewards. This happens because the system updates the user'srewardDebt
after the callback.Vulnerability Details
When claiming the dev's rewards are calculated as follows:
uint256 pending = info.pendingRewards + (totalRewardDebt - info.rewardDebt) * info.weight;
Then we do a call to that dev to transfer his funds and only after that call do we update his reward debt to the total reward debt.
This can create a flow in which the developer loses a portion of his rewards. For example if the dev has a contract that receives his rewards and wants to use that money to then buy an NFT, as he believes in the protocol and wants to support it, he would have a
receive
function like this:However in this scenario the
DevFund::receive
function will be triggered (if the airdrop hasn't started) which increasestotalRewardDebt
and only after that the dev's info is updated to thetotalRewardDebt
, which means that the user will later receive less rewards becauseinfo.rewardDebt
will include the new rewards from the received minting fees but the dev didn't claim them.Proof of Concept
This is
DevFund::claim()
:We can see that the
info.rewardDebt
happens after the call to the dev so now if the dev immediately calls the TraitForgeNft contract like that:This will mint the developer some NFTs and for each minted NFT the
DevFund::receive()
will be triggered. The minting inTraitForgeNft
distributes the fees to theNukeFund
contract and if the airdrop hasn't started yet, the rewards are then redirected to theDevFund
contract which triggers thereceive
function and thetotalRewardDebt
is updated:Only after these steps we update the reward debt of the dev, which will be much higher so the developer loses the rewards from this minting fees.
!NOTE This scenario is more than likely to happen because the developers will want to support the protocol and participate in it.
Imapct
A developer loses a portion of his rewards.
Tools Used
Manual Review
Recommended Mitigation Steps
Follow the CEI pattern and transfer the dev's funds only after updating all the state variables in
DevFund::claim()
.Assessed type
Other