BullBearSwap Info

BullBearSwap's Smart Routing Algorithm searches thousands of routes and compares with top DEX aggregators.

BullBearSwap Logo

TrustNet Score

The TrustNet Score evaluates crypto projects based on audit results, security, KYC verification, and social media presence. This score offers a quick, transparent view of a project's credibility, helping users make informed decisions in the Web3 space.

63.87
Poor Excellent

Real-Time Threat Detection

Real-time threat detection, powered by Cyvers.io, is currently not activated for this project.

This advanced feature provides continuous monitoring and instant alerts to safeguard your assets from potential security threats. Real-time detection enhances your project's security by proactively identifying and mitigating risks. For more information, click here.

Security Assessments

Static Analysis Dynamic Analysis Symbolic Execution SWC Check Manual Review
Contract address
0x79E9...eC3d
Network
PulseChain
License MIT
Compiler v0.8.30+commit.73712a01
Type DEX-aggregator swap settlement router
Language Solidity
Onboard date 2026/11/02
Revision date 2026/11/02

Summary and Final Words

No crucial issues found

The contract does not contain issues of high or medium criticality. This means that no known vulnerabilities were found in the source code.

Contract owner cannot mint

It is not possible to mint new tokens.

Contract owner cannot blacklist addresses.

It is not possible to lock user funds by blacklisting addresses.

Contract owner cannot set high fees

The fees, if applicable, can be a maximum of 25% or lower. The contract can therefore not be locked. Please take a look in the comment section for more details.

Contract cannot be locked

Owner cannot lock any user funds.

Token cannot be burned

There is no burning within the contract without any allowances

Ownership is renounced

The contract does not include owner functions that allow post-deployment modifications.

Contract is not upgradeable

The contract does not use proxy patterns or other mechanisms to allow future upgrades. Its behavior is locked in its current state.

Scope of Work

This audit encompasses the evaluation of the files listed below, each verified with a SHA-1 Hash. The team referenced above has provided the necessary files for assessment.

The auditing process consists of the following systematic steps:

  1. Specification Review: Analyze the provided specifications, source code, and instructions to fully understand the smart contract's size, scope, and functionality.
  2. Manual Code Examination: Conduct a thorough line-by-line review of the source code to identify potential vulnerabilities and areas for improvement.
  3. Specification Alignment: Ensure that the code accurately implements the provided specifications and intended functionalities.
  4. Test Coverage Assessment: Evaluate the extent and effectiveness of test cases in covering the codebase, identifying any gaps in testing.
  5. Symbolic Execution: Analyze the smart contract to determine how various inputs affect execution paths, identifying potential edge cases and vulnerabilities.
  6. Best Practices Evaluation: Assess the smart contracts against established industry and academic best practices to enhance efficiency, maintainability, and security.
  7. Actionable Recommendations: Provide detailed, specific, and actionable steps to secure and optimize the smart contracts.

A file with a different Hash has been intentionally or otherwise modified after the security review. A different Hash may indicate a changed condition or potential vulnerability that was not within the scope of this review.

Final Words

The following provides a concise summary of the audit report, accompanied by insightful comments from the auditor. This overview captures the key findings and observations, offering valuable context and clarity.


Smart Contract Analysis Statement

Contract Analysis

The BullBearSettlement contract implements a permissionless and immutable swap settlement router that executes multi-hop trades across PulseX, Uniswap-style, Balancer and stable-swap venues on PulseChain in a single transaction, supports native PLS in and out, offers signature-based and gasless relayed execution through Permit2, and charges a surplus-based fee that is capped at 6% of the trade output. The deployed bytecode matches the verified source exactly, the contract is not a token, and it has no mint, burn, blacklist or upgrade functionality.

Ownership Privileges

