Two-band commission and the minimum fee are not implemented on-chain
The funding model assumes a two-band commission with a minimum fee, but the contract charges a single flat rate with no floor. The pool accrues materially less than the solvency model provides for, and the shortfall grows with the share of small deals.
Description
The funding model depends on two structures the contract does not implement: a two-band seller commission — 2% on deals up to 5,000,000 cNGN with a 5,000 cNGN minimum where the percentage fee is lower, and 1.5% above — and a resulting pool income of roughly 0.40% of GMV at the standard rate, falling to 0.30% above the upper band.
The implementation is a single flat rate with no banding and no floor:
uint256 public sellerCommissionBps = 150; // 1.5%uint256 public poolShareOfCommissionBps = 2000; // 20%function _poolShareForDeal(uint256 dealValue) internal view returns (uint256) {uint256 commission = (dealValue * sellerCommissionBps) / BPS;return (commission * poolShareOfCommissionBps) / BPS;}
setCommissionRates sets one global pair of values, so the two rates cannot coexist and the minimum-fee floor cannot be expressed at all.
Vulnerable Scenario: The following steps illustrate the issue:
- Launch proceeds on the deployed defaults, so pool income is 0.30% of GMV rather than the 0.40% the model assumes for standard-rate volume — a 25% shortfall against plan before any deal is disputed.
- A 100,000 cNGN deal is created. The model expects the 5,000 cNGN minimum commission to bind, giving the pool 20% x 5,000 = 1,000 cNGN.
_poolShareForDealinstead computes 0.3% x 100,000 = 300 cNGN.- Break-even dispute rate on that deal size moves from the modelled figure to 3.75% at the deployed rate, against a planning assumption of 5%.
Impact
The pool accrues materially less than the solvency model provides for, in the area the design is most sensitive to, and the shortfall grows with the proportion of small deals.
Commission collection is additionally best-effort: EscrowFactory._createEscrow wraps the pool call in try/catch and emits CommissionSkipped when the treasury allowance is short, so a deal can be created having contributed nothing while still consuming pool capacity if it later disputes.
Recommendation
Implement banded accrual in _poolShareForDeal: select the commission rate by comparing dealValue against a governance-settable band boundary, and apply a governance-settable minimum commission so the floor binds on small deals as the model requires. Reconciling the difference off-chain instead would leave pool solvency dependent on a recurring manual top-up with no on-chain trigger and no alarm if it lapses.
Gate commission accrual on funded value before adding the minimum-fee floor. createEscrow is permissionless and pulls the accrual from commissionPayer on a caller-declared nominal value, so a floor applied first converts that path from a near-zero rounding effect into a fixed-rate drain on the treasury of minCommission x poolShareOfCommissionBps per call. This ordering is a correctness dependency, not a preference.
Resolution
Fixed. Two-band commission is implemented with bandEdge, upperBps and a 5,000 minimum, plus a guard preventing the upper band from exceeding the standard rate.
Affected files
contracts/ArbitrationPool.sol#L190-L191contracts/ArbitrationPool.sol#L362-L367contracts/ArbitrationPool.sol#L1252-L1255contracts/EscrowFactory.sol#L268-L276