Oraxel Info

The Daily Strike. 👁️

Oraxel Logo

Team and KYC Verification

The team has securely submitted their personal information to SolidProof.io for verification.

In the event of any fraudulent activities, this information will be promptly reported to the relevant authorities to ensure accountability and compliance.

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.

62.18
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
N/A
Network N/A
License N/A
Compiler N/A
Type N/A
Language Solidity
Onboard date 2026/08/31
Revision date 2026/10/15

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 not renounced

The owner retains significant control, which could potentially be used to modify key contract parameters.

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 Manual Re-Audit Statement - Open

Re-Audit Context

This statement re-assesses the original Oraxel (ORAX) audit against the non-upgradeable unified token and prediction-game contract deployed to BNB Smart Chain mainnet at 0x3b076dfa4fefec34ce356ec14f2f540e7e738b7d. This is substantial new scope rather than a narrow patch review. During the refreshed latest-state observation ending at block 127,764,870, the owner was a single externally owned account holding the full supply, the embedded treasury balance was zero, and no PancakeSwap V2 pair or V3 pool existed for ORAX/WBNB or ORAX/USDT. The daily game was not bootstrapped, but the promotional round was open with three BULL and two BEAR addresses recorded. The liquidity check does not establish absence on every DEX or quote asset.

Status Of The Original Findings

Of the thirty-one baseline findings, twenty-two are fixed or obsolete, three are partially addressed and six are acknowledged standing or design items; none remains Open. The original High-severity volume-forgery mechanism is removed because the new contract has no buy or sell counters. The replacement voting design checks held tokens, but its lock lifecycle and reward model have separate new findings. The period is now one day but remains duplicated in two code paths, so that baseline item is only partially fixed. The incorrect USDT constant is gone, the verified build uses the optimizer, and the compiler pragma is pinned.

New Findings In The Merged Contract

The manual review records eleven new findings: three High, four Medium, one Low and three Informational. The three High findings should block distribution, listing, treasury funding and voting:

  • The anti-sandwich hook marks addresses even for zero-value transfers. An empty account can front-run transfer(target, 0) to block the target's next ORAX movement in that block. This can affect the owner, holders, future pools, claimants and funded Bear settlement; a future pool is also limited to one ORAX movement per block.
  • The owner can replace the price feed immediately, while the owner or a keeper chooses settlement timing and may select any historical oracle round within a day. Together with owner-controlled strikes, this gives privileged actors effective control over game outcomes and treasury reward or burn flows.
  • Daily voting remains open until the public oracle value is read and settled, permitting outcome-aware voting. Losing principal is not forfeited, so one holder can vote opposite sides through two wallets and guarantee a treasury reward without prediction risk.

The Medium findings cover unreserved treasury obligations, broad owner and keeper control over payout and voter-lock liveness, zero-cost Sybil capture of the promotional reward pool, and out-of-order day processing that can overwrite strikes and release an older unresolved lock. The promo round is already open, so the Sybil-voting path is actively reachable even though the treasury is currently empty. Three BULL and two BEAR addresses had voted while the non-voting owner still held the full supply, confirming that all five recorded voter addresses held no ORAX. The complete findings and remediation guidance are in the full report.

Ownership And Centralisation

The owner cannot mint or arbitrarily blacklist a non-participating holder, but it controls the feed, keepers, strikes, payout parameters and settlement. The daily reward ratio is read at claim time, allowing different winners from the same round to receive different or zero rewards. Voted balances unlock only after privileged settlement succeeds, so withheld settlement, broken configuration or key loss can leave participant balances locked. A multisignature and timelock are necessary operational mitigations but cannot repair these code-level defects; a corrected deployment is required.

Open Validation Work

This manual re-audit is not closed. The independent local bytecode reproduction, static analyzers, new fuzz/property/symbolic harnesses, and deterministic or fork proofs for the High and Medium findings were not run in this pass. They must be completed against the final remediation build, followed by verification of owner, keeper, feed, treasury reserves, liquidity and round initialization. This statement is not final assurance until that work is complete.