The ownership of the contract has been eliminated by design: the contract was deployed without an owner and cannot be upgraded, and three immutable roles, each held by a single externally owned account, carry narrowly scoped powers instead. These privileged roles retain the following privileges and limitations:

  • The guardian can pause and unpause every swap function at any time and without delay, and can separately switch off the self-directed Permit2 swap path.
  • The benchmark attestor signs the benchmark used for each protocol-built trade and thereby determines the fee of that trade, from zero up to the hard ceiling of 6% of the output.
  • The benchmark attestor co-signs the route of every gasless relayed order, which is the only control over which pools a relayer may use to fill that order.
  • The fee recipient receives all surplus fees collected by the contract.
  • No role can move, freeze or withdraw user funds. The contract holds no user balances between trades, and users can recover their own recorded leftovers at any time, even while the contract is paused.
  • The fee parameters, the fee ceiling, the trusted venues and every privileged address are fixed at deployment and cannot be changed, raised or upgraded by anyone.
  • Every trade enforces the user's own non-zero minimum output, so neither the attestor nor a relayer can push a user below the amount the user accepted.
  • None of the three addresses is a multisig, none of them can be rotated and there is no timelock. The guardian has never been used and currently holds no funds for gas, so a lost or compromised key could only be addressed through a new deployment.

Security Features

The contract implements several positive security features:

  • Every pool is authenticated against the trusted factories' own registries or an explicit allowlist before any funds move, and every leg must demonstrably deliver the token it declares.
  • Strict per-trade accounting ensures that a trade can only spend what it brought in, that balances already held by the contract are never paid out, that leftovers are refunded to the trader, and that no tracked token ends below its starting balance.
  • Every entry point is protected against reentrancy, swap callbacks are only honoured from the pool or manager the contract is currently calling, and unsolicited native transfers are rejected.
  • Signed attestations are bound to the chain and to this contract, malleable signatures are rejected, Permit2 signatures are single-use, and the 6% fee ceiling held in every one of the 323 settlements executed on-chain so far.
  • The code is immutable and verified, uses no external libraries, and protects users with a mandatory minimum output on every trade.

Key Observations

No critical or high severity issues were identified, and no path was found that would allow theft of user funds or of funds held by the contract. The most relevant observation concerns transparency of the fee: the source header promises that users always receive at least the competing quote and pay no fee when it is not beaten, while the deployed logic charges half of the quote-time advantage and can still charge a fee when execution lands slightly below the competing quote. On-chain, 32 of 301 fee-paying trades fell into this case, with fees of at most 0.12% of the output. We recommend that the team corrects the published fee description, funds the guardian and moves privileged keys to stronger custody in future versions, adds events for guardian actions and for leftover funds owed to users, and binds each fee attestation to a single trade. We also recommend that gasless orders let users sign whether they receive the wrapped or the native token, because the relayer that submits an order can currently choose either form. Native currency forced into the contract outside the normal flow could be taken by any caller, although the contract currently holds none. Tokens that charge a tax on transfers can only be sold through routes built for them.

Note - This Audit report consists of a security analysis of the BullBearSettlement smart contract. This analysis did not include economic analysis of the contract's tokenomics. Moreover, we only audited the main contract for the BullBearSettlement team. Other contracts associated with the project were not audited by our team. We recommend investors do their own research before investing.

Files and details

Findings and Audit result

medium Issues | 2 findings

Pending

#1 medium Issue
Fee is charged when the delivered output is below the benchmark, contrary to the documented guarantees
BullBearSettlement.sol
L10-13
L1794-1871
Description

The contract header states that the protocol takes 50% of the surplus above the benchmark, that the fee is zero whenever the benchmark is not beaten and that the user always receives at least the benchmark output. The implemented fee is instead derived from two quote-time numbers, quotedOutput minus benchmarkOutput, and is still charged when the executed output drifts up to 1% below quotedOutput. A trade can therefore deliver less than the benchmark and still pay a fee. Internal comments acknowledge the change, but the header that users and integrators read on the explorer was never updated. On-chain data confirms the effect: of 301 fee-paying settlements, 32 paid a fee even though the delivered amount was at or below the signed benchmark (up to 51 bps below, with fees of up to 12.27 bps), and in a further 132 the fee exceeded half of the realised surplus. We re-executed one of these settlements in its original block context and observed the same result.

