Platform: Code4rena
Start Date: 13/11/2023
Pot Size: $24,500 USDC
Total HM: 3
Participants: 120
Period: 4 days
Judge: 0xTheC0der
Id: 306
League: ETH
Rank: 108/120
Findings: 1
Award: $1.37
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: rvierdiiev
Also found by: 0x175, 0x3b, 0xMango, 0xarno, 0xpiken, Bauchibred, DarkTower, ElCid, Giorgio, HChang26, Kose, KupiaSec, Madalad, PENGUN, Pheonix, RaoulSchaffranek, SpicyMeatball, T1MOH, Tricko, Udsen, Yanchuan, aslanbek, ast3ros, bart1e, bin2chen, chaduke, d3e4, deepkin, developerjordy, glcanvas, inzinko, jasonxiale, jnforja, mahyar, max10afternoon, mojito_auditor, neocrao, nmirchev8, openwide, osmanozdemir1, peanuts, pep7siup, peritoflores, pontifex, rice_cooker, rouhsamad, t0x1c, tnquanghuy0512, turvy_fuzz, twcctop, ustas, vangrim, zhaojie, zhaojohnson
1.3743 USDC - $1.37
https://github.com/code-423n4/2023-11-canto/blob/335930cd53cf9a137504a57f1215be52c6d67cb3/1155tech-contracts/src/Market.sol#L150-L153 https://github.com/code-423n4/2023-11-canto/blob/335930cd53cf9a137504a57f1215be52c6d67cb3/1155tech-contracts/src/Market.sol#L174-L189 https://github.com/code-423n4/2023-11-canto/blob/335930cd53cf9a137504a57f1215be52c6d67cb3/1155tech-contracts/src/Market.sol#L141-L145 https://github.com/code-423n4/2023-11-canto/blob/335930cd53cf9a137504a57f1215be52c6d67cb3/1155tech-contracts/src/bonding_curve/LinearBondingCurve.sol#L14-L25
In this protocol, the buying or selling price of a share is calculated through a linear bonding curve. The higher the supply of shares in circulation, the more expensive they gradually become. This mechanic exposes users to front running attacks. If User A
wants to buy shares but User B
bought a substantial amount of shares before hand, the User A
will suffer from a price increase, and User B
can make a direct profit by selling the shares at an increased price. The same is applicable in a scenario where User A
wants to sell his shares, but receives a substantial lesser selling income because has been frontrunned by User B
.
Let's assume that the priceIncrease
variable is set to 2.
The initial supply of shares is 5.
Bob wants to buy 10 shares. Expected cost: 220
Alice quickly buys 20 shares before Bob's transaction is mined. Supply is now 25. (spent 630)
The increased supply due to Alice's transaction affects Bob's price for the transaction.
Bob ends up paying the actual cost of 660 instead of 220. 420 more than the expected price.
Alice can sell her shares for 1050. Alice profit amounts to 420.
Manual review
Set a slippage parameter to compare it against the price for selling or buying. Such as :
@> function buy(uint256 _id, uint256 _amount, uint256 maxPrice) external { require(shareData[_id].creator != msg.sender, "Creator cannot buy"); (uint256 price, uint256 fee) = getBuyPrice(_id, _amount); // Reverts for non-existing ID @> If(price + fee > maxPrice) revert PriceIsTooHigh(); SafeERC20.safeTransferFrom(token, msg.sender, address(this), price + fee); // The reward calculation has to use the old rewards value (pre fee-split) to not include the fees of this buy ...
@> function sell(uint256 _id, uint256 _amount, minSellPrice) external { (uint256 price, uint256 fee) = getSellPrice(_id, _amount); @> if(price - fee) < minSellPrice revert PriceIsTooLow(); // Split the fee among holder, creator and platform _splitFees(_id, fee, shareData[_id].tokensInCirculation); // The user also gets the rewards of his own sale (which is not the case for buys) uint256 rewardsSinceLastClaim = _getRewardsSinceLastClaim(_id); rewardsLastClaimedValue[_id][msg.sender] = shareData[_id].shareHolderRewardsPerTokenScaled; shareData[_id].tokenCount -= _amount; shareData[_id].tokensInCirculation -= _amount; tokensByAddress[_id][msg.sender] -= _amount; // Would underflow if user did not have enough tokens // Send the funds to the user SafeERC20.safeTransfer(token, msg.sender, rewardsSinceLastClaim + price - fee); emit SharesSold(_id, msg.sender, _amount, price, fee); }
MEV
#0 - c4-pre-sort
2023-11-18T10:33:44Z
minhquanym marked the issue as duplicate of #12
#1 - c4-judge
2023-11-28T23:14:14Z
MarioPoneder changed the severity to 2 (Med Risk)
#2 - c4-judge
2023-11-28T23:34:29Z
MarioPoneder marked the issue as satisfactory