Files and details

Findings and Audit result

high Issues | 4 findings

Resolved

#1 high Issue
Buy and sell volume can be fabricated at zero token cost
OraxelToken.sol
L86-109
Description

The transfer hook increases a user's buy or sell counter whenever tokens move to or from one of the two liquidity pool addresses, without checking that a trade actually took place. A PancakeSwap pool exposes a skim function that pushes any balance above its recorded reserve to an address of the caller's choosing. Sending tokens to the pool is therefore recorded as a full sell, and calling skim immediately afterwards is recorded as a full buy while returning the very same tokens. The round trip costs nothing but gas and can be repeated without limit. Running twenty-five loops against the live deployment turned a 10,000 token position into 250,000 tokens of recorded buy volume and 250,000 tokens of recorded sell volume, with the balance unchanged at the end. Two independent fuzzers reached the same result in two calls each, driving recorded sell volume above the entire token supply. The project team has since supplied the contract that consumes these figures, the OraxelGame contract at 0x1A64253E4104015A52662080a66d5cA1C5602eac, deployed alongside the token and wired to it on-chain. The game only accepts a vote from an account with at least one token of daily buy volume, records that buy volume as the size of the vote, and pays each winner a percentage of it, currently one percent, out of a treasury allowance. Forged buy volume therefore turns directly into treasury payouts. An attacker inflates its buy volume with the skim loop, votes for the side that wins, which with the current strike setting has been the same side in all 1,064 rounds resolved so far, and at claim time borrows tokens from the same pools for the length of one transaction to pass the game's balance check. The one percent reward is larger than the roughly 0.25 percent cost of that borrow and the process repeats every round, so the treasury allowance behind the game can be paid out to someone who never bought a token.

Pending

#2 high Issue
Anti-sandwich hook enables tokenless per-address censorship and limits a DEX pool to one movement per block
OraxelToken.sol (unified, BSC mainnet 0x3b07...8b7d)
L272-305
Description

The overridden _update marks every non-exempt sender and recipient before calling the OpenZeppelin balance update. The marker is applied even when value is zero. Standard ERC-20 zero-value transfers succeed from an account with no balance, so an empty EOA can call transfer(target, 0), consume the target's block allowance and make the target's next incoming or outgoing movement revert. No ORAX dust is required. This permits repeated front-running or spam against the owner before distribution, any holder, a future pool, a reward claimant, or DEAD_ADDRESS. Marking DEAD_ADDRESS can revert a funded Bear settlement because its burn transfer becomes the second movement involving that address; marking a claimant can revert the reward transfer. A DEX pair is also treated like any user, so the first trade or zero-value poison transfer touching the pair blocks every later trade against that pair in the block. Self-transfers always revert because the sender is marked before the identical recipient is checked. The defect is live before listing and becomes more disruptive once trading and funded game flows begin.

Pending

#3 high Issue
Owner-controlled price source and discretionary settlement let privileged actors choose game outcomes
OraxelToken.sol (unified, BSC mainnet 0x3b07...8b7d)
L232-236
L360-403
L595-719
L827-833
L853-861
Description

Settlement for daily and promo rounds is read from priceAggregator, while setPriceAggregator accepts any non-zero address immediately and without interface, decimals or timelock validation. A crafted contract can satisfy every round-data check and return any positive price, letting the owner force either winner. Even with the genuine feed, processDailyClose can run at any point in the current UTC day, and processMissedDay accepts any Chainlink round whose updatedAt falls anywhere inside the requested 24-hour bucket rather than the round nearest a committed close time. An owner or keeper can therefore cherry-pick an intraday price whenever the range crosses the strike. The owner additionally chooses the initial non-zero strike, which may be negative or arbitrarily high, and can tune later strikes by +/-50%. These controls determine winners and, once funded, direct treasury rewards or burns. At the recorded state block the configured feed was genuine and the treasury empty, but the capability is built into the deployed code.

Pending