Pending

#2 medium Issue
Single-key control over pausing, fee attestation and relayed routing without timelock or rotation
BullBearSettlement.sol
L417
L447
L485
L509-512
L1036-1067
L1299-1301
L1756-1770
Description

Three privileged addresses are fixed at deployment and all of them are externally owned accounts without code: the guardian 0x4689Ec8F7A3D45aACe677177F23dbf7c361bf1Ae, the benchmark attestor 0xa7001d0C7570b0242C28831F70cF2eA53d7f3CD4 and the fee recipient 0xC24d1419ab76617A29eEB32B944b8f9FE7e34C53. The guardian can freeze every swap entry point at any time and without delay, yet it has never transacted and holds no gas to act in an emergency. The attestor key alone determines the fee of every attested trade, up to the hard ceiling of 6% of output, and its co-signature is the only control over which route a relayer may execute for a gasless order. Because all three addresses are immutable, a lost or compromised key cannot be rotated or revoked without redeploying, and a compromised guardian could keep the contract paused indefinitely. User funds are not held between transactions, so the impact is bounded by each user's own floor, but users must trust single keys for fair fees, correct routing and availability.

low Issues | 5 findings

Pending

#1 low Issue
Native value held by the contract can be withdrawn by any caller through an unvalidated wrapper
BullBearSettlement.sol
L1333-1364
L1998-2030
Description

executeToNative and executeRelayedToNative unwrap the net output by calling withdraw on params.buyToken and then send netOutput in native currency from the contract's own balance. The buy token is never compared with WRAPPED_NATIVE and the amount actually returned by withdraw is not measured. A caller can therefore name a token-shaped contract whose withdraw does nothing, route a trade into it through a pair they create on a trusted factory, and receive native value that never came from the swap. The guarded receive function blocks ordinary transfers, so the exposure is limited to native value that arrives without calling it, for example through SELFDESTRUCT or a block reward. We reproduced this against a copy of the live chain state: 5,000 PLS forced into the contract were withdrawn by an unrelated caller, who received 4,960.77 PLS. executeNative likewise forwards msg.value to an unvalidated sellToken.

Pending

#2 low Issue
Benchmark attestations are replayable within their window and are not bound to the trader or relayer
BullBearSettlement.sol
L442-443
L1086-1097
L1259-1316
L1756-1770
Description

A benchmark attestation covers only the sell token, buy token, sell amount, benchmark, quoted output and a deadline. It carries no nonce and does not name the trader, the recipient, the floor or the relayer, so one signature is valid for any trade of identical size for up to 300 seconds. In the relayed flow the user signs the order and the attestor signs the route, but the fee attestation is chosen freely by the submitting relayer, who may attach any still-valid attestation, including one with a lower benchmark and therefore a higher fee, up to the 6% ceiling. The user's signed floor still holds and the fee goes to the protocol, so the impact is limited to users paying more than the quote they were shown.

Pending

#3 low Issue
Privileged state changes emit no events
BullBearSettlement.sol
L1036-1067
Description

pause, unpause and setPermit2DirectPaused change contract-wide state that blocks user transactions, but none of them emits an event. Users, integrators and monitoring systems can only notice a pause by polling storage or by watching their own transactions fail, and there is no auditable on-chain history of when the guardian acted.

Pending

#4 low Issue
Residue entitlements are invisible to the users they belong to
BullBearSettlement.sol
L613
L1730-1751
L1906-1949
Description

When the refund of leftover tokens to a trader fails, for example because the token blacklists the trader, the amount is recorded in the private mapping _residueOwed and stays in the contract until the trader calls sweep or sweepTo. No event is emitted when the entitlement is created or paid, and there is no public getter, so a trader has no on-chain way to learn that funds are waiting. The funds are safe, because only the recorded owner can claim them, but in practice they are likely to remain unclaimed.

