Closed code423n4 closed 1 year ago
0xA5DF marked the issue as primary issue
Might be the intended design of the protocol, will leave open for sponsor to comment
0xA5DF marked the issue as duplicate of #744
hansfriese marked the issue as unsatisfactory: Invalid
Lines of code
https://github.com/code-423n4/2023-04-frankencoin/blob/main/contracts/Equity.sol#L266-L270 https://github.com/code-423n4/2023-04-frankencoin/blob/main/contracts/Equity.sol#L290-L297
Vulnerability details
Bug Description
Share Distrbution
In the
Equity
contract, the amount of shares minted to a depositor is determined usingcalculateSharesInternal()
:Equity.sol#L266-L270
Where:
capitalBefore
- Total amount of Frankencoin in reserves before the deposit.investment
- Amount of Frankencoin deposited by the user.The amount of shares minted can be simplified into the following formula:
$$ shares = totalShares \times \biggl( \sqrt[3]{ 1 + \frac{investment}{capitalBefore} } - 1 \biggr) $$
Due to the cube root, users will receive less shares for depositing the same amount of Frankencoin when
capitalBefore
is larger. Assume the following:totalShares = 1000
capitalBefore = 1000
If a user deposits 1000 Frankencoin, he gains the following amount of shares:
$$ shares = 1000 \times \biggl( \sqrt[3]{ 1 + \frac{1000}{1000} } - 1 \biggr) \approx 260 $$
Now, if another user deposits 1000 Frankencoin, he will get less shares:
$$ shares = 1260 \times \biggl( \sqrt[3]{ 1 + \frac{1000}{2000} } - 1 \biggr) \approx 182 $$
Share Redemption
Similarly, the
calculateProceeds()
function is used to calculate how much Frankencoin a user should get for burning shares:Equity.sol#L290-L297
Where:
shares
- Amount of shares burned by the user.The amount of Frankencoin can be simplified into the formula below:
$$ proceeds = capital \times \biggl( 1 - \biggl( 1 - \frac{shares}{totalShares} \biggr)^3 \biggr)$$
Due to the power 3, users will receive less Frankencoin for burning the same amount of shares when
totalShares
is smaller. Assume the following:capital = 1000
totalShares = 1000
If a user redeems 100 shares, he gains the following amount of Frankencoin:
$$ proceeds = 1000 \times \biggl( 1 - \biggl( 1 - \frac{100}{1000} \biggr)^3 \biggr) = 271 $$
Now, if another user redeems 100 shares, he will get less Frankencoin:
$$ proceeds = 729 \times \biggl( 1 - \biggl( 1 - \frac{100}{900} \biggr)^3 \biggr) = 217 $$
Impact
When the total amount of Frankencoin in the
Equity
contract is large, depositors will gain less shares, thereby losing a portion of their assets. Likewise, when the total amount of shares is small, users that redeem shares will get less Frankencoin in return.As a user's amount of shares corresponds to his voting power, this also affects all voting functionality in the protocol.
Proof of Concept
The following code contains two tests:
test_ShareDistributionIsWrong()
demonstrates how the distribution of shares is incorrect.test_RedemptionIsWrong()
illustrates how the amount of Frankencoin gained from redemptions is incorrect.Recommendation
Consider simplifying both formulas:
totalShares * investment / capitalBefore
:capital * shares / totalShares
:Alternatively, use an existing implementation of ERC4626 to handle share accounting, such as Openzeppelin or solmate.