#4 high Issue
Daily voting remains open until settlement and opposing wallets can obtain risk-free rewards
OraxelToken.sol (unified, BSC mainnet 0x3b07...8b7d)
L679-702
L727-756
L774-799
Description

dailyVotingClosed is set only inside the same processDailyClose transaction that reads the public latest oracle answer and resolves the round. Until that transaction executes, a holder can observe or front-run it, compare the public answer with the public strike, vote for the already knowable result, and be unlocked immediately by settlement. Independently, the contract never transfers or forfeits a losing principal: two wallets controlled by one holder can vote equal positions on opposite sides, both recover full transferability after resolution, and one is guaranteed rewardRatio percent of its position from the treasury. The mechanism therefore rewards transaction ordering or a costless hedge rather than a prediction and can repeatedly deplete a funded treasury.

medium Issues | 7 findings

Resolved

#1 medium Issue
The mainnet USDT address kept in the source comments is not the USDT contract
OraxelToken.sol
L25-33
Description

The router, USDT and wrapped BNB addresses are stored as constants holding testnet values, with the intended mainnet equivalents sitting next to them in comments to be swapped in by hand before a mainnet launch. Two of the three commented values are correct. The USDT one is not - it is a corrupted version of the real address and there is no contract at all at that location on BSC mainnet. Nothing is wrong with the contract as currently deployed, since the value sits in a comment and has no runtime effect. The risk is entirely at launch: creating a pool does not check that either side is a real token, so a mainnet deployment using that address would succeed quietly and produce a pool that can never hold liquidity or be traded. Because the pool addresses are fixed at construction with no setter, that outcome cannot be corrected afterwards.

Resolved

#2 medium Issue
Adding and removing liquidity is recorded as selling and buying
OraxelToken.sol
L96-107
Description

Adding liquidity moves tokens from the provider into the pool, which matches the condition the contract uses to recognise a sell. Removing liquidity moves them back out, which matches the condition for a buy. Neither is a trade and neither puts any directional pressure on the price, yet both are recorded at their full amount. Tests against the live pools confirm it: adding 1,000 tokens of liquidity recorded 1,000 tokens of sell volume, and withdrawing the position afterwards recorded roughly the same amount as buy volume. A liquidity provider therefore builds up large phantom buy and sell figures simply by managing a position, and adding and withdrawing repeatedly generates volume for little more than gas. Any ranking or reward derived from these counters ends up favouring liquidity providers over actual traders, which is the opposite of what a buy and sell metric is normally meant to capture. The game contract makes this concrete: withdrawing liquidity creates buy volume, so a liquidity provider becomes eligible to vote and is paid a reward on the withdrawn tokens without having bought anything.

Resolved

#3 medium Issue
Multi-hop trades credit the volume to a pool contract instead of the trader
OraxelToken.sol
L96-101
Description

When a swap is routed through more than one pool, the router settles each leg by sending the output straight to the next pool. If this token sits in the middle of such a route, both the sender and the receiver of its leg are pool contracts. The contract checks the sender first, so the whole amount is credited as a buy to the receiving pool, and the account that actually initiated and paid for the trade is credited with nothing at all. A test against the live pools, swapping tUSDT to ORAX to WBNB, recorded zero buy and zero sell volume for the trader and roughly 433 tokens of buy volume for the WBNB pool contract. Both pools needed for this route already exist, so the path is available today. As well as losing genuine volume, it gives a trader a way to trade without being recorded, and a way to push volume onto an address they do not control. For the game contract that consumes these figures, this particular case does little harm, because a pool cannot vote and a trader routing through the token did not set out to hold it. The same root cause does affect genuine buyers, though. The token credits whichever address the pool pays out to, so a purchase whose tokens are delivered through an intermediary such as an aggregator or a wallet's built-in swap contract is credited to that contract, and the buyer is refused when trying to vote.

Pending

#4 medium Issue
Rewards and promo payouts depend on a pre-funded treasury with no liability accounting, so claims can revert and later winners are starved
OraxelToken.sol (unified, BSC mainnet 0x3b07...8b7d)
L774-800
L440-472
Description