Pending

#5 low Issue
Relayed orders do not bind whether the output is delivered wrapped or native
BullBearSettlement.sol
L561-582
L1224-1316
Description

The RelayedOrder signed as the Permit2 witness does not state whether the output is delivered wrapped or native. executeRelayed and executeRelayedToNative pass a fixed flag to the shared _executeRelayed, and neither the witness nor the attestor's route digest covers it, so one pair of signatures is valid for both entry points. For an order that buys the wrapper, the submitting relayer, or anyone if no relayer is named, therefore chooses the delivery form. Amounts are unaffected: the net output is identical, the refund and fee are paid in the wrapped token, and a failed native delivery reverts without consuming the Permit2 nonce. The risk is substitution of the asset form: a recipient contract without a receive function cannot be filled natively, a deposit address, integration or smart account that only credits the wrapped token may not credit native value, and a user expecting native currency may receive the wrapped token. The relayed entry points are unused on the live deployment.

optimization Issues | 7 findings

Pending

#1 optimization Issue
Trusted-factory lookups make many external calls per leg
BullBearSettlement.sol
L2201-2209
L2266-2278
Description

Every V2 leg calls getPair on up to five factories, and every V3 leg calls getPool plus a poolByPair probe on up to four factories, including probes that are known to revert on three of them. Production routes average 19 legs and reach 49, so these repeated lookups account for a large share of the gas spent per settlement.

Pending

#2 optimization Issue
Redundant balance snapshots and quadratic de-duplication over edges
BullBearSettlement.sol
L1605-1721
Description

_snapshotEdgeTokens and _assertBaselinesHeld read two balances per edge even when the same token appears in many edges, and _refundedEarlier rescans all previous edges for every token, which grows quadratically with route length. On long routes these external calls and calldata scans add tens of thousands of gas.

Pending

#3 optimization Issue
Trusted factory lists are read from storage on every lookup
BullBearSettlement.sol
L457-466
L2202-2205
L2268-2270
Description

trustedV2Factories and trustedV3Factories are dynamic storage arrays that never change after deployment, yet every lookup pays a storage read for the length and for each element. The lists are short and fixed in size.

Pending

#4 optimization Issue
EIP-712 domain separator recomputed for every signature check
BullBearSettlement.sol
L1072-1080
Description

_domainSeparator hashes the name, version, chain id and contract address every time an attestation is verified, although only the chain id can change after deployment.

Pending

#5 optimization Issue
Relay context written to storage on every relayed fill
BullBearSettlement.sol
L600-601
L1310-1315
Description

_executeRelayed writes the order owner and the relayer refund to storage before settlement and clears them afterwards. On a chain without transient storage this costs several thousand gas per relayed transaction.

Pending

#6 optimization Issue
Public constants and dead code consume scarce bytecode space
BullBearSettlement.sol
L343-348
L519
L2878-2880
Description

The deployed runtime is 24,311 bytes, only 265 bytes below the 24,576-byte limit. Unused public constants generate getter functions, and the never-read V3_SWAP_ROUTER immutable and the unused _min helper add code that does nothing. This headroom will be needed for future fixes, such as the events recommended in this report.

Pending

#7 optimization Issue
Output balances are read twice per leg
BullBearSettlement.sol
L2069
L2091
L2235-2246
L2440-2445
L2717-2723
L2753-2794
Description

_executeEdge reads the output token balance before and after each leg for its produce guard, and most adapters read the same balance again to compute their own amountOut. Each extra read is an external call.

informational Issues | 15 findings

Pending

#1 informational Issue
Stable-pool allowlist contains an incompatible Balancer V3 pool
BullBearSettlement.sol
L1023-1027
L2730-2798
Description

