BullBearSwap

What this page is about

One contract settles every BullBearSwap trade. This is what it can and cannot do.

When you swap on BullBearSwap, the site finds the route and a single contract executes it: every hop in one transaction, the minimum output you signed checked at the end, the result sent to you. That contract is the only code your tokens ever touch, so it is the thing worth reading.

It is verified on Etherscan and on the PulseChain explorer, so anyone can read the exact code that runs. It has no owner, no admin functions and no upgrade path: every setting is fixed at deployment and cannot be changed. It only pulls the exact amount of your swap from the wallet that calls it, and the site approves that exact amount rather than unlimited, so nothing stays approved afterwards. It cannot touch anyone else's tokens.

There is no third-party audit yet; that is the next step. Until then the source is the audit: read it before you swap. What follows is everything we checked ourselves, how, and what the tools said, with nothing filtered out.

Ethereum 0x128716EaAb226A68299d3c0198a38c1e90C52356
PulseChain 0xc74de6B549D5A667ABc3867c8904D1d00420825C

Settlement contract · security review · 8 Sep 2026

Can this contract hurt you?

Five questions to ask before signing, each answered from the verified source. Nothing here is a promise. Every tile names the thing in the code you can check.

Who controls it?
Nobody. No admin keys.
Eleven values are set once at deployment and can never change: the fee, the attestor, the fee recipient, and the venue and router addresses. No owner, no proxy, no upgrade path.
grep the source: onlyOwner 0 · delegatecall 0 · selfdestruct opcode 0 (1 mention, in a comment) · public immutable 11
What can it move?
Your swap. Nothing else.
Every entry point pulls the swap's own sell amount from the caller. The relayed path uses a Permit2 witness signature bound to sell token, buy token, amount, minimum output, deadline and recipient.
the app approves the exact amount, never unlimited; the swap consumes it
Could anyone drain it?
Nothing to drain.
The contract holds no balances between swaps. Two per-transaction values and four guard slots are all its mutable state, and each is reset before the transaction ends.
one non-swap function, sweep(), returns stuck tokens to the caller and is blocked mid-swap
Exploitable findings?
0 of 740.
Slither's 740 lines across both deployed builds, plus Aderyn and a line-by-line read: none lets anyone move funds they did not swap. Every line is explained below and in the raw outputs.
427 lines Ethereum build · 313 lines PulseChain build · 1 open fork test
Third-party audit?
Not yet.
This is the automated half of an audit, triaged by the team against the code. A third-party review is the next step.
verified source · ownerless and immutable · exact approvals · analysis published

How it was analysed

Two independent static analyzers, run on the deployed source, then every line read by the team

The tools

  • Slither, by Trail of Bits: a static analysis framework for Solidity that runs a suite of vulnerability detectors over the source without executing it. It is the standard first pass in professional smart-contract audits.
  • Aderyn, by Cyfrin: a second, independently written static analyzer with its own detector set, so the two do not share blind spots.
  • Static analysis reads code and flags known risky patterns. It cannot judge intent: a pattern that is dangerous in one contract is deliberate in another. That is why every flagged line below is answered against the code, not counted.

What was run, exactly

  • Slither 0.11.6 with solc 0.8.30, optimizer on at 200 runs, EVM Shanghai, all 102 detectors, none suppressed. Aderyn 0.6.8 with defaults.
  • Once per deployed build: the Ethereum source at HEAD and the PulseChain source at commit 9d4c0a8, each recompiled and matched to the bytes on chain before analysis, so the code analysed is the code that runs.
  • Every result triaged by the team with the reason it can or cannot cause harm, published in full below and in the raw outputs. Not run: symbolic execution (Mythril). Not done: an independent third-party review, which is the next step.

Check it yourself

Two chains, two builds, both matched to the source

The two chains run different builds of the same contract. The Ethereum build adds relayed execution and is compiled through the IR pipeline. What matters is that the source we analysed is the code your transaction hits, so each build is recompiled and compared with the bytes on chain.

Ethereum

viaIR on
Address
0x128716EaAb226A68299d3c0198a38c1e90C52356
Source
BullBearSettlement.sol at HEAD, 2,108 lines, one contract, no imports
SHA-1
b879f4b7b0506a8f034e5ad53499d02b6df31d6b
Compiler
solc 0.8.30, optimizer 200 runs, EVM shanghai
Runtime
23,465 bytes
MATCH artifact equals the on-chain code except its 40 immutable reference positions

PulseChain