Daily claim() pays betAmount * rewardRatio / 100 and promo claimPromoReward() pays a per-winner share, both by _transfer from the contract's own balance, each guarded only by a point-in-time treasuryBalance() >= amount check. Nothing reserves funds against outstanding obligations: the sum of all winners' claimable rewards is never tracked, and the treasury can only be topped up by someone transferring ORAX into the contract (there is no dedicated funding function and the initial supply is minted to the admin, not the contract). If the treasury is underfunded relative to winnings, claims succeed on a first-come basis until the balance runs out and then revert for everyone else, so honest winners can be unable to claim through no fault of their own. The burn-on-BEAR path competes for the same balance. This is a solvency and fairness problem rather than a theft vector.

Pending

#5 medium Issue
Centralisation and liveness expanded: broad owner powers can alter payouts and keep voter balances locked
OraxelToken.sol (unified, BSC mainnet 0x3b07...8b7d)
L43
L232-242
L329-335
L433-435
L679-719
L774-799
L827-861
Description

The audited token had no privileged role, while the unified contract inherits Ownable and exposes setPriceAggregator, setKeeper, strike/reward/burn setters, bootstrapStrike, promo start/resolution and owner-or-keeper daily processing. There is no timelock or two-step ownership transfer, and the recorded owner is a single EOA holding 100% of supply. rewardRatio is read when each daily winner claims rather than snapshotted for the round, so the owner can pay favored claimants at 4%, reduce later claims, or set it to zero and make a victim's claim succeed for zero while becoming permanently claimed. Voted tokens unlock only when owner/keeper settlement succeeds; withholding settlement, losing privileged keys, choosing a broken feed, or renouncing ownership without a usable keeper path can keep voter balances locked indefinitely. The owner cannot mint or arbitrarily freeze an account that has not opted into voting, but the privileged liveness and payout control is materially broader than the original token.

Pending

#6 medium Issue
Zero-cost promo voting is Sybil-capturable and remains open until its public outcome is settled
OraxelToken.sol (unified, BSC mainnet 0x3b07...8b7d)
L311-357
L360-427
L440-468
Description

votePromo requires no token balance, stake, identity, deadline or voting-close transaction, yet the reward pool is divided equally by the number of winning addresses. One actor can create many EOAs or contracts and vote on both sides, dominating the winner count regardless of the result and capturing nearly all of the promo pool for gas cost. Because voting stays open until resolvePromoRound executes and the current feed value is public, an attacker can also front-run a public resolution and place only winning votes. The configured promo ratio can expose up to 10% of the shared treasury in this one-off distribution. In the refreshed observation the promo round was already VOTING_OPEN with three BULL and two BEAR addresses recorded while the non-voting owner still held 100% of supply, proving that all five recorded voter addresses held no ORAX. The treasury was still empty.

Pending

#7 medium Issue
Out-of-order day processing can overwrite strikes and release an earlier unresolved token lock
OraxelToken.sol (unified, BSC mainnet 0x3b07...8b7d)
L260-266
L624-719
L727-753
Description

_processDay writes dailyStrikePrice[day + 1] unconditionally but marks day resolved only when dailyStrikePrice[day] is non-zero. If day N with votes is skipped, processing zero-strike day N+1 can create a strike for N+2 while N remains unresolved. On N+2, an N voter can hold the old locked amount plus a minimal new amount, vote again, and overwrite userLastVotedDay with N+2. getLockedTokens then ignores the unresolved N lock, allowing the older principal to move before N settles while its historical reward entitlement remains. Processing days out of order can also overwrite an existing active or historical strike because no sequence or existing-value guard is enforced. This directly invalidates the single-active-lock invariant.

low Issues | 7 findings

Pending

#1 low Issue
The fifteen minute period is hard-coded, named as a day, and duplicated in the game contract
OraxelToken.sol
L68-73
Description