The first entry of trustedStablePools in the live deployment, 0x93a21D04d86c36f92D4Ae835A3Db1d2edC877195, is a Balancer V3 StablePool (Bridged Stablecoins, BPEG) registered in the Balancer V3 vault. The stable adapter calls exchange or swap directly on the pool, which a Balancer V3 pool does not implement, so every leg routed to this entry reverts. The constructor does not validate allowlisted pools, and because the list is immutable the entry can never be corrected. The failure is safe, but it shows a configuration gap in an immutable trust anchor.

Pending

#2 informational Issue
Protocol fee can be avoided by omitting the attestation
BullBearSettlement.sol
L1544-1547
L1756-1770
Description

A missing, expired or invalid benchmark attestation results in a fee of zero rather than a revert. This is deliberate, so that a signing outage never blocks a swap, but it also means that any user who builds calldata directly, for example by reusing a route from the public interface and dropping the signature, can trade through the contract without paying the protocol fee.

Pending

#3 informational Issue
Unchecked integer casts in venue adapters
BullBearSettlement.sol
L2241
L2481
L2686-2687
Description

int256(amountIn) in the V3 adapter and -int256(amountIn) in the V4 adapter reinterpret the input amount without a range check. An amount of 2^255 or more becomes negative, which the pool reads as an exact-output swap, the opposite of what the adapter intends. uint160(amountIn) and uint48(deadline) in the Balancer V3 adapter truncate silently. Only a token that reports an implausibly large balance can reach these values, and the truncated Permit2 values fail safely, so there is no practical impact today, but the code relies on assumptions it does not enforce.

Pending

#4 informational Issue
Pool authentication helpers have unhandled edge cases
BullBearSettlement.sol
L2201-2209
L2266-2278
Description

_requireTrustedV2Pool accepts pool equal to the zero address whenever the requested pair does not exist, because getPair returns zero in that case. Execution then transfers the input to the zero address and only reverts afterwards, when token0 is called on an address without code. _requireTrustedV3Pool makes a high-level getPool call on every trusted factory, so a factory that does not implement getPool would revert every V3 leg instead of being skipped. All four configured factories implement getPool, so neither case causes harm in the live deployment, but both depend on behaviour outside the check itself.

Pending

#5 informational Issue
Sandwich protection relies entirely on caller-supplied floors
BullBearSettlement.sol
L1465
L2180-2185
L2238-2244
L2477-2483
Description

V3 and V4 legs swap with full-range price limits, and V2 legs compute their output from current reserves, so each leg accepts whatever price the pool offers at execution time. The only protection is the final minBuyAmount, which must be non-zero, plus any per-leg minimums chosen by the caller. This is a common aggregator design, but on a public mempool a loose floor can be exploited directly by sandwich bots. On-chain, five of the ten failed settlements reverted only because prices moved within their own block.

Pending

#6 informational Issue
SettlementExecuted under-reports the recipient's amount when the fee transfer fails
BullBearSettlement.sol
L1972-1989
Description

If the fee transfer to FEE_RECIPIENT fails, _distribute sends the fee to the recipient and sets surplusFee to zero, but the netOutput value in the emitted event is not increased by the redirected fee. The event then shows a lower amount than the recipient actually received. _distributeNative handles the same situation correctly.

Pending

#7 informational Issue
Inaccurate and outdated documentation in the source
BullBearSettlement.sol
L7-13
L524-528
L1324-1332
L1592-1601
L1880-1882
L1927-1931
L2718-2719
Description

Several comments no longer describe the code. The header says there is no owner and no admin key, yet a guardian can pause the contract. The executeNative NatSpec says no receive function exists, the Balancer V3 adapter says the contract is non-payable, and the sweepTo comment refers to a transient slot although regular storage is used. The Balancer V3 notes say that a missing router makes the leg revert with EdgeSwapFailed, while it actually fails with empty revert data. The post-condition note still calls pool authentication an open fix although it has been implemented, and computeFee returns a surplus figure that does not follow the documented fee logic. Comments like these mislead integrators and future reviewers.

Pending

#8 informational Issue
Dead code, unused declarations and a vestigial immutable
BullBearSettlement.sol
L108-144
L214-231
L318-333
L343-348
L519
L2878-2880
Description

