A miscalculation in market debt accounting within the Zaros protocol causes incorrect credit and debt allocations to vaults. The current implementation inverts the expected logic, leading to markets appearing in debt when they should be in credit, and vice versa. This issue stems from how credit deposits and net USD token issuance are combined in the debt formula.
The explanation of how the protocol should operate based on the specification given by the zaros team which is when traders realize a negative pnl, i.e they lose money in the perp engine, they send assets from their margin balance to cover the notional value of the loss. Those assets are credited to vaults delegating liquidity to the respective market that made the trader realize the loss,
so a vault is in credit when the net sum of its markets debt/credit realizations is negative, i.e it has received more assets than it had to issue usdTokens to cover profitable trades. It is in debt when that same value is positive, representing the inverse scenario (usdToken issuance greater than the usd val of assets collected). Debt value should be negative if credit deposits > net usd issuance.
This is not the case in the code as CreditDelegationBranch::depositcreditformarket has the following code:
Credit Deposits are incremented when the perp engine deposits a non-usdc or usd token of engine to the market making engine. The total market debt is calculated using this function:
Market:;getrealizeddebt is calculated with:
This realized debt calculates how much non-usdc tokens that are in the marketmakingengine contract which is Market::creditDepositsValueUsdX18 and 'adds' it to the Market::netusdtokenissuance. I say add in quotation because Market::netusdtokenissuance will always be negative. Market::netusdtokenissuance is updated whenever perp engine sends usd token of engine back to the marketmakingengine contract using CreditDelegationBranch::depositCreditForMarket. if usd token of engine is sent back to the contract, then that means that there is less usdz that zaros has to redeem 1:1 for usdc. If there is less usd token engine to account for, then zaros has less debt. Using the logic from the protocol, total debt is supposed to be positive if netusdissuance > credit deposits but instead the opposite is the case where market debt is positive when credit deposits > net usd token issuance.
Incorrect Debt and Credit Allocation: Instead of market debt being positive when netUsdTokenIssuance > creditDeposits, the current implementation inverts this logic, making credit deposits appear as market debt.
As a result, vaults inaccurately register as being in debt when they should be in credit, and vice versa.
Incorrect Reward Distribution: Since vaults earn rewards based on their credit participation, an incorrect debt calculation could lead to underpayment for vaults that should be in credit and overpayment to vaults that should be in debt.
This weakens the integrity of the reward distribution model, making the system unreliable for liquidity providers.
Manual Review, Foundry
To resolve this issue, the protocol should fix the calculation of market debt to correctly reflect the intended logic as per the protocol specification. The following steps should be implemented:
Correct the Debt Calculation in Market::getRealizedDebtUsd
Update the debt calculation formula so that debt is positive when netUsdTokenIssuance > creditDeposits, not the other way around. The correct formula should be:
Since netUsdTokenIssuance represents debt issuance, it should be positive when debt is high.creditDepositsValueUsdX18 represents assets held, which reduces the market’s net debt.
Modify the CreditDelegationBranch::depositCreditForMarket function so that USDC deposits correctly reduce debt and credit deposits increase the credit balance and do not incorrectly inflate the market’s debt.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.