The helper that produces the key for the daily volume mappings divides the block timestamp by fifteen minutes, even though it is named getCurrentDay and its comment describes it as returning the current day. The project team has explained that this is deliberate. The token was built for the OraxelGame contract, which runs fifteen minute rounds and computes the period with an identical function, and the game's on-chain history shows 1,077 rounds processed at that cadence. The divisor is therefore right for its consumer, and this finding was re-rated from medium to low. What remains is a consistency problem. The same value is hard-coded separately in two contracts that can never be changed, both call it a day, and nothing checks that the two agree. If they ever differ, for example because only one of them is switched to a daily period for mainnet, the game reads a period the token never writes and every vote fails. The naming has already caused a defect in the game itself: its function for recovering a missed round checks the price against a real calendar day, which no fifteen minute period number can ever match, so any round the operator misses can never be resolved.

Resolved

#2 low Issue
Deployment can be blocked by creating either pool first
OraxelToken.sol
L55-62
Description

Creating a liquidity pool fails when that pool already exists, and the constructor does not handle that case. The address a contract will be deployed to can be worked out from the deployer's address and transaction count, both of which are visible while the transaction is still pending, so an observer can create either of the two pools first and make the whole deployment revert. Nothing is lost when this happens, because the deployment simply fails and can be retried from a different transaction count, which produces a different address. Someone willing to keep paying to create pools could keep doing this, though each attempt has to be front-run individually. The current deployment already succeeded, so this matters for a future deployment rather than for the contract that is live now.

Resolved

#3 low Issue
Tracked pool addresses are fixed forever with no way to correct them
OraxelToken.sol
L35-36
Description

The two pool addresses are written once during deployment and there is no function to change them and no administrator who could ever add one. If liquidity later moves to a different venue, whether a newer pool type, a different quote asset or another exchange, the counters quietly stop reflecting what is happening in the market while continuing to return numbers that look perfectly plausible. There is no signal on-chain that the data has become incomplete, which makes this the kind of problem that is noticed long after it starts. For the game contract this also decides who may take part: only purchases from these two pools count towards the buy volume that voting requires, so a buyer on any other venue, including a newer pool type on the same exchange, is refused as though they had never bought.

Resolved

#4 low Issue
Volume accounting changes state without emitting any event
OraxelToken.sol
L97-107
Description

Six storage mappings are updated on qualifying transfers and none of those updates emits an event. The only log produced is the standard transfer event that every token emits. Indexers and analytics services therefore cannot observe the accounting directly and have to replay transfers and reimplement the classification logic themselves, which means any difference between the rule in the contract and the reimplementation goes unnoticed. It also makes the other problems in this area harder to spot in production monitoring, because there is nothing to alert on.

Resolved

#5 low Issue
A movement between two pools is only ever counted as a buy
OraxelToken.sol
L102
Description

The two conditions that classify a movement are chained together, so when the sender and the receiver are both pool addresses the first condition matches and the second is never reached. The movement is recorded as a buy for the receiving pool and never as a sell for the sending pool. The result is that buy and sell totals become asymmetric for these movements, and aggregate buy volume can exceed aggregate sell volume even though exactly the same tokens crossed both boundaries. Any calculation of net flow built on the two counters is skewed as a result. This branch ordering is also the mechanism behind the multi-hop misattribution reported separately.

Acknowledged

#6 low Issue
Reporting period boundaries can be nudged by block producers
OraxelToken.sol
L71-81
Description

All three period keys are derived from the block timestamp, which a block producer can shift slightly within the tolerance the network allows. A transaction close to a boundary can therefore be pushed into the neighbouring period. With daily, weekly and monthly windows this would be negligible. With a fifteen minute window the boundaries occur ninety-six times more often, so the proportion of time that is close to a boundary is correspondingly larger and a well-timed sender or a block producer has more influence over which period a trade lands in. This only becomes material if the periods control a reward, and the game contract confirms that they do: a purchase only makes an account eligible to vote within the same fifteen minute period, and the reward is sized on that period's buy volume.

Pending