viaIR off
Address
0xc74de6B549D5A667ABc3867c8904D1d00420825C
Source
the same file at git 9d4c0a8, 2 Sep 2026, no relayed entry points
SHA-1
94f7d3f26f37f5368ea846d6f4e5afaccabf4061
Compiler
solc 0.8.30, optimizer 200 runs, EVM shanghai
Runtime
24,162 bytes
MATCH recompiled source reproduces the on-chain code byte for byte, metadata included

Tools: Slither 0.11.6 and Aderyn 0.6.8, one run per build. Explorer pages list four solc 0.8.30 compiler bugs; none applies here: the contract uses no transient storage (and targets Shanghai, where it does not exist), has no mutually recursive functions, inherits from nothing, and has no storage arrays. The next deployment compiles with 0.8.36 or later, which retires the notices.

Every finding, sorted by what it could do to you

Not by tool severity. By consequence.

Scanners rank patterns; a swapper cares about outcomes. So the 740 scanner lines plus a line-by-line manual review are grouped here by the worst thing each one could actually cause, with the reason it cannot cause more.

Move funds you did not swaptheft, drain, third-party approvals
  • ConfirmedThe 18 "reentrancy-balance" Highs are one idiom, 18 findings across the 12 adapters: read the output balance before and after the venue call and trust the difference, not the venue. Every entry point is re-entrancy guarded and every callback is bound to the pool or manager recorded before the call, so a callback cannot inflate the difference.
  • ConfirmedThe "arbitrary-send-eth" Highs send native to two immutable venues, to the wrapper's own deposit, or to the swap's recipient for the swap's own output, in the same transaction. The contract holds no native between transactions and rejects any other sender.
  • ConfirmedThe attestation signature can only ever add a fee, capped at 6% of gross and only when BBS beat the benchmark. It binds no caller and carries no nonce; that is harmless today and noted for the day the digest is reused for anything that grants value.
0
Revert your swapdenial of service, funds untouched
  • ConfirmedThe fee function can overflow on an absurd attestor-signed quote, despite a comment saying it never reverts. It needs a compromised attestor and a token with an absurd supply; the result is a revert, never a wrong fee. Hardening item.
  • ConfirmedFee-on-transfer sell tokens revert in the V2 direct path because the input is pre-tax and the reserves are pre-transfer. Tax tokens are out of scope by decision.
2
Show you a less accurate quoteyou still get at least the signed minimum
  • ConfirmedThe Uniswap V4 hook mask refuses the two hook permissions that can change a swap amount, but a dynamic-fee pool can change its LP fee between quote and fill. The per-leg minimum and the 1% drift guard still hold; the quote can be slightly off.
1
Strand your own residualrecoverable through sweep, or open
  • ConfirmedThe V2 adapter derives direction from the pool's first token and never checks the edge's output token. A mislabeled edge misroutes the caller's own residual into the permissionless sweep; nobody else's funds are involved. Hardening item.
  • OpenNative input on the Ekubo and V4 modes: a venue refunding unused native reverts the whole swap (safe), while a venue that neither consumes nor refunds it would strand value that sweep cannot recover. Needs a fork test against the live router before the comment is trusted.
1 + 1 open
Nothingcomments, style, unused code
  • Six comments no longer match the code: "always at least the benchmark output", "no receive() exists", "transient slot", "hooks pinned to zero", "hookless pools only", "adapters 0 to 8" (there are 12). Comments cannot change on chain; they are listed openly here and fixed at the next deployment.
  • 337 and 225 "calls-loop" lines are one pool call per edge, where a failing pool reverts the caller's own swap by design. Constructor zero checks, deadline timestamps, an unused helper, a misnamed constant, an unused immutable, unlimited-cast warnings on values bounded by the user's own amounts.
the rest
427
Slither lines, Ethereum
21 High, 23 Medium, 348 Low, 35 Info. One warning per venue call site explains 337 of them.
313
Slither lines, PulseChain
22 High, 23 Medium, 234 Low, 34 Info. Fewer call sites; one extra High is the same native send, same verdict.
1 + 9
Aderyn, both builds
One High on a cast that real amounts cannot reach; nine cosmetic Lows. Identical lists on both chains.
0
Detectors suppressed
No configuration hides anything. The complete outputs are linked at the bottom of this page.

Reported privately, credited

Disclosed findings, and the researchers who found them.

Separate from the contract scanner lines above: issues researchers reported to us privately — fixed ones, and one under active remediation — listed here so they are known. A duplicate report of a listed issue is credited, not re-rewarded; the finding belongs to whoever reported it first. The full technical write-up of a remediation-in-progress finding publishes here once its fix ships.

