ZEON Info
A fixed 1,000,000,000 supply, priced in USDT, with real holder rewards built in from day one. No minting, no hidden levers — every rule is locked on-chain and verifiable before you buy in.
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.
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
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.
Token transfer can be locked
Owner can lock user funds with owner functions.
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:
- Specification Review: Analyze the provided specifications, source code, and instructions to fully understand the smart contract's size, scope, and functionality.
- Manual Code Examination: Conduct a thorough line-by-line review of the source code to identify potential vulnerabilities and areas for improvement.
- Specification Alignment: Ensure that the code accurately implements the provided specifications and intended functionalities.
- Test Coverage Assessment: Evaluate the extent and effectiveness of test cases in covering the codebase, identifying any gaps in testing.
- Symbolic Execution: Analyze the smart contract to determine how various inputs affect execution paths, identifying potential edge cases and vulnerabilities.
- Best Practices Evaluation: Assess the smart contracts against established industry and academic best practices to enhance efficiency, maintainability, and security.
- 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 - Re-Audit, Third Round
Contract Analysis
The HyperfolioVault contract implements an upgradeable asset custody vault that accepts deposits of a single configured asset - either an ERC20 token or the chain's native currency - and forwards them to an interchangeable external strategy contract that is expected to generate yield. It is not a token: it has no supply, no transfers, no allowances and no mint or burn functions. Deposits and withdrawals are not open to the public. They can only be initiated by one designated gateway contract, referred to in the code as the main chef, which is responsible for identifying users and enforcing whatever rules the wider protocol applies. The vault keeps a simple internal ledger of how much each user has deposited and pays withdrawals against that ledger.
This is the third review of the contract. The first revision was a substantial one that grew the vault by roughly two hundred and fifty lines and closed three of the five high severity issues at the root; the second, reviewed here, is small and surgical, one file and some thirty lines, and it closes the two defects the previous report asked for first. The accounting defect that made the vault insolvent with any asset that taxes transfers had already been dealt with, because deposits verify that the vault received exactly what was requested and reject the asset otherwise. Strategy migration already hands funds over through the strategy's own deposit entry point and asserts that they were registered, so a configuration change cannot strand the vault's balance, and an emergency escape hatch already detaches a broken strategy without calling into it at all. Ownership transfer is two-step, renouncing is disabled outright, a guardian can pause deposits, and the configuration setters carry the validation they were once missing.
What this revision adds is the fix for the last remaining correctness defect. Withdrawals are now served from the vault's own liquidity first, with the strategy asked only for the shortfall, and the strategy pays the vault rather than the depositor. The vault has become the single point from which users are paid, so a depositor always receives exactly the amount recorded against their position regardless of how the liquidity happened to be split between the vault and the strategy. That closes the case where a strategy accepting only part of a deposit left the remainder idle in the vault and then blocked both the full withdrawal and any partial one inside the idle portion. The same change also fixed a second defect for free: the check that verifies delivery was an exact equality, which rejected over-delivery as well as under-delivery and could freeze a position permanently over a single wei of favourable rounding. It is now a lower bound measured on the vault's own balance and written so that it cannot underflow, under-delivery still reverts, and a smart-contract wallet that forwards value inside its receive hook, which could never be paid before, is paid correctly. Separately, the reward channel gained a check that stops a strategy handing depositor principal to the owner's chosen recipient. After this revision no high severity and no medium severity issue remains open: every one is either closed at the root or recorded as a documented design decision we accept.
Three things temper that assessment, none of them severe, and all three concern protocol revenue or unsupported assets rather than depositor funds. The bound added to the reward channel is the wrong one. It requires the strategy's reported balance not to fall across the claim, rather than requiring it to still cover what the vault owes its users afterwards. Because the strategy interface defines that reported balance as everything the strategy holds, accrued yield sits inside it, and a well-behaved strategy forwarding nothing but its surplus lowers the figure and trips the check. The channel therefore cannot currently perform the task it was added for, which means the decision that yield belongs to the protocol has no working route to collect it. Principal is never at risk either way. Second, relaxing the delivery check to a lower bound was right, and the contract states that the resulting over-delivery stays in the vault as protocol funds, but nothing can move it out again: it is not in the ledger, the reward channel operates on the strategy, and the asset rescue refuses the configured asset by design, which is the control that stops the owner reaching user deposits and should not be relaxed. Third, the delivery check sits inside the branch that enters the strategy, so a withdrawal the vault can cover on its own is paid without any check at all. For a well-behaved asset that is correct and cheaper; for an asset that shortens transfers the ledger is debited in full while the user is paid less. That last case is bounded by the deposit path rejecting such assets, although we found that the rejection is made per amount rather than per asset, so deposits small enough that the fee truncates to zero are accepted and accumulate into a position that is then awkward to exit. That behaviour is not new in this revision and reproduces identically against the previous one.
We reviewed the revised contract function by function and then as a whole, and confirmed each conclusion against the compiled contract rather than against the source alone. Both static analysers were re-run, along with pattern-based scanning, symbolic execution and three independent fuzzing engines, and the storage layout and initialisation pattern were re-checked for upgrade safety; no state variable was added, removed or reordered this round, and the layout is identical to the one previously reviewed. One hundred and three targeted test cases now cover the contract, twenty of them written for this round as a dedicated suite that asserts the new behaviour positively rather than merely confirming that the old defect has stopped reproducing, alongside twelve invariant properties and sixteen symbolic ones. Of the eighty-one distinct test cases preserved from the two earlier rounds, seventy-five were unaffected and six changed status, every one of them in the intended direction, which is the evidence that the revision caused no regression. Across this review we record no high severity issue open, none medium, ten low, twelve informational observations and seven gas optimisations, against seventeen issues resolved and nine accepted as design decisions. We again found no critical issues, in the sense that no unprivileged party can drain the contract.
Ownership Privileges
The ownership of the contract has been not yet established, because the contract was not deployed at the time of review. Initialisation assigns ownership to whichever address performs it, so the owner will be determined by the deployment procedure. The owner retains full privileges including:
- Replacing the yield strategy at any time, with immediate effect. This remains by far the most consequential power in the contract: attaching a new strategy grants that strategy an unlimited allowance over the vault's asset and then pushes the vault's entire balance to it. The revision added validation that the target is a deployed contract and that it declares the same asset as the vault, which stops a mistyped address, but it is not a barrier to a deliberately hostile one, which only has to declare the right asset to pass. There is no timelock of any length, and the deposit pause does not cover this path, so it stays fully available during an incident. The absence of a delay here is a deliberate decision by the team, taken so that the protocol can respond immediately to a security incident, and it is discussed in the risk section below.
- Claiming strategy rewards to an address of the owner's choosing. This exists to give protocol yield an auditable destination rather than leaving it dangling, which we consider the right call. It is no longer a route to depositor principal: the vault now records the strategy's reported balance before the claim and rejects the call if that balance has fallen, so a strategy that treats its whole balance as claimable is refused. The bound is stricter than it needs to be and currently also refuses claims that move nothing but yield, which is discussed below. A strategy that misreports its own balance defeats the check, which is part of the standing trust in strategy contracts rather than a separate exposure.
- Rescuing tokens other than the configured asset, and rescuing native currency from a token-mode vault, to an address of the owner's choosing. This is new and is scoped correctly: the configured asset can never be swept and the native sweep reverts in native mode, so there is no route from these functions to user deposits. We verified that a user's position survives a rescue untouched.
- Changing the address of the gateway contract that is permitted to deposit and withdraw, which determines who can move funds in and out of the vault on behalf of users. A zero address is now rejected.
- Changing the configured asset and the sentinel address that decides whether the vault operates in token mode or native mode. Both now additionally require the strategy to be detached first, which was the part of this power that carried real risk, and the asset must be a deployed contract. Both changes are still permitted whenever the internal ledger reads zero, which is not the same as the vault being genuinely empty; the team has explained that keying on the physical balance would let anyone block reconfiguration forever by donating one wei, and we accept that reasoning now that the ignored residue is recoverable through the scoped rescue path.
- Appointing and removing a guardian, and lifting a deposit pause. The guardian can pause deposits but cannot lift the pause and cannot affect withdrawals in any way.
- Transferring ownership, which is now a two-step process requiring the incoming owner to accept. Renouncing ownership has been disabled outright and reverts unconditionally, so the owner-only recovery paths can no longer be orphaned.
- The owner cannot alter any individual user's recorded balance, and cannot credit or delete a ledger entry directly. All ledger changes flow through the deposit and withdrawal paths.
- The owner cannot block a specific user or address. There is no blacklist, allowlist or freeze capability anywhere in the contract, and no per-account restriction of any kind.
- The owner cannot charge a fee. The contract has no fee parameter at all, so there is no deposit fee, withdrawal fee or performance fee, and consequently no fee ceiling that could be raised.
- Neither the owner nor the guardian can halt withdrawals. The pause mechanism added in the revision covers deposits only, and we confirmed both by test and symbolically that no combination of pause state and caller can stop a user exiting.
Security Features
The contract implements several positive security features:
- Both the deposit and the withdrawal path are protected against reentrancy, using the transient-storage variant of the guard, and the revision extended that protection to the strategy setter, the emergency detach and the reward claim. We verified that a strategy re-entering the configuration path from inside its own unwind is rejected, and that mixing this non-upgradeable guard into an upgradeable contract is safe here, because it writes only to transient storage and reserves no persistent slot that could collide with the proxy layout.
- Deposits are now verified rather than inferred. The vault measures its own balance before and after the pull and requires the delta to equal the requested amount, so nothing can be credited that the vault did not receive in full. The strategy's self-reported figure is no longer inside the accounting window, which closes the path that previously let a depositor be credited with value they did not supply.
- Strategy migration is now all-or-nothing. Funds are handed over through the strategy's own deposit entry point and the strategy's reported balance must have risen accordingly, or the whole configuration change reverts and nothing moves.
- The vault is the single point from which depositors are paid. A withdrawal is served from the vault's own balance first and the strategy is entered only for the shortfall, paying the vault rather than the user, after which the vault forwards exactly the recorded amount. This removes a whole class of failure in which funds the vault demonstrably owned could not be paid out, and it means the depositor is insulated from a strategy that returns a little more or routes funds differently than expected. We verified that the strategy is asked only for the difference and is not entered at all when the vault can cover the amount, using a strategy whose unwind reverts unconditionally. The protection against a strategy that pays too little was checked for regression at the same time and still holds.
- An emergency detach exists that makes no external call to the strategy whatsoever. We confirmed against a strategy that reverts on every entry point including its view functions that the ordinary path fails and the emergency path succeeds, leaving the pointer cleared, the allowance revoked and withdrawals falling through to the vault's own balance.
- Ownership is two-step and cannot be renounced, confirmed symbolically over all callers, and the pending-owner slot lives in its own namespaced location so it adds nothing to the sequential storage layout.
- The upgradeable initialisation pattern is implemented correctly. The implementation disables its own initialisers in the constructor, so the implementation contract itself cannot be claimed by an outside party, and the initialiser cannot be called twice. We re-verified the storage layout slot by slot after the revision: every pre-existing variable keeps its slot, the two new variables share the slot the reserved block previously started at, the reserved block was correctly reduced to compensate, and the overall footprint ends where it did before.
- Access to user funds is narrowed to a single gateway address rather than being open to the public, which removes a large class of direct attacks and keeps user-facing policy in one auditable place.
- The attack surface that causes most vault losses is simply absent. There is no price oracle to manipulate, no swap or liquidity path to sandwich, no signature verification to replay, no unbounded loop to exhaust with gas, and no cross-chain messaging that could be spoofed. Arithmetic uses the compiler's checked operations throughout, with no unchecked blocks anywhere in the code.
- Token transfers are handled through wrappers that tolerate the non-standard ERC20 implementations that return no value, and approvals to the strategy use a forced-approval routine that resets the allowance before setting it, which is the correct handling for tokens that reject a direct overwrite.
- The documentation in the source was rewritten and is now accurate about the vault's own behaviour, including the deliberate rejection of fee-taxed assets and the decision that yield belongs to the protocol. Two cautions remain. The vault interface still describes the opposite of what the implementation now does on both fee handling and withdrawal sourcing, and should be corrected before an integrator relies on it. And the source comments now carry this engagement's finding identifiers throughout, some of them pointing at the wrong finding; comments should describe the invariant being maintained rather than the report that asked for it.
Principal Risks for the Project Team and for Investors
The first report identified three themes. One is now closed, one is partially closed, and one is unchanged. They should be read together with the new issues the fixes introduced.
- Accounting overstating what the vault holds is resolved. This was the central defect of the first review and it has been dealt with properly. Deposits are now measured against the vault's own balance and must match the requested amount exactly, so an asset that takes a cut on transfer is rejected at the door instead of being credited at an inflated figure, and the strategy's self-reported balance no longer participates in the calculation at all. The solvency property that previously failed on the very first deposit now survives its full budget in all three fuzzing engines and across the Foundry invariant suite. Two consequences of choosing rejection over measurement should be accepted deliberately rather than discovered later: the asset allowlist is now a security control, because a token whose transfer behaviour changes after deployment silently stops accepting deposits, and a token that hands control to third-party code mid-transfer turns a surplus inflow into a failed deposit rather than an inflated credit.
- The ability to return funds no longer depends on where the strategy chose to put the money. This theme is now closed. A broken strategy can always be detached without being called; a strategy that pays less than asked cannot clear a claim while reporting success; and, with this revision, assets sitting idle in the vault are reachable, so a strategy that caps how much it will accept or return no longer blocks withdrawals the vault could cover itself. The property test that previously demonstrated a credited position could not be redeemed now survives its full budget in the Foundry invariant suite and in both coverage-guided fuzzers, and we validated that property against the previous version of the contract to confirm it still detects the defect rather than having been weakened. Two operational limits should still be written into the incident runbook rather than left to be rediscovered - whatever a broken strategy still holds is not recovered by detaching it, and a native-mode vault has no way to be funded from anywhere except its attached strategy, which is precisely the component that has just been detached.
- Protocol revenue, rather than depositor funds, is where the remaining loose ends sit. None of these can cost a depositor their principal, and we record them so that the project does not discover them after launch. The channel built to collect strategy yield currently rejects claims that move only yield, because it is bounded against the strategy's balance before the claim rather than against what the vault owes its users, so the yield stays in the strategy. Surplus that reaches the vault when a strategy returns slightly more than requested is retained by design but has no route out, since it is deliberately not in the ledger and the asset rescue correctly refuses the configured asset. And the check on what a depositor actually received applies only when the strategy is entered, so a withdrawal the vault covers on its own is unverified, which matters for an asset that shortens transfers. Each of these is a small, well-understood change, and all three should be made before the vault holds funds so that the accounting the project reports matches what the contract does.
- Administrative power remains unconstrained, and the team has decided that it should stay that way. This is the theme that has moved least, and it is the largest single gap between the contract's technical quality and its readiness to hold user funds. The surrounding controls did improve: ownership transfer is two-step, renouncing is impossible, the incoming strategy is validated, and a guardian can stop new deposits during an incident. None of that constrains the action that matters. There is no timelock of any length on any setter, the strategy is granted an unlimited allowance that persists for as long as it is attached, the vault's entire balance moves in the same transaction, and the deposit pause does not cover configuration, so the most consequential privileged action stays fully available exactly when a guardian has judged something to be wrong. We verified end to end that an owner can attach a hostile strategy, move the whole balance in one transaction, block every user withdrawal, and then sweep later inflows through the standing allowance. We recommended a timelock on the privileged setters and the team has declined it deliberately, on the grounds that a mandatory delay would also prevent the protocol from reacting at speed to an incident of the kind that has become more frequent across the industry, and that keeping these controls immediate is what allows them to respond. We consider that a legitimate trade-off and record it as an accepted design decision rather than an unresolved defect, with two qualifications: the argument is an argument against a blanket delay, whereas the exposure comes almost entirely from the single function that attaches a strategy, and the emergency responses it is protecting are already separate functions that could remain immediate either way. Investors should therefore understand the position clearly. None of this is a coding defect - it is a governance choice - but it means that users of the vault are trusting a single key directly, with no notice period and no opportunity to exit ahead of a change. Independently of the timelock decision, we would still expect ownership held by a multisig whose address, signers and threshold are published, the strategy allowance sized to the deposit rather than left unlimited, and the reward claim bounded so that it cannot reach principal; none of those three slows an emergency response down.
One precondition of this review was not met, for the third round running, and should be closed before deployment. The repository still contains only the four source files, with no build configuration, no dependency manifest, no lockfile and no deployment scripts, so the build again relied on an auditor-supplied configuration and auditor-pinned dependencies. That means the compiler version, the optimizer settings, the target chain rules and the library versions that will produce the deployed bytecode are all unverifiable from the repository. This matters more than usual here, because the reentrancy guard depends on transient storage and therefore on a Cancun-capable chain, a requirement currently recorded only in a source comment. All four files also still carry floating compiler pragmas, and the strategy interface accepts a far wider range than the vault itself, so a consumer can compile it with a compiler the vault would reject. Committing the build configuration, the lockfile and the deployment scripts, and pinning every pragma to the exact deployment version, would remove the possibility of the audited and deployed bytecode diverging.
On the pre-defined claims commonly checked by investors, our verdict is as follows. The contract cannot mint, cannot burn, cannot blacklist and cannot set fees, because none of those mechanisms exist in it. It is upgradeable, and the upgrade authority sits entirely outside the audited code with no delay attached, so the implementation should be assumed replaceable at any time by whoever controls the proxy administrator; the revision documents this honestly but cannot constrain it, and the administrator address should be published, held separately from the vault owner, and placed behind a timelock. On the question of whether funds can be locked, the answer is now no for every scenario we were able to construct. Ownership can no longer be renounced and a broken strategy can always be detached, which closed the permanent-freeze scenario from the first report. The two temporary lock-ups that remained after the first revision are closed by this one: a strategy that accepts only part of a deposit no longer blocks withdrawals against the idle remainder, and a strategy that returns marginally more than requested no longer blocks them at all. We were unable to construct any remaining case in which a depositor's recorded principal cannot be withdrawn, other than one requiring the vault to be configured with an asset the project already documents as unsupported. On the separate question of a delay on the privileged setters, the team has chosen not to add one so that it can respond to incidents immediately; we accept that as a documented trade-off, and investors should read it as meaning that the owner key can replace the strategy and move the entire balance at any moment, without notice.
Note - This Audit report consists of a security analysis of the HyperfolioVault smart contract. This analysis did not include economic analysis of the contract's tokenomics. Moreover, we only audited the main contract for the HyperfolioVault team. Other contracts associated with the project were not audited by our team, and this matters more than usual here: the gateway contract that controls all access to the vault, and every strategy contract the vault will delegate funds to, are both fully trusted by the code we reviewed and both fall outside this scope. That trust deepened with this revision rather than lessening - the vault now relies on strategies to register migrated funds, to deliver withdrawals to the exact wei, and to distinguish reward from principal when a claim is made, and none of those properties can be verified from inside the vault. The decision that yield belongs to the protocol also means depositors receive principal only, so the economics of the vault depend entirely on the farm layer paying out, and that layer is likewise outside this scope. We recommend investors do their own research before investing.
Files and details
Findings and Audit result
high Issues | 1 findings
Resolved
#1 high Issue
Trading lock can be re-engaged at any time while the owner stays liquid
The trading lock was flipped by a toggle rather than released by a one-way latch, so the owner could re-engage it at any point after launch with no delay and no limit on repetition. The guard blocked a transfer only when neither party was excluded, and the owner was excluded at construction, so the owner and every other excluded address stayed fully liquid while ordinary holders were frozen. The launch event fired only on the first release, so every subsequent change to this critical state was silent. This is the canonical honeypot construction and the highest-value target for a key compromise.
medium Issues | 1 findings
Resolved
#1 medium Issue
Renouncing ownership while trading is locked bricks the token permanently
Inherited renouncement was unguarded, so renouncing before launch, or at any moment while the lock was engaged, removed the only mechanism that could ever unlock transfers. Because the contract is not upgradeable and has no second administrator there was no recovery path, and every owner-only setter became permanently uncallable as well. Single-step ownership transfer produced the same outcome from a mistyped destination.
low Issues | 8 findings
Acknowledged
#1 low Issue
Exclusion and pair registration are unbounded bypasses, and every advertised limit can be removed
One call grants an address simultaneous immunity from the trading lock, the transaction size limit, the cooldown, the wallet cap and all fees, with no way to grant a subset. Marking an ordinary wallet as a trading pair is equally broad in the other direction, and de-registering the real pair silently zeroes all fee collection. The size and wallet limits enforce only a lower bound, so either can be raised to the integer maximum, and the cooldown accepts zero. Every anti-whale protection the documentation presents as fixed at two percent of supply per wallet and half a percent per transaction is therefore removable in full, instantly, and none of the five setters emits an event, so holders have no on-chain signal that any of it happened.
Acknowledged
#2 low Issue
Fees can be avoided entirely by trading through an unregistered venue
Fees apply only when one side of a transfer is a registered trading pair, and that registry is populated one address at a time by the owner. Any venue not explicitly registered is fee-free: a concentrated-liquidity pool, a pair against a different quote asset, a newly created pair, an over-the-counter desk, a lending market, or an exchange deposit address. Because anyone can create a pair permissionlessly, registration is inherently reactive, and peer-to-peer transfers are untaxed by design, so a trade can also be settled off-venue at zero cost.
Pending
#3 low Issue
Per-sender cooldown denies service to shared contracts and can be burned by any approved spender
The cooldown was keyed on the sender and stamped on every outgoing transfer, with no distinction between an individual wallet and a contract serving many users, so any shared contract holding the token was limited to one outgoing transfer per window in total across all of its users and one user's activity blocked everyone else. Separately, because the delegated transfer path stamps the token holder rather than the caller, any third party holding an allowance can consume a victim's cooldown slot.
Resolved
#4 low Issue
A claim source that is not also excluded cannot be funded and is throttled
The note on the claim-source flag presented it as the mechanism that lets a payout contract deliver allocations, which understated the consequence: an address flagged only as a claim source remains subject to the trading lock, the transaction size limit and the cooldown. An operator following the note literally got a sale that could not be funded above the wallet cap at all, and once funded could make only one payout per cooldown period across all buyers.
Resolved
#5 low Issue
Rotating a fee wallet leaves exclusions stale, and a non-excluded fee sink refunds its own fee
The three fee-wallet setters neither excluded the new address nor cleared the flag on the old one. The new wallet became subject to the trading lock, the size limit, the cooldown and the wallet cap when it tried to spend, while the previous wallet kept its exemption indefinitely, quietly widening the privileged set on every rotation. More significantly, a fee destination that was not excluded refunded its own fee leg when it sold, because that leg became a self-transfer worth nothing while the final transfer still deducted the full fee, so whoever controlled a sink had a fee-avoidance path.
Resolved
#6 low Issue
Ownership transfer leaves the exemption on the retired owner and grants none to the new one
Only the deploying account was excluded at construction, and transferring ownership did not touch the exclusion mapping. After ownership moved to a multisig the retired deploying key kept a permanent exemption from the trading lock, the size limit, the cooldown, the wallet cap and all fees, while the wallet that actually administered the contract held none of them. A key that had supposedly been retired retained privilege forever, and it is the single address most likely to be compromised, since it existed as a hot deployer key.
Resolved
#7 low Issue
Privilege is one shared boolean, so clearing it for one role revokes it for another
Exemption from the trading lock, the size limit, the cooldown, the wallet cap and all fees is expressed as a single boolean per address. Two of the fixes in this revision now write to that boolean automatically: the fee-wallet setters clear it for the retired sink, and an ownership handover clears it for the retired owner. Neither checks whether the address being cleared still holds another role that needs the exemption. Two reachable configurations follow. If a fee wallet is set to the deployer's address, which nothing in the constructor rejects, then handing administration to a multisig clears the exemption on an address that is still the configured fee sink. If two fee roles point at one address, rotating either clears the exemption while the other role still uses it. The consequence is that the self-refunding fee sink returns: a sink that is not exempt receives its own fee leg as a self-transfer, so the fee never leaves and the effective rate on its own sales drops. Measured on the resubmitted source, a wallet in this state pays four hundred basis points where five hundred is configured. It is also subject to the size limit and the cooldown when it tries to spend. Nothing is lost or minted, no user funds are at risk, and one call restores the exemption, but nothing warns that it happened and the deployment configuration that triggers it looks entirely reasonable.
Acknowledged
#8 low Issue
The cooldown exemption is keyed on code size, which is not an account-type test on this chain
The cooldown now applies only when the sender carries no code, which correctly stops a shared payout contract being throttled across all of its users. The check treats an empty code slot as proof of an ordinary wallet, and that equivalence no longer holds on the target chain: the Pascal hardfork activated delegated account code on mainnet in March 2025, so an ordinary wallet can install a delegation designator and carry code while remaining an externally owned account under the same private key. Such a wallet reads as a contract here and skips the cooldown entirely. The anti-sniper cooldown therefore becomes opt-out: any participant willing to sign one delegation transaction, which is a routine account-abstraction operation rather than an exploit, trades with no per-sender throttling while ordinary wallets remain limited to one transfer per window. The advantage accrues precisely to the sophisticated automated participants the cooldown exists to slow down. The cooldown is a soft launch-window measure rather than a security control and the owner can already set it to zero, which is what holds this low, but it directly qualifies a control the project has just changed.
informational Issues | 4 findings
Acknowledged
#1 informational Issue
A fee wallet can still be pointed at a registered trading pair, which switches off fee collection
Registering a trading pair now refuses an address that is one of the three fee wallets or the token itself, which closes one direction. The three fee-wallet setters were not given the matching guard, so the same end state is still reachable from the other side: pointing a fee role at an address that is already a registered pair is accepted without complaint, and the setter then excludes it. Because a transfer is only taxed when neither party is excluded, the venue becomes fee-free and every trade through it stops paying. Confirmed on the resubmitted source: after that single call a sale into the registered pair delivered the full amount and none of the three sinks received anything. A second, smaller consequence is that the new guard also runs when the flag is being cleared, so while an address holds both roles it cannot be de-registered as a pair at all and has to be unwound in the other order. Held at informational because it grants no capability the owner does not already have: excluding the pair directly reaches the same end state in one call and is an explicit part of the accepted exemption design, so this adds nothing to a malicious or a compromised owner. Against an honest one it is a configuration mistake with no external trigger, no user funds at risk, and only the project's own revenue affected. It is announced by the wallet-rotation event and one call restores the previous state. It nonetheless silently removes all fee revenue while it lasts, which is why it is worth closing.
Acknowledged
#2 informational Issue
The constructor accepts the token itself as a fee sink although its own comment says it does not
The three rotation setters gained a check that refuses the token's own address as a fee destination. The constructor gained distinctness checks in this revision but not that one. The comment introducing those checks states that the fee wallets must be distinct from each other, from the deployer, and from the token contract itself, and the first two are enforced by a require while the third is not enforced anywhere. The configuration the setters exist to prevent can therefore still be established at deployment, and a reader of the constructor would reasonably believe it cannot. The address is knowable in advance, because a contract address is derived from the deploying account and its nonce, so this is reachable by a script that computes it rather than only by coincidence. Fees then accrue to a contract with no sweep function, no approval path and no owner-controlled transfer, so they are unrecoverable. Confirmed on the resubmitted source: forty thousand tokens were stranded inside the contract by a single one million token sale, and the setter refuses the very configuration the constructor accepted. Held at informational because it is a one-shot deployment mistake with no external trigger, reachable only by a script that deliberately computes the address it is about to deploy to and passes it back as an argument. It is fully visible in the constructor arguments on the block explorer before any user funds exist, and the runbook already requires reading the three configured sinks back. What is lost is the project's own future fee revenue rather than anything a holder or a buyer owns. The substance worth acting on is that the comment and the code disagree, which is the kind of gap that survives review.
Acknowledged
#3 informational Issue
The new cooldown floor removes the end state the design depends on
The cooldown setter gained a lower bound of fifteen seconds, so the value can no longer be set to zero. The reasoning offered for keeping the code-size test as the account-type check is that the cooldown is a soft launch-window measure that is intended to be relaxed once the launch window has passed, and the previous review recommended standing it down as the planned end state for the same reason. The floor removes that option permanently. Two costs become permanent with it. The first is the surviving half of the cooldown finding: the delegated transfer path stamps the token holder rather than the caller, so anyone holding an allowance can consume that holder's slot, and at the floor a single dust pull every fifteen seconds keeps a holder blocked indefinitely for the price of one transaction per window. Confirmed on the resubmitted source across repeated cycles, with the blanket exemption the only remaining remedy. The second is that every ordinary wallet carries a per-transfer throttle for the life of the token, while a wallet that installs a delegation designator does not, so the asymmetry the previous review described also becomes permanent. Held at informational because neither of those two impacts is introduced here. Both were already reported, both are already carried at low severity against the cooldown itself, and both are unchanged in reach and cost by this revision. What this records is narrower and is a consistency problem rather than a new exposure: the revision states in one place that the cooldown is meant to be relaxed after launch and in another makes relaxing it impossible, so the rationale offered for accepting the known bypass no longer describes the contract it is attached to. Recording it at low as well would count the same underlying impact twice.
Acknowledged
#4 informational Issue
The comment on the immutable limits claims more than the contract guarantees
Both size limits became constants in this revision, and the comment introducing them states that locking them makes the interface's claim of no admin override and no fine print literally true and verifiable. The values are indeed fixed forever, which is a real improvement. Their enforcement is not. The size limit is skipped whenever either party to a transfer is excluded, and the wallet cap is skipped whenever the recipient is excluded, the recipient is a registered pair, or the sender is flagged as a claim source. All three flags are owner-controlled and unbounded. Confirmed on the resubmitted source: one call let a wallet send three times the transaction limit, a second let a recipient hold three times the wallet cap, and flagging a sender as a claim source waived the recipient's cap for everyone it pays. The distinction matters because the claim is made to holders rather than to developers, and the honest version of it is that the figures are fixed while exemption from them is not.