#7 low Issue
Per-block markers break reward flows and let zero transfers censor claims and Bear settlement
OraxelToken.sol (unified, BSC mainnet 0x3b07...8b7d)
L285-291
L440-468
L648-655
L774-797
Description

Rewards are paid with _transfer(address(this), user, amount), so the hook checks and marks the recipient even though the treasury sender is exempt. A winner cannot claim after any movement involving that address in the block, cannot claim and forward in one transaction, and can be repeatedly censored by an attacker front-running each attempt with transfer(user, 0). The same root cause lets transfer(DEAD_ADDRESS, 0) revert a funded Bear-resolution burn. This is a downstream consequence of R-01, but its effect is more than a one-block post-claim delay.

optimization Issues | 8 findings

Resolved

#1 optimization Issue
The compiler optimizer is switched off
OraxelToken.sol
Lbuild configuration
Description

The verified build has the optimizer disabled. Rebuilding with it enabled at two hundred runs reduces the deployed code from 7,104 bytes to 3,597 bytes, a reduction of just under half, and saves roughly 1,400 to 1,950 gas on every transfer. Deployment cost falls substantially as well, from the 5.72 million gas the live deployment consumed. This is the single highest value change available in this category.

Resolved

#2 optimization Issue
Pool addresses should be immutable rather than stored in state
OraxelToken.sol
L35-36
Description

Both pool addresses are written once during deployment and then read on every single transfer for the life of the contract. Declaring them immutable turns those two storage reads into values baked directly into the code, saving roughly 4,200 gas on a transfer that touches them for the first time in a block and around 200 thereafter.

Resolved

#3 optimization Issue
The timestamp is read three separate times per transfer
OraxelToken.sol
L95-107
Description

The transfer hook calls three separate helpers to obtain the daily, weekly and monthly period keys, and each one independently reads the block timestamp and performs its own division through an internal function call.

Resolved

#4 optimization Issue
View functions are public but never called internally
OraxelToken.sol
L111-133
Description

The three volume getters are declared public even though no code inside the contract calls them, which means the compiler generates an unnecessary memory copy of their arguments on every external call.

Resolved

#5 optimization Issue
The redundant guard can be removed from the transfer path
OraxelToken.sol
L94
Description

The condition guarding the volume accounting reads two storage slots on every transfer and can only ever be false during deployment, so for the entire operational life of the contract it is pure overhead on the hot path.

Resolved

#6 optimization Issue
Buy and sell figures could share one storage slot, but not yet
OraxelToken.sol
L39-48
Description

The buy and sell figures for each period are held in separate mappings and therefore in separate storage slots. Packing the pair into a single slot using two smaller numbers would halve the number of previously untouched slots written for any account that both buys and sells within the same period. The saving is real, but it depends entirely on the volume figures first being bounded by actual trading activity.

Resolved

#7 optimization Issue
The initial supply is computed at deployment instead of being a constant
OraxelToken.sol
L53
Description

The initial supply is expressed as a large literal multiplied by ten raised to the power returned by a virtual function call, all evaluated at deployment time, rather than being written directly as a compile-time constant.

Resolved

#8 optimization Issue
The pool creation event carries a variable-length text field
OraxelToken.sol
L50
Description

The event announcing pool creation carries a variable-length text label describing which pool it refers to, which has to be encoded at runtime and cannot be filtered on efficiently by indexers.

informational Issues | 16 findings

Resolved

#1 informational Issue
Compiler version is not pinned
OraxelToken.sol
L2
Description

The pragma accepts any compiler from 0.8.20 upward within the 0.8 series rather than naming a single version. The deployed contract was built with 0.8.20, so this has no effect on what is live today, but a rebuild on a different machine or at a later date can silently produce different bytecode and different behaviour without anything in the source changing.

Pending

#2 informational Issue
Documentation is missing and, where present, incorrect
OraxelToken.sol
L68-70
Description

