Pieces Protocol

First Flight #32
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: medium
Invalid

[M-01] Events are emmited before token transfers

Description:

Events should be the last occurence of each function to signal the success of the user action. TokenDivider emits events before an action is completed. For example, in the function TokenDivider::sellErc20() the event is emited before the transaction. If TokenDivider::sellErc20() fails at the token transaction, the function will revert. But, even though the function reverts, the event is already pushed giving the impression that the TokenDivider::sellErc20() succeded.

@> emit OrderPublished(amount, msg.sender, nftPegged);
IERC20(tokenInfo.erc20Address).transferFrom(msg.sender, address(this), amount);

The events should be placed last in the following functions:
TokenDivider::divideNft
TokenDivider::claimNft
TokenDivider::sellErc20
TokenDivider::buyOrder

Impact:

This issue happens every time a function reverts after the event is emited. Very often.

Proof of Concept:

function testMySell() public {
vm.startPrank(USER);
erc721Mock.approve(address(tokenDivider), TOKEN_ID);
tokenDivider.divideNft(address(erc721Mock), TOKEN_ID, AMOUNT);
tokenDivider.sellErc20(address(erc721Mock), 1e18, AMOUNT);
}

The function sellErc20() will revert after the event is emited (because the user did not aprove the transaction), as you can see in the logs.

├─ [117709] TokenDivider::sellErc20(ERC721Mock: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], 1000000000000000000 [1e18], 2000000000000000000 [2e18])
@> │ ├─ emit OrderPublished(amount: 2000000000000000000 [2e18], seller: user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D], nftPegged: ERC721Mock: [0x2e234DAe75C793f67A35089C9d99245E1C58470b])
│ ├─ [2966] ERC20ToGenerateNftFraccion::transferFrom(user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D], TokenDivider: [0x90193C961A926261B756D1E5bb255e67ff9498A1], 2000000000000000000 [2e18])
│ │ └─ ← [Revert] ERC20InsufficientAllowance(0x90193C961A926261B756D1E5bb255e67ff9498A1, 0, 2000000000000000000 [2e18])
│ └─ ← [Revert] ERC20InsufficientAllowance(0x90193C961A926261B756D1E5bb255e67ff9498A1, 0, 2000000000000000000 [2e18])
└─ ← [Revert] ERC20InsufficientAllowance(0x90193C961A926261B756D1E5bb255e67ff9498A1, 0, 2000000000000000000 [2e18])
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 8.30ms (1.96ms CPU time)

Recommended Mitigation:

For each event, change the order in the function. For example, at TokenDivider::sellErc20():

function sellErc20(address nftPegged, uint256 price,uint256 amount) external {
if(nftPegged == address(0)) {
revert TokenDivider__NftAddressIsZero();
}
if( amount == 0) {
revert TokenDivider__AmountCantBeZero();
}
ERC20Info memory tokenInfo = nftToErc20Info[nftPegged];
if(balances[msg.sender][tokenInfo.erc20Address] < amount) {
revert TokenDivider__InsuficientBalance();
}
balances[msg.sender][tokenInfo.erc20Address] -= amount;
s_userToSellOrders[msg.sender].push(
SellOrder({
seller: msg.sender,
erc20Address: tokenInfo.erc20Address,
price: price,
amount: amount
})
);
- emit OrderPublished(amount,msg.sender, nftPegged);
IERC20(tokenInfo.erc20Address).transferFrom(msg.sender,address(this), amount);
+ emit OrderPublished(amount,msg.sender, nftPegged);
}

The events need to be changed in the following functions:
TokenDivider::divideNft
TokenDivider::claimNft
TokenDivider::sellErc20
TokenDivider::buyOrder

Updates

Lead Judging Commences

fishy Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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