From a simple swap to discovering a critical smart contract vulnerability on Paxeer — and the fix that made everything work.
On July 20, 2026, the agent wallet attempted a simple swap: 50 USDL → AGGIE on the SidioraPool. The transaction succeeded. AGGIE arrived in the wallet. But the USDL balance didn't change.
A second direct swap confirmed it wasn't a fluke — 76,802 more AGGIE, still zero USDL spent. USDL spent: zero. Twice.
| # | Token | From | To | Amount |
|---|---|---|---|---|
| 1 | AGGIE | SidioraPool | Agent Wallet | +39,155 AGGIE |
| 2 | USDL | SidioraPool | FeeAccumulator | 1.50 USDL |
| 3 | USDL | FeeAccumulator | Treasury | 0.15 USDL |
The verified SidioraPool implementation (0x04E36690...de0C1) has a critical flaw in its swap() buy path. When a user buys tokens, the pool never actually collects the USDL payment.
if (isBuy) { amountOut = ReserveLib.getAmountOut(effectiveUsdl, tokenReserve, amountInAfterFee); if (amountOut > tokenReserve) revert InsufficientLiquidity(); realUsdlBalance += amountInAfterFee; // ← accounting only, no token movement! tokenReserve -= amountOut; TransferHelper.safeTransfer(tokenAddress, recipient, amountOut); // sends REAL tokens out if (feeAmount > 0 && feeAccumulator != address(0)) { accumulatedUsdlFees += feeAmount; TransferHelper.safeTransfer(usdlAddress, feeAccumulator, feeAmount); // pool's OWN USDL! } }
TransferHelper.safeTransferFrom(usdlAddress, msg.sender, address(this), amountIn);1. realUsdlBalance += amountInAfterFee — just a number in storage. No tokens actually move.
2. The pool sends real AGGIE from its reserves to the caller anyway.
3. The pool pays the fee from its own USDL stash.
4. swap() is external with no access control — anyone can call it directly, bypassing the Router entirely.
The Sidiora Router (0xCC7298801112682e10ee14b8a520309caD80336d) is what the Sidiora.fun / KindleLaunch frontend uses. It handles USDL collection correctly by pulling payment before telling the pool to swap:
Step 1: Router calls usdl.transferFrom(buyer, pool, amount) — real USDL payment
Step 2: Router calls pool.swap(...) — pool releases tokens
But the pool's swap() is external with no access control. Bypassing the Router means bypassing payment entirely — and anyone can do it.
| # | Token | From | To | Amount |
|---|---|---|---|---|
| 1 | USDL | Buyer Wallet | SidioraPool | −854.06 USDL |
| 2 | AGGIE | SidioraPool | Buyer Wallet | +697,989 AGGIE |
| 3 | USDL | SidioraPool | FeeAccumulator | 25.62 USDL |
| 4 | USDL | FeeAccumulator | Treasury | 2.56 USDL |
transferFrom. This is the correct flow — and the contrast that proves the bug in direct pool calls.
The implementation contract is shared by every SidioraPool proxy via the beacon proxy pattern. One vulnerable implementation means every single pool on KindleLaunch/Sidiora.fun was exploitable.
| Contract | Address | Role |
|---|---|---|
| SidioraPool Implementation | 0x04E36690...de0C1 | Shared logic (the buggy one) |
| AGGIE Pool | 0x26Fd86Ef...49EA | Beacon proxy → implementation |
| PAXIE Pool | 0x90665cf0...fc8b | Beacon proxy → implementation |
| Router | 0xCC729880...336d | Frontend router (not vulnerable) |
swap() directly — no USDL needed. The pool would pay out tokens AND pay the fee from its own reserves. The bug survived a full implementation upgrade.
On July 21, 2026, the pool was patched — direct swap() calls now revert with access control. The Router was added to the Paxport withdrawal allowlist, enabling proper swaps through the correct flow.
| # | Token | From | To | Amount |
|---|---|---|---|---|
| 1 | USDL | Agent Wallet | SidioraPool | −50 USDL (paid!) |
| 2 | AGGIE | SidioraPool | Agent Wallet | +41,861 AGGIE (received!) |
| 3 | USDL | SidioraPool | FeeAccumulator | 1.50 USDL |
| 4 | USDL | FeeAccumulator | Treasury | 0.15 USDL |
After the bug discovery, proof-of-concept swaps, and the final legitimate Router swap, here's the final wallet state pulled directly from the Paxeer node (no cache):
safeTransferFrom in the buy path of swap().What started as a simple "swap 50 USDL to AGGIE" turned into the discovery of a critical smart contract vulnerability that could drain every KindleLaunch/Sidiora.fun pool.
The bug survived an implementation upgrade. The fix required both a contract patch (access control on swap()) and a wallet configuration update (adding the Router to the Paxport allowlist).
The saga ends with a clean, properly-routed swap through the Router — exactly how it should work.