The only substantive comment on a public function describes the daily helper as returning the current day, which is not what it does. Everything else is undocumented: the three volume getters, the two pool address variables, the six mappings and the event all have no description, and there is no contract level summary explaining the purpose of the tracking. A comment that is actively wrong is more damaging than no comment, because a reader will trust it. The game contract already shows the cost: it treats the same period number as a real day in one place, which breaks its recovery of missed rounds.

Acknowledged

#3 informational Issue
Explicit getters duplicate the automatically generated ones
OraxelToken.sol
L111-133
Description

The three convenience getters return exactly what the public mappings already expose, only bundled together as a pair of values. They add roughly seven hundred bytes of deployed code, which is a meaningful share of a contract of this size, and they give integrators two different ways to read the same underlying value with no guidance on which is intended.

Pending

#4 informational Issue
Parameter names do not follow the language style guide
OraxelToken.sol
L112-127
Description

Six parameters across the three volume getters are prefixed with an underscore, a convention the language style guide reserves for internal and private members rather than function arguments.

Acknowledged

#5 informational Issue
The whole supply and effectively all liquidity sit with one ordinary account
OraxelToken.sol
L53
Description

The constructor creates the entire supply and assigns it to the deploying account in one step, with no vesting, no lock and no distribution logic. On-chain that account still holds just under 99.7 percent of the supply. It also holds effectively all of the liquidity provider tokens for both pools, none of which have been burned or placed in a locker. The deploying account is an ordinary externally owned account rather than a multi-signature wallet, so a single compromised key would be enough to move all of it. For a test deployment this is unremarkable, but it is the dominant economic consideration for anyone holding the token. The same account also owns the game contract, which pays rewards and performs burns out of a separate treasury wallet's allowance and whose settings its owner controls.

Acknowledged

#6 informational Issue
Tokens sent to the contract address cannot be recovered
OraxelToken.sol
Lcontract-wide
Description

There is no rescue or sweep function, and no administrator who could ever add one, so any token mistakenly transferred to the contract address is permanently stuck. This applies to the contract's own token and to any other asset.

Acknowledged

#7 informational Issue
Compiled for a newer instruction set than some chains support
OraxelToken.sol
Lbuild configuration
Description

The compiler defaults for this version target a newer instruction set, and the deployed code contains just over two hundred uses of an instruction introduced by that upgrade. The chain the contract is deployed on supports it, so there is no problem today, but the same source built with the same defaults would fail to deploy on a chain that has not adopted the upgrade.

Resolved

#8 informational Issue
The guard on the volume tracking is dead code after deployment
OraxelToken.sol
L94
Description

The check that both pool addresses are set can only ever be false while the constructor is running, because both are assigned there and are never cleared afterwards. For the entire remaining life of the contract it costs two storage reads on every single transfer and always evaluates to true. It also unnecessarily couples the two pools, since if only one had been set the tracking would have been disabled for both.

Resolved

#9 informational Issue
Burning tokens held by a pool creates phantom buy volume for the zero address
OraxelToken.sol
L96-101
Description

A burn is processed through the same internal hook as a transfer, with the recipient set to the zero address. Because the classification only looks at whether the sender is one of the two pool addresses, a burn of tokens held by a pool would credit the zero address with buy volume that corresponds to no purchase. On the deployed system this cannot actually happen. Entering that branch requires the pool contract itself to call the burn function, or a third party to hold an allowance granted by the pool, and a standard PancakeSwap V2 pool does neither - its compiled code contains no reference to either burn function, and it never grants an allowance on a paired token. The issue is recorded because the classification logic really does fail to exclude the zero address, and that would become a live problem if the token were ever paired on a non-standard venue.

Resolved

#10 informational Issue
The address returned when creating a pool is not checked
OraxelToken.sol
L55-62
Description

The two addresses returned when the pools are created are stored without checking that they are not the zero address. With the fixed, trusted factory the contract actually uses, this cannot occur - the factory either reverts or returns a valid address, and it has no path that returns zero. The observation is therefore about defensive hardening rather than a live defect. It matters only if the factory address is ever turned into a constructor parameter, which is what we recommend elsewhere, because at that point an unexpected factory could leave both pool variables unset and quietly disable the tracking with no visible failure.

