Tangy Tortilla Fox
Medium
_calcCost
is used to get the price for each sell/buy. Where that function is made to round up or down, depending on if the user buys TRUST or DISTRUST.
cost = positiveCostRatio.mulDiv(
market.basePrice, // 0.01
1e18,
isPositive ? Math.Rounding.Floor : Math.Rounding.Ceil
);
However that rounding is wrong and the function should round depending on if the user buys or sells votes. As currently on every TRUST sell/buy would round down and with every DISTRUST buy/sell it would round up, possibly with time causing the contracts to be insolvent, more on it bellow.
In order for insolvency to be caused there must be a pool with 0 creationCost
. That is not only possible, but likely as admins can create pools with whatever creation cost they want, which intuitively suggest that they would create them with 0 creationCost
, as what's the point in spending the system funds on markets when they can utilize them in another way.
} else {
// when an admin creates a market, there is no minimum creation cost; use whatever they sent
marketFunds[profileId] = msg.value;
}
Inside this market, if insolvency is achieved the funds would be taken from other markets, causing another market to become insolvent. And if that market also has 0 creationCost
(many market will be made this way, especially if admins need to make some for mock profiles) these funds would be taken from the buy/sell amount. This means that if the users from the second market decide to sell their votes, all but one last user would be able to exit it.
_calcCost
rounds based on if the user buys TRUST or DISTRUST
function _calcCost(
Market memory market,
bool isPositive,
bool isBuy,
uint256 amount
) private pure returns (uint256 cost) {
uint256[] memory voteDelta = new uint256[](2);
if (isBuy) {
if (isPositive) {
voteDelta[0] = market.votes[TRUST] + amount;
voteDelta[1] = market.votes[DISTRUST];
} else {
voteDelta[0] = market.votes[TRUST];
voteDelta[1] = market.votes[DISTRUST] + amount;
}
} else {
if (isPositive) {
voteDelta[0] = market.votes[TRUST] - amount;
voteDelta[1] = market.votes[DISTRUST];
} else {
voteDelta[0] = market.votes[TRUST];
voteDelta[1] = market.votes[DISTRUST] - amount;
}
}
// cost(new) - cost(old)
int256 costRatio = LMSR.getCost(
market.votes[TRUST],
market.votes[DISTRUST],
voteDelta[0],
voteDelta[1],
market.liquidityParameter
);
uint256 positiveCostRatio = costRatio > 0 ? uint256(costRatio) : uint256(costRatio * -1);
cost = positiveCostRatio.mulDiv(
market.basePrice, // 0.01
1e18,
isPositive ? Math.Rounding.Floor : Math.Rounding.Ceil
);
}
- An admin creates a market with 0
creationFee
- Users to use the admin created pool
- Admin creates a few markets for big entities/personas with 0 creation cost
- Users use these markets
- After some time every users sells all their votes from a certain market
- Due to
_calcCost
rounding he function would round a few wei down - The other expensive market becomes insolvent, as the few wei comes from it's own buy/sell funds
- It's last user is unable to withdraw
Pools may become insolvent, or the least take funds from other pools, breaking the core invariant
No response
Fix is extremely simple, just change from isPositive
to isBuy
in order to rounds up on buys and down on sells.
cost = positiveCostRatio.mulDiv(
market.basePrice,
1e18,
- isPositive ? Math.Rounding.Floor : Math.Rounding.Ceil
+ isBuy ? Math.Rounding.Ceil : Math.Rounding.Floor
);