Recumbent Shamrock Barracuda
High
The _calculateBuy
function calculates fees (protocolFee
and donation
) using msg.value
instead of the actual funds utilized for the purchase (fundsPaid
). This results in users being overcharged if msg.value
exceeds the amount needed to purchase the intended vote, and users who provide more ETH than needed to make the purchase lose more.
https://github.com/sherlock-audit/2024-11-ethos-network-ii/blob/main/ethos/packages/contracts/contracts/ReputationMarket.sol#L942-L960
The root cause lies in the misalignment between the fee calculation basis and the actual funds used:
- Fee Basis Mismatch: Fees are derived from
msg.value
(total ETH sent) rather thanfundsPaid
(ETH actually spent for votes). - Implementation Error: The call to
previewFees
usesfunds
(mapped tomsg.value
) without adjusting for the actual paid funds.
- Overpayment: Users providing excess ETH are charged disproportionately high fees, leading to financial inefficiency and user dissatisfaction.
-
Setup
- Assume UserA with
msg.value = 10 ETH
, UserB withmsg.value = 9 ETH
- Number of votes to buy = 5 votes.
fundsPaid
required for 5 votes = 8 ETH.(Assume that the number of votes that UserA and UserB can buy with the funds they pays is at most 5. In other words, since buying 6 votes requires 11 ETH, users can only buy 5 votes.)- Fee rate = 10%.
- Assume UserA with
-
Execution
- Fees are calculated on
msg.value
: UserA:protocolFee = 1 ETH
UserB:protocolFee = 0.9 ETH
- Actual funds used (
fundsPaid
) for votes:fundsPaid = 8 ETH
(UserA and UserB are the same.) - Total paid:
UserA:
totalPaid = 9 ETH
,overPaid = 9 - 8 - 8 * 0.1 = 0.2 ETH
UserB:totalPaid = 8.9 ETH
,overPaid = 8.9 - 8 - 8 * 0.1 = 0.1 ETH
- Fees are calculated on
-
Observed Behavior
- UserA and UserB spent more than necessary.
- UserA and UserB bought the same number of votes, but UserA paid more than UserB.
To address the identified issue in _calculateBuy
, the fee calculation logic should be corrected to ensure that fees are proportional to the actual funds spent on purchasing votes. Here’s the step-by-step mitigation strategy:
(The same as the calcFee
function of EthosVouch
contract.)
- Update calculation logic of
fundsAvailable
:fundsAvailable = funds * BASIS_POINTS_BASE / (BASIS_POINTS_BASE + entryProtocolFeeBasisPoints + donationBasisPoints)
- After the loop, calculate the final
protocolFee
anddonation
for the totalfundsPaid
:(protocolFee, donation) = previewFees(fundsPaid, true); fundsPaid += protocolFee + donation;