The internal function _min is never called. The interfaces IStableSwap, IMakerPsm, IBalancerV1Pool, IDodoV2Pool, IFluidDexLite and IFwToken are unused. The public constants PROTO_PSM, PROTO_BV1, PROTO_DODO, PROTO_FLUID and PROTO_RINGSWAP refer to adapters that were removed, and V3_SWAP_ROUTER is stored but never read.

Pending

#9 informational Issue
Floating pragma
BullBearSettlement.sol
L2
Description

The source declares pragma solidity ^0.8.24 while the deployment was compiled with 0.8.30. A floating pragma allows the same source to be compiled with different compiler versions, which makes reproducible builds and verification harder.

Pending

#10 informational Issue
Non-descriptive reverts
BullBearSettlement.sol
L1344
L2834
L2847
L2874
Description

executeNative rejects a wrapper that minted nothing with a bare revert, and the safe transfer, transferFrom and approve helpers revert with empty data. Users and integrators get no reason when a transaction fails on these paths, which makes failures hard to diagnose.

Pending

#11 informational Issue
sweep and sweepTo can succeed without paying anything
BullBearSettlement.sol
L1942-1946
Description

sweepTo returns without reverting when the caller has no recorded residue, when the contract balance is zero, or when the payable amount is below minAmount. A caller who uses minAmount as a safety check, or who passes the wrong token, receives a successful transaction that transferred nothing.

Pending

#12 informational Issue
Tokens sent to the contract by mistake cannot be recovered
BullBearSettlement.sol
L1926-1949
Description

By design there is no rescue function. Tokens transferred directly to the contract, and donations that never passed through a settlement, remain locked permanently, because sweep only pays recorded residue and settlements never distribute pre-existing balances. This protects against theft by later callers, but any user mistake is irreversible.

Pending

#13 informational Issue
Trust in permissionless and unverified external venues
BullBearSettlement.sol
L457-466
L2621-2727
Description

Pool authentication confirms that a pool was created by a trusted factory, but factories such as PulseX and the forked Uniswap V2 and V3 deployments let anyone create pools for arbitrary tokens, and Balancer V2 and V3 pools are registered permissionlessly and can run custom pricing or hook logic. Such pools are genuine but can still return poor value. The Balancer V3 Permit2 at 0xa0E293567a37F3de1c1C6Fc98d3C58b7652b309E, which receives an allowance during every Balancer V3 leg, is not source-verified on the explorer, although its domain separator and code size match the canonical Permit2. Users are protected only by their floors and, on relayed orders, by the attestor-signed route.

Pending

#14 informational Issue
Uniswap V4 native-input legs do not pin the wrapper
BullBearSettlement.sol
L2566-2582
Description

For native-output legs the adapter checks that tokenOut equals WRAPPED_NATIVE before wrapping, but for native-input legs _univ4Pay calls withdraw on the caller-supplied tokenIn without a matching check. The Uniswap V4 path is disabled in this deployment because UNIV4_POOL_MANAGER is the zero address, so there is no current impact, but a future deployment that enables it would inherit the asymmetry.

Pending

#15 informational Issue
Taxed (fee-on-transfer) input tokens cannot be settled by the V2 and V3 adapters
BullBearSettlement.sol
L2146-2193
L2218-2250
Description

_swapV2Direct transfers amountIn to the pair and prices amountOut from that full amount, with the default fee factor of 9971 unless the route overrides it in extraData. A token that taxes transfers delivers less to the pair, so the pair's constant-product check fails and the settlement reverts. _swapV3Direct fails in the same way, because the pool checks after its callback that it received the full requested input. The sell-token pull is measured correctly, and executeFot only covers taxed buy tokens, so a taxed token can only be sold when the route supplies a fee factor adjusted for the tax. That factor must match the tax: a higher one reverts and a lower one gives up part of the output to the pool. The behaviour fails closed and puts no funds at risk, but it limits which tokens can be sold.