Acknowledged

#11 informational Issue
Standard approval race condition
OraxelToken.sol
Linherited
Description

The approval function overwrites the existing allowance rather than adjusting it, so a spender watching pending transactions can spend the old allowance and then the new one. This is the standard behaviour of the ERC20 standard itself and is not specific to this project - the library used here deliberately removed the increase and decrease helpers in its current major version. It is recorded for completeness because a conformance check flags it, not because the contract does anything unusual.

Resolved

#12 informational Issue
Volume is measured in tokens, so the two pools cannot be compared
OraxelToken.sol
L39-48
Description

Both pools write into the same counters, measured in this token's own units, with no record of which pool produced a given figure and no value attached. The number therefore answers how many tokens crossed a pool boundary, not how much was traded. Prices differ between the two pools and both change over time, so the figure cannot be converted into a value after the fact. The live pools illustrate the point clearly: one holds about 87,637 tokens against 2,011 tUSDT while the other holds 1,000,000 tokens against 0.001 WBNB, which are wildly different implied prices for the same quantity of tokens being added to the same counter. This finding was re-rated from low to informational after the project team supplied the game contract, which measures and pays its rewards in this token, so token-denominated volume is the unit its only known consumer needs. The limitation still applies to any analytics use of the figures.

Resolved

#13 informational Issue
Month periods drift against the calendar and weeks start on a Thursday
OraxelToken.sol
L75-81
Description

The monthly period is a fixed thirty days, so twelve periods cover only 360 days and the boundaries drift five days each year, six after a leap year. Within a few years a monthly period bears no relation to any calendar month. The weekly period is seven days counted from the start of Unix time, which fell on a Thursday, so weeks run Thursday to Wednesday in UTC rather than Monday to Sunday. Neither has any security consequence, but reporting periods will not line up with what a user or an accountant expects, and the mismatch grows steadily over time. This finding was re-rated from low to informational after the project team supplied the game contract, which reads neither the weekly nor the monthly figures, so no known consumer is affected. Both sets of counters are nevertheless written on every transfer to or from a pool.

Pending

#14 informational Issue
Promo division remainder and zero-winner snapshots leave stale distribution accounting
OraxelToken.sol (unified, BSC mainnet 0x3b07...8b7d)
L401-413
L457-468
L569-585
Description

promoRewardPerWinner uses integer division, so the remainder stays in the shared treasury. If the winning side has zero voters, promoRewardPerWinner is zero while promoTotalRewardPool still records a nominal pool. The tokens are not locked or stranded: they remain part of the shared treasury and may be consumed by later burns or rewards. The issue is stale and potentially misleading distribution accounting, including progress reporting, rather than loss of access to funds.

Pending

#15 informational Issue
Embedded treasury funding has no dedicated entry point or event
OraxelToken.sol (unified, BSC mainnet 0x3b07...8b7d)
L216-230
Description

treasuryWallet() intentionally returns address(this), and operators fund it with a normal ORAX transfer. Such a top-up does emit the standard ERC-20 Transfer event, so funds are observable, but there is no typed funding operation or dedicated event distinguishing treasury funding from an accidental transfer. This is an integration and operational clarity issue, not a direct loss vector.

Pending

#16 informational Issue
Round outcome is owner-tunable through mutable and unconstrained strike inputs
OraxelToken.sol (unified, BSC mainnet 0x3b07...8b7d)
L631-646
L827-833
L853-861
Description

Each next-day strike is the previous close adjusted by strikePriceOffsetBps, and BULL wins only when close strictly exceeds strike. A negative offset biases BULL and a positive offset biases BEAR; the owner may change it at any time within +/-50%. The one-time bootstrapStrike is even less constrained: it accepts any non-zero int256, so a negative initial strike guarantees BULL against a valid positive feed and an arbitrarily high strike guarantees BEAR. The offset was zero and bootstrap unused at the recorded state block, but these owner-controlled inputs compound R-02.