A simulated liquidation on Base failed at every size from 55% of the position upward, with no error message. It read as a hard limit on how much collateral the pool could absorb. It was the transaction's gas limit.
With the limit raised, the full position cleared $8,618 through a single route. The liquidator who really took that position made about $9,033.
The position was $79,623 of debt against cbXRP collateral on Morpho, in Base block 51,354,208 on September 15, 2026. The liquidation contract borrows the debt, repays it, takes the collateral, sells it through one route and keeps what is left.
Sized at half the position, the call passed. Sized at 55%, 75% or 100%, it failed, and the failing calls returned empty data. No reason, no custom error.
The obvious reading was a cliff in the pool: somewhere between 33,882 and 37,270 cbXRP, the route stopped being able to absorb the sale. That reading was wrong, and several rounds of work were built on it before it was caught.
The calls were running out of gas. The full-size call uses 695,582 gas, and the limit was a fixed 700,000. That looks like enough. It is not.
A contract can pass on at most 63/64 of its remaining gas when it calls another contract. A liquidation like this one nests several calls deep: the flash loan, its callback, the liquidation, the swaps and their callbacks. Each level keeps back a sliver, so the innermost call gets less than the arithmetic suggests.
When an inner call runs out, it does not explain itself. It returns nothing, and from outside that is indistinguishable from a revert with no message.
The same calls at a 3,000,000 gas limit, against the same block:
| Share of position | cbXRP seized | Debt repaid | Gas used | USDC received | Kept after repaying |
|---|---|---|---|---|---|
| 55% | 37,270 | $43,793 | 620,794 | $48,972 | $5,179 |
| 100% | 67,764 | $79,623 | 695,582 | $88,241 | $8,618 |
Both pass. The quote the contract was built from matches what it received to within 0.11%. The 75% size passes too.
debug_traceCall shows the inner frame that failed and why. Here it said out of gas, not a failed swap.The liquidator who took this position on chain cleared about $9,033 by splitting the sale across several pools. One route, sized in full, gets $8,618, or 95% of that. The pool had the depth all along.
With your own contract and calldata, the test is two calls. Set the variables first.
# The same call at two limits, against the same block. # Empty output at the first and a result at the second means gas, not liquidity. cast call $CONTRACT $CALLDATA --from $OWNER --gas-limit 700000 --block $BLOCK --rpc-url $RPC cast call $CONTRACT $CALLDATA --from $OWNER --gas-limit 3000000 --block $BLOCK --rpc-url $RPC # What the call really uses, to size the limit from cast estimate $CONTRACT $CALLDATA --from $OWNER --block $BLOCK --rpc-url $RPC