Loud Onyx Perch
Medium
When the protocol calculates a vote price, it computes the costRatio
then which is divided by the price's base price, so some rounding should be made here, currently, the protocol rounds the answer down for TRUST
votes:
cost = positiveCostRatio.mulDiv(
market.basePrice,
1e18,
isPositive ? Math.Rounding.Floor : Math.Rounding.Ceil
);
On the other hand, in _calcVotePrice
, which is used for the same goal but represents a midpoint between the next marginal transaction, the comment clearly states that the answer should be rounded up for TRUST
votes:
function _calcVotePrice(Market memory market, bool isPositive) private pure returns (uint256) {
// odds are in a ratio of N / 1e18
uint256 odds = LMSR.getOdds(
market.votes[TRUST],
market.votes[DISTRUST],
market.liquidityParameter,
isPositive
);
// multiply odds by base price to get price; divide by 1e18 to get price in wei
@> // round up for trust, down for distrust so that prices always equal basePrice
return
odds.mulDiv(market.basePrice, 1e18, isPositive ? Math.Rounding.Floor : Math.Rounding.Ceil);
}
This results in misleading vote costs being returned to the users.
Inversed rounding conditions in both _calcVotePrice
and _calcCost
:
odds.mulDiv(market.basePrice, 1e18, isPositive ? Math.Rounding.Floor : Math.Rounding.Ceil);
https://github.com/sherlock-audit/2024-12-ethos-update/blob/main/ethos/packages/contracts/contracts/ReputationMarket.sol#L1001-L1003 https://github.com/sherlock-audit/2024-12-ethos-update/blob/main/ethos/packages/contracts/contracts/ReputationMarket.sol#L1054-L1058
No response
No response
No response
The wrong vote price/cost will be returned.
Add the following test in ethos/packages/contracts/test/reputationMarket/rep.fees.test.ts
:
describe('PoC', () => {
it('vote price rounding inveresed', async () => {
const user1 = ethosUserA.signer;
await reputationMarket
.connect(user1)
.buyVotes(DEFAULT.profileId, true, 1, 1, { value: DEFAULT.creationCost });
expect(await reputationMarket.getVotePrice(DEFAULT.profileId, true)).to.be.equal(
5002499999791666n, // Rounded down
);
});
});
Inverse the rounding in both _calcVotePrice
and _calcCost
.
function _calcVotePrice(Market memory market, bool isPositive) private pure returns (uint256) {
// ...
// multiply odds by base price to get price; divide by 1e18 to get price in wei
// round up for trust, down for distrust so that prices always equal basePrice
- return odds.mulDiv(market.basePrice, 1e18, isPositive ? Math.Rounding.Floor : Math.Rounding.Ceil);
+ return odds.mulDiv(market.basePrice, 1e18, isPositive ? Math.Rounding.Ceil : Math.Rounding.Floor);
}
function _calcCost(
Market memory market,
bool isPositive,
bool isBuy,
uint256 amount
) private pure returns (uint256 cost) {
// ...
cost = positiveCostRatio.mulDiv(
market.basePrice,
1e18,
- isPositive ? Math.Rounding.Floor : Math.Rounding.Ceil
+ isPositive ? Math.Rounding.Ceil : Math.Rounding.Floor
);
}