Swap-report record integrity — a reported swap could be recorded without binding to its settlementa wallet-history / attribution issue; no user funds involved and no swap outcome changed
  • FixedA researcher reported that a completed-swap report could be recorded in wallet-history without the record being bound to the actual on-chain settlement it named — so a real settlement transaction could be attached to a record naming a different recipient or amount, producing a misattributed history row. No user funds are ever involved and no swap outcome changes; it is a record-integrity issue. Reports are now verified against the settlement event itself: the recipient and token amounts must match the record, or it is not accepted. Credit: Reza; independently reported by Killer Man. Low, fixed.
1
Settlement adapter — a stable-swap leg could deliver a different token than declaredthe difference was strandable; the user still received at least the minimum they signed for
  • FixedA researcher showed that a stable-swap leg could be constructed to deliver a different token than the one it declared, leaving the difference stranded in the settlement contract. The user still received at least the minimum output they signed, so this was never an underpayment. A one-wei per-leg output floor now backstops every adapter, so a leg that delivers none of its declared token reverts rather than proceeding. Credit: Luis_MZ. Medium, rewarded.
1
Relayed-order execution — route now attested on-chainreported privately; not permissionless (needs BBS's own relayer); recipient, signed minimum and now the exact route enforced on-chain
  • FixedA researcher reported a hardening gap in the relayed-order execution path: the route a relayer submitted was not bound by the user's signature, so BBS's own relayer key could have substituted a route that spent the whole slippage budget down to the signed minimum. Triggering it was never permissionless, and no user could receive less than they signed for. Fixed: the settlement now requires BBS's attestor to have co-signed the exact route for that order and nonce (RouteNotAttested otherwise), the relayer address is part of the signed order, and a fork replay of the original report reverts. Live in the current Ethereum deployment (contract 0x917a31bB: runtime bytecode identical in size to the build from this source, 24,311 bytes, carrying the check). First reported privately by ibnu76 (2026-09-09); High, rewarded. A duplicate of this known issue was independently reported by Galih (2026-09-09); credited, not re-rewarded.
1
Velora rival quote shown before its feea less accurate comparison; your own BBS route and its signed minimum were never affected
  • FixedBBS fetched Velora (ParaSwap) quotes without zeroing Velora's default 0.01% partner fee, so the Velora figure BBS displayed, compared, and — where a swap could be sent to Velora — set the minimum from was the pre-fee amount, up to that fee above what Velora would actually deliver. BBS now requests Velora with the partner fee zeroed, so the shown, compared and executed-against number is the deliverable; the reporter re-verified zero-wei drift. Credit: ibnu76. Medium, rewarded.
1
Operator-only internal details reachable without authenticationinternal operations/telemetry surfaces; no user funds, private keys, or personal data were exposed
  • FixedA researcher found the internal operator status endpoint (/status) was reachable on the public host without authentication, exposing operational and telemetry data intended only for the operator. It is now gated behind operator authentication — an unauthenticated request receives a 404. The same researcher also reported an error response on the public API that could reveal internal service addresses; that detail is now suppressed for unauthenticated callers as well. No user funds, private keys, or personal data were ever exposed, and no user-facing behaviour changed. Credit: Gwei (@highestgwei). Low, credited.
1
Fee can rise on favorable fills in the rare high-surplus regime — under remediationthe quoted fee is still the most you ever pay; the signed minimum output is enforced before any fee; never observed in production
  • Fix in progressA researcher showed that when the attested quote-time surplus is unusually large — above any level seen in production — the fee is bounded by a ceiling that scales with the amount actually delivered, so a better-than-quoted fill can raise the fee up to, but never above, the quote-time fee. The quoted fee stays the maximum charged, no user receives less than the minimum they signed, and no third party is involved. Making the doctrine — favorable slippage belongs wholly to the user — exact in every regime is a one-line change (cap the ceiling at the quoted output); it ships with the next settlement deployment, after which the full write-up publishes here. Verified on the deployed contract via the read-only computeFee. Credit: Aliyaan Blaike. Low, credited.
1 open
Relay submit does not re-check the signer at the boundaryno fund loss — Permit2 and the pre-broadcast worker both bind the signature to the owner on-chain
  • FixedA researcher noted that the relayed-order submit endpoint queued an order before recovering the signer, relying on Permit2 and the pre-broadcast worker check to bind the signature to the order's owner — both of which held, so no user funds were ever at risk and no third party could move them. Fixed: the submit endpoint now recovers the EIP-712 signer (contract wallets via ERC-1271) and refuses a mismatch before anything is queued. Credit: Raju Arlingzu. Low, credited.
1
Stranded-residue handling — aligning with the “router holds no funds” modelaffects only the rare blocked-refund case; no funds at risk in normal operation
  • Fix in progressA researcher observed that when a refund of a user's stranded residue is blocked, the residue rests in the settlement contract, where — as with every router that reads its own balance — a leftover balance is extractable. The fix, matching the established industry model, is for the settlement to hold no funds at all — escrowing any blocked-refund residue out of the contract so nothing rests in a spendable balance while the owner still reclaims it. This ships in a dedicated settlement deployment. Credit: Aswer S.R. Low, credited.
1 open
Surplus Orders error response carried operational fieldsa service-unavailable body exposed a backend count, an internal path and a timing figure; no funds, keys, or personal data
  • Fix in progressAfter the earlier disclosure that suppressed internal service addresses, a researcher found that the newer Surplus Orders endpoint's service-unavailable (503) body still carried operational detail — the number of solve backends, the internal path, and a server-side timing figure. The internal addresses were already removed on this surface; the remaining count/path/timing are being reduced to a fixed public error body, with operational detail kept to server logs only, matching how major routers format public errors. Credit: ibnu76. Low, credited.
1 open
Page could be embedded in a third-party frame (clickjacking)fixed; no user funds — every swap still requires the wallet's own signature prompt, which a framing page cannot see or control
  • FixedA researcher reported that the app did not send frame-protection headers, so a malicious site could load it inside a hidden frame and overlay it (clickjacking). Every response now sends X-Frame-Options: DENY and Content-Security-Policy: frame-ancestors 'none', so the app refuses to render inside any third-party frame. No user funds were ever at risk — every swap requires the wallet's own confirmation prompt, which a framing page cannot see or control. Credit: Fawwaz (@AkasakaID); the missing-headers gap was independently flagged by Galih. Low, credited.
1

What changes, and what does not

Hardening at the next deployment. No redeploy for its own sake.

Ships with the next contract change

  1. Bound checks before the two casts in the Balancer V3 adapter.
  2. One pinned compiler, 0.8.36 or later, Shanghai target kept for PulseChain.
  3. Fee function returns zero above a sane quoted-output bound; the unchecked block goes.
  4. V2 adapter verifies the edge's output token against the pool.
  5. Comments and header rewritten to match the code; the misnamed constant renamed; the unused immutable dropped; the unused helper removed; constructor zero checks.
  6. Decision on binding the caller and adding a nonce to the attestation digest.

Stated plainly

  • The fee is voluntary at the contract level. A caller who strips the attestation from the calldata executes the same route fee-free. An outage must never block a swap, so this is the model as built.
  • The contract cannot receive anything by accident. Native is accepted only from the wrapper it is unwrapping at that moment; every other sender is refused.
  • Open verification: the native partial-fill behaviour on the Ekubo and V4 modes, on a fork, before any of the above ships.
  • Not established: what only an independent reviewer can add. That is the next step.

Verified source. Ownerless and immutable. Exact approvals. Automated analysis published. No third-party audit yet.

Found something?

Report it privately. We answer within 48 hours and pay for real findings.

How to report

  • DM @bullbearswap on X or Telegram, or email security@bullbearswap.com for long write-ups, attachments or a fork replay.
  • Include the component (contract address and chain, or the app URL), steps to reproduce, the impact you believe it has, and a reward address.
  • Keep details out of public replies and group chats. Test on a fork; never with other users' funds.
  • Acknowledgement within 48 hours, severity within 7 days, a disclosure date agreed with you, public credit unless you decline.

Rewards

  • Critical (loss or theft of user funds through the contract or the relayed-order flow): up to 2,500 USDC.
  • High (users receive less than the enforced minimum, a fee where the contract forbids it, the settlement paying a third party): up to 750 USDC.
  • Medium (quotes or attestations a user can be made to sign without loss beyond the signed floor; app issues exposing user data): up to 250 USDC.
  • Low / informational: public credit.
  • Caps, paid from protocol fee revenue, rising with it. Findings already listed above are known.
  • The table is a baseline, not a ceiling. An exceptional report, or a researcher who keeps finding things, is paid above it. Everyone credited here gets a look at what ships next before the public does; we want the people who understand these contracts on our side for the long term.

Scope, rules and rewards in full: the security policy. Out of scope: the venues the contract routes through, wallets, RPC providers, and anything needing a compromised device or key.