Orchard Info

AAPL Orchard is a 75-second planting game on Robinhood Chain. Each harvest one of 25 plots blooms, and whoever planted there splits the crop — paid in AAPL, tokenized Apple stock.

Orchard 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.

48.80
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/03
Revision date 2026/08/03

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 Analysis Statement

Contract Analysis

The Orchard contract implements an ERC20 token with a fixed supply of one million units, a three percent tax applied to purchases and sales through its Uniswap pair, launch phase restrictions intended to limit sniping, and an automatic mechanism that converts the collected tax into ETH and forwards it to a fixed fee recipient. The source published on the block explorer matches the deployed bytecode exactly, which we confirmed by recompiling with the recorded compiler settings and comparing the result byte for byte. The token cannot mint, cannot burn, has no blacklist, no pause switch, and is not upgradeable - there is no proxy and no delegatecall anywhere in the deployed code. Total supply is fixed at construction and we proved by formal analysis, and separately by more than seven hundred thousand randomised test transactions, that no execution path exists which can change it.

The assessment found no critical and no high severity issues. What we did find concentrates in one area of the code. The transfer logic assigns a provisional tax before it works out what kind of transfer it is dealing with, and two paths then fail to replace that provisional value. One of those paths is the transfer that seeds the liquidity pool when trading opens, which means the pool was taxed against itself at launch. This is not a theoretical concern: the on chain record shows that the full supply of one million tokens was handed to the contract along with two ETH, yet the liquidity tokens that were minted correspond exactly to a pool of nine hundred and seventy thousand. Thirty thousand tokens, three percent of the supply, were withheld and later sold as ordinary tax, and the opening price was set about three percent above what the team intended. The second affected path meant that ordinary wallet to wallet transfers were charged three percent instead of the configured zero percent for as long as no purchase had yet taken place. That window has since closed for this token but it applied to every distribution made before launch.

We also found that the anti whale restrictions do not achieve what a reader would expect. The size limits are skipped entirely during the opening window, and outside that window they only apply to purchases, never to transfers between wallets or to sales. A position larger than the wallet limit can therefore be assembled simply by buying from several addresses and consolidating. Both limits have in any case already been lifted on the live token.

The counter that drives all of the launch phase behaviour deserves particular mention, because it can be advanced by anyone at no cost. The contract treats any transfer coming out of the trading pair as a purchase, including one that moves nothing at all, and a standard Uniswap pair allows anybody to trigger exactly such a transfer. Each one costs only gas. Driven far enough before trading opens, the opening block allowance is used up in advance and every genuine purchase in that block is rejected, which turns the anti sniping measure into an obstacle to the launch it was meant to protect. The same weakness cuts the other way as well, and instructively so: because the liquidity was only taxed against itself while that counter still stood at zero, a single one of these free calls made before trading opened would have prevented the entire thirty thousand token shortfall described above. On the deployed token none of this can still be used for gain, since the counter has long passed every threshold that depended on it, but it is a significant weakness in the underlying template.

Two further points concern the mechanism that converts collected tax into ETH. Every conversion is submitted without any minimum acceptable output, which allows others to trade around it and capture part of the proceeds. Separately, that conversion runs inside the transfer of whoever happens to trigger it, and it is not isolated from failure, so if the conversion fails for any reason the seller's own transaction fails with it. A holder can therefore find themselves unable to sell through no fault of their own. These two interact, and the order in which they are addressed matters: adding a minimum output bound without first isolating the conversion would turn an ordinary price movement into a market that nobody can exit.

Finally, taxation applies only to the single trading pair created when the token was deployed. Anyone is free to create another pair for this token, and trades routed through it are treated as ordinary transfers and pay nothing. No holder loses anything as a result - what is lost is fee income - but it does mean the three percent figure should be read as applying to the main pair rather than universally.

Ownership Privileges

The ownership of the contract has been renounced, and the owner address now reads as zero. This is an important detail to understand correctly, because it does not mean the token is unowned. A second privileged address is written permanently into the contract at deployment and retains meaningful authority. The owner retains full privileges including:

  • Setting the buy and sell tax at any time, without notice and without any delay, through a permanently hard coded operator wallet that survives the ownership renouncement
  • Triggering an unlimited conversion of the entire accumulated tax balance into ETH in a single transaction, ignoring the per swap ceiling that the automatic path respects
  • Withdrawing any token held by the contract, including the contract's own accumulated tax, directly to the operator wallet rather than to the declared fee recipient
  • Withdrawing any ETH held by the contract to the operator wallet, including ETH sent to the contract by mistake
  • The tax can never exceed three percent - this ceiling is fixed permanently in the contract code and cannot be raised by anyone, which was confirmed by formal proof and by extensive randomised testing
  • No privileged function can create new tokens, destroy existing ones, freeze an account, blacklist an address, or take tokens out of any holder's balance
  • The functions that were restricted to the owner, namely opening trading and lifting the trading limits, are now permanently unreachable because the owner address is zero
  • The operator wallet is an ordinary externally owned account rather than a multi signature wallet, it cannot be transferred, replaced or given up, and there is no timelock in front of any of its functions

Security Features

The contract implements several positive security features:

  • The entire liquidity position was burned to an unrecoverable address at launch and the deployer holds none of it, so the liquidity is permanently locked and cannot be withdrawn by anyone
  • Supply is immutably fixed at one million tokens with no mint or burn path of any kind, and the tax ceiling of three percent is stored as an immutable value that no function can modify
  • The contract is built on a current and widely reviewed standard library, uses custom errors and explicit parameter validation throughout, contains no assembly, no delegatecall and no self destruct, and performs all arithmetic with the compiler's built in overflow protection and no unchecked blocks anywhere
  • The automatic tax conversion is bounded by the size of the sale that triggers it, which meaningfully limits both the price impact of each conversion and the value that can be extracted around it

A Note on Reuse

This assessment covers a deployed and immutable token, and separately the reusable contract it was generated from. The distinction matters for anyone reading the severity counts. Several of the issues described above are serious in the code but can no longer be acted on against this particular token, because its launch window has closed, its limits have been removed and its tax ceiling is permanently fixed at three percent. Read against the live token, one medium and six low severity findings remain genuinely exploitable, and none of them can reach holder balances. The remainder are either historical, unreachable under this configuration, or matter only if the same contract is deployed again. Our view is that the deployed token is sound within the bounds described, and that the underlying template should not be reused without first addressing the launch counter, the single pair taxation model, the incomplete parameter validation and the way a failed tax conversion can block a sale.

Verification Performed

Beyond manual line by line review, the following was established independently:

  • The recompiled code is byte for byte identical to the deployed code everywhere outside the forty two immutable value slots, and each of those forty two values was decoded and matched against the recorded constructor arguments, so no part of the deployed contract is left unaccounted for
  • Eleven properties covering value conservation on all three transfer classifications, the behaviour of zero value transfers, allowance arithmetic, the impossibility of a tax exceeding the transferred amount, and access control on the privileged functions were proved to hold for all possible inputs rather than merely tested
  • A further ten properties held across more than fifty thousand randomised transaction sequences, and twenty four across a further seven hundred thousand, with no counterexample found
  • Forty six targeted tests reproduce each reported issue against a faithful reconstruction of the live deployment, so every finding in this report is accompanied by a working demonstration rather than an assertion

We would add one qualification about what those numbers mean, since figures of this kind are easy to over read. A proof holds only over the conditions it was set up under, and a count of randomised transactions measures how much of the code was reached rather than how strictly it was checked. Following an independent review of this assessment, four of our automated properties were found to be weaker than their names suggested and were rewritten to assert what they claimed to, and additional adversarial scenarios covering liquidity operations and direct manipulation of the pair were added. The findings above reflect the strengthened versions. We mention this because the value of testing lies in whether the checks were capable of failing, not in how many times they were run.

Note - This Audit report consists of a security analysis of the Orchard smart contract. This analysis did not include economic analysis of the contract's tokenomics. Moreover, we only audited the main contract for the Orchard 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 | 5 findings

Pending

#1 medium Issue
Initial liquidity is taxed against itself, so three percent of the pool allocation never reaches the pair
UtilityToken.sol
L229-232
L252
L261-266
Description

The transfer hook sets a provisional tax of initialBuyTax percent whenever buyCount is still zero, before it decides which kind of transfer it is looking at. When trading is opened the token contract moves its whole balance into the pair. That leg matches none of the branches that follow - it is not a purchase from the pair, the sell branch explicitly excludes the token contract as sender, and the plain transfer branch only runs once at least one purchase has happened. The provisional three percent therefore survives and is skimmed off the liquidity. The pair receives ninety seven percent of what was sent and the remaining three percent stays behind in the token contract, where it is later sold as if it were ordinary trading tax. This is measurable on chain for the live token: the whole supply of one million was sent to the contract together with two ETH, yet the liquidity tokens that were minted correspond exactly to a pool of nine hundred and seventy thousand tokens, so thirty thousand tokens were withheld. The listing price ended up about three point one percent higher than intended and the pool is permanently shallower than the project planned. It is worth recording how narrowly this depended on the purchase counter still being at zero: had anyone advanced that counter beforehand, which costs nothing but gas, the provisional tax would have been skipped and the full allocation would have reached the pool.

Pending

#2 medium Issue
Plain wallet to wallet transfers are taxed at the buy rate until the first purchase happens
UtilityToken.sol
L229-232
L255-257
Description

The configured transfer tax for this token is zero, so a transfer between two ordinary wallets is expected to arrive in full. That only holds once at least one purchase has been recorded. While the purchase counter is still at zero the provisional tax set at the top of the transfer hook is never overwritten, because the branch that would apply the transfer tax is guarded by a check that at least one purchase has happened. The result is that every wallet to wallet transfer made before the first buy loses three percent of its value to the contract, silently and contrary to the configured parameters. On the live token this window has long closed, but it applied to every distribution made before launch, and it re-opens in full for any new token created from the same template.

Pending

#3 medium Issue
Transaction and wallet size caps are not enforced at all during the opening window
UtilityToken.sol
L233-249
Description

The purchase path contains two mutually exclusive branches. If the current block equals the block in which trading was opened, only the count of purchases is checked against the first block cap. The size checks against the maximum transaction amount and the maximum wallet size sit in the alternative branch and are therefore skipped entirely for the whole duration of that opening window. Anyone transacting inside that window can take a position of any size, limited only by the depth of the pool and by the total number of purchases allowed. Testing against a faithful reproduction of the deployment shows a single purchase of roughly two hundred and fourteen thousand tokens against a configured cap of twenty thousand, which is more than ten times the intended limit. The effect is amplified on this network because the block number visible to the contract advances only about once every sixteen seconds while the network itself produces roughly one hundred and fifty blocks in that time, so what reads like a single block in the source is in practice a wide and predictable window.

Pending

#4 medium Issue
Anti whale caps only cover purchases from the pair and are bypassed by consolidating wallets
UtilityToken.sol
L233-249
Description

The maximum transaction and maximum wallet checks are only reached when the sender is the trading pair, which means they apply to purchases and to nothing else. Transfers between wallets are not checked, and sells are not checked either. A party that wants to build a position larger than the wallet cap only needs to buy up to the cap from several addresses and then move everything into one address afterwards, which the contract permits without objection. This was reproduced directly: three wallets each bought at the limit and consolidated into a single wallet holding roughly forty two thousand tokens against a cap of twenty thousand. The same gap means an oversized position can also be sold in one transaction, since the transaction size check never runs on the sell path.

Pending

#5 medium Issue
Tax swaps accept any output amount and use the current block time as their deadline
UtilityToken.sol
L295-306
Description

Every conversion of collected tax into ETH is submitted with a minimum output of zero, which means the contract will accept whatever the pool returns no matter how unfavourable. It also passes the current block timestamp as the deadline, which can never expire and therefore offers no protection against a delayed inclusion. Because a sell by any user can trigger one of these conversions, an observer can place orders either side of it and capture part of the proceeds. The value lost comes out of the fee recipient's income and the accompanying price movement is borne by holders. The automatic path does limit each conversion to the size of the sell that triggered it, which meaningfully caps the damage per event, but the operator triggered conversion has no such limit and can move the entire accumulated balance in one unprotected swap.

low Issues | 13 findings

Pending

#1 low Issue
Ownership has been renounced but a hard coded operator wallet keeps permanent control
UtilityToken.sol
L88
L184-217
Description

Ownership of the live contract has been given up and the owner slot now reads as the zero address, which is commonly taken as a sign that no one can influence the token any more. That reading is incomplete. A second privileged address is fixed at deployment time and is stored as an immutable value, and it still governs setting the buy and sell tax, triggering a manual conversion of the collected tax, withdrawing arbitrary tokens held by the contract, and withdrawing ETH held by the contract. There is no setter for this address, no way to transfer it, and no way to renounce it, so the control is permanent for the life of the token. The address in question is an ordinary externally owned account rather than a multi signature wallet, so a single compromised key is sufficient to exercise all of it. The practical impact is contained because the tax ceiling is fixed at three percent and cannot be raised, and because the contract cannot mint, freeze or seize holder balances, but the disclosure matters.

Pending

#2 low Issue
The rescue function can take the contract's own accumulated tax to the operator wallet
UtilityToken.sol
L201-208
Description

The rescue helper is intended to recover unrelated tokens that were sent to the contract by mistake. It does not exclude the contract's own token, so calling it with the token's own address transfers the entire accumulated tax balance directly to the operator wallet. The transfer runs while the internal swap latch is engaged, so it is not taxed on the way out. This bypasses the fee recipient entirely and changes the form of the payout from ETH proceeds to raw tokens, which the operator can then dispose of on their own terms. It is within the operator's existing trust boundary rather than an outside attack, but it is an undeclared route for value to leave the contract and it undermines the separation between the operator wallet and the fee recipient that the rest of the design sets up.

Pending

#3 low Issue
Rescue of tokens that return a malformed success value fails permanently
UtilityToken.sol
L308-315
Description

The rescue helper interprets the response of the outbound transfer by decoding it as a boolean whenever the response is not empty. Some tokens return a payload that is shorter than a full word. Decoding such a response does not fail gracefully - it triggers a low level panic that aborts the whole call. The consequence is that any token behaving this way can be received by the contract but never recovered from it, so the balance is stuck for good. The condition is easy to reach in practice because non standard return data is common among older and hand written tokens, and it was reproduced with a token returning a single byte.

Pending

#4 low Issue
The tax conversion runs before balances are updated and its payout can re-enter with taxation disabled
UtilityToken.sol
L252-254
L277-293
Description

When a sell is processed, the contract converts part of its accumulated tax to ETH in the middle of the transfer hook, before the sender's and recipient's balances have been written. The proceeds are pushed to the fee recipient with all remaining gas forwarded. If that recipient is a contract, it regains control at a point where the internal swap latch is still engaged, and while that latch is engaged the transfer hook skips taxation and every size limit. A fee recipient behaving this way can therefore move tokens free of tax and free of limits, which was reproduced in testing. Balance readings taken by outside integrators during this window also reflect an intermediate state rather than a settled one. On the live token the fee recipient is an ordinary externally owned account with no code, so there is nothing to call back and the issue is not currently exploitable, but the ordering is incorrect and the exposure would become real the moment that address were replaced by a contract in a future deployment.

Pending

#5 low Issue
The operator triggered conversion ignores the per swap size limit
UtilityToken.sol
L184-190
Description

The contract defines a ceiling on how many tokens may be converted to ETH in a single swap, and the automatic conversion respects it. The operator triggered conversion does not consult it at all and instead sells the entire balance the contract is holding. With a zero minimum output on the same call, a large accumulated balance is pushed through the pool in one transaction, producing a price impact well beyond what the configured ceiling was meant to allow and creating an obvious opportunity for others to trade around it. Testing showed a balance six times the configured ceiling being sold in a single swap.

Pending

#6 low Issue
The trading gate is advertised but never actually enforced
UtilityToken.sol
L54
L100
L161
L173
Description

The contract declares a dedicated error for the case that trading is not yet open, and it maintains a flag that is set when trading is opened. No code path anywhere reads that flag to restrict transfers, and the error is never raised. Transfers therefore work normally before trading is opened, which was confirmed by moving tokens between two wallets on a freshly deployed instance. The practical exposure at launch was small, since the deployer held the entire supply at that point, but the arrangement is misleading to anyone reviewing the source and it would allow trading to begin on an alternative venue ahead of the official opening if any tokens had been distributed beforehand.

Pending

#7 low Issue
Liquidity provision and withdrawal are both taxed as trades
UtilityToken.sol
L233-249
Description

The hook decides what kind of transfer it is looking at purely by checking whether the pair is the sender or the recipient, so it cannot tell a trade apart from a liquidity operation. Withdrawing liquidity produces a transfer out of the pair, which is read as a purchase: the exit is charged the buy tax, it advances the purchase counter, and while the size limits are still active a large exit is rejected outright, preventing a provider from leaving in a single transaction. Adding liquidity produces a transfer into the pair, which is read as a sale: it is charged the sell tax and it triggers the automatic tax conversion, so the pair receives less than the depositor sent, the deposit ratio is skewed against them and they are issued fewer liquidity tokens than expected. On this deployment the whole liquidity position was burned and the limits have been removed, so the withdrawal side is academic, but the provision side still applies to anyone who wants to deepen the pool from here.

Pending

#8 low Issue
The template accepts a hundred percent tax and the opening tax rate ignores the declared ceiling
UtilityToken.sol
L269-275
L317-340
Description

Parameter validation accepts opening and closing tax rates of up to one hundred percent and a tax ceiling of up to one hundred percent. Separately, the helpers that decide the rate in force return the opening rate while the purchase counter is still at or below its threshold, and that opening rate is never compared against the declared ceiling. A deployment can therefore advertise a low ceiling and still charge far more than it during the opening phase, and because the parameter that controls when the opening phase ends is itself unvalidated it can be set high enough that the phase never ends. For the token actually deployed here the numbers are benign - the ceiling is three percent, the opening rates are three percent, and the ceiling is enforced on every later change - so the concern is confined to reuse of the template.

Pending

#9 low Issue
Liquidity is added with no minimum amounts and the result of the call is discarded
UtilityToken.sol
L160-176
Description

Opening trading adds the contract's whole token and ETH balance as liquidity while accepting zero as the minimum for both sides and discarding the three values the call returns. Because the amounts actually consumed are never compared against the amounts intended, the contract has no way of noticing when the pool ends up with less than was sent. That is not hypothetical here - it is precisely how the three percent liquidity shortfall went unnoticed. The missing minimums also mean that anyone who pushes tokens or wrapped ETH into the empty pair beforehand can shift the opening price, and the call will proceed regardless instead of rejecting the distorted result.

Pending

#10 low Issue
Ownership transfer is a single step and the approval race condition is not addressed
UtilityToken.sol
L53
Description

The contract uses the single step ownership pattern, so an ownership transfer takes effect immediately and a mistyped destination address would have permanently removed the ability to open trading or lift the limits. That risk did not materialise and no longer applies, because ownership has since been renounced deliberately. The token also inherits the well known allowance race condition of the standard, where changing a non zero allowance directly to another non zero value can be exploited by a spender who front runs the change and spends both amounts. Both are noted for completeness and for the benefit of future deployments from this template.

Pending

#11 low Issue
Purchase counter can be advanced by anyone at no cost
UtilityToken.sol
L233
L250-251
Description

The transfer hook treats any transfer coming from the trading pair as a purchase and increments the internal buy counter, with no check that the amount is greater than zero. A standard Uniswap pair exposes a public skim function that anyone may call, and on a pair whose balances match its reserves that call performs a zero-value transfer out of the pair. The token counts it as a real buy. Nothing is spent apart from gas, roughly 77,000 per increment. The counter is not merely a statistic - it gates the first-block purchase cap, both tax-reduction thresholds, the point at which automatic tax conversion becomes possible, and the provisional tax that applies while the counter is still zero. Before trading is opened the first-block branch is never taken, so the counter can be driven arbitrarily high in advance. Once the team then opens trading, the cap is already exhausted and every genuine purchase in the opening block is rejected, which turns the anti-sniping control into a denial of service against the launch itself. The same weakness has a second face: because the launch self-taxing issue only occurs while the counter is still zero, a single free skim beforehand would have skipped it entirely and delivered the full liquidity allocation to the pool. On the deployed token this is spent - the counter stands at 2,386, trading is open and every affected threshold has been passed - but in the reusable template it defeats the launch protection outright.

Pending

#12 low Issue
A failed tax conversion also cancels the user's sale
UtilityToken.sol
L254
L289
L295-306
Description

When the accumulated tax reaches its threshold, the transfer hook converts part of it to the native currency by calling the router directly inside the user's transfer, and that call is not guarded. Any failure inside the router - a rejected payment to the fee recipient, insufficient liquidity in the pair, a zero conversion amount, or a minimum-output bound if one were later added - propagates outward and reverts the seller's transaction. A holder whose only involvement was being the one to push the accumulated tax over the threshold is unable to exit, and will remain unable to for as long as the underlying condition persists. This also constrains how the missing slippage protection can be fixed, since adding a minimum-output bound without first isolating the conversion would convert an ordinary price movement into a frozen market.

Pending

#13 low Issue
A zero conversion cap passes validation and permanently blocks sales
UtilityToken.sol
L288-289
L325-339
Description

Constructor validation rejects a conversion cap above one hundred percent but accepts zero, and the accompanying cross-check between the conversion threshold and the conversion cap is satisfied when both are zero. The resulting per-swap cap is zero, so the amount handed to the router is zero and the router rejects it. Combined with the fact that a failed conversion also cancels the user's sale, the outcome is that every sale which reaches the conversion threshold reverts, permanently. The same situation arises for small supplies where the cap is expressed in basis points and rounds down to zero. This is not reachable on the deployed token, whose cap is configured at one percent, but any future deployment from the same template can fall into it through a plausible configuration choice.

optimization Issues | 3 findings

Pending

#1 optimization Issue
Mutable state occupies five storage slots where two would be enough
UtilityToken.sol
L92-102
Description

The purchase counter, the conversion counter, the last conversion block and the opening block each occupy a full storage slot, and the three boolean flags share a fifth. All seven values fit comfortably in a single slot: the counters never approach the range of a thirty two bit integer, the block markers never approach a forty eight bit one, and the flags need one byte each. Packing them would save four slots at deployment and, more usefully, would collapse several separate writes on the sell path into one, since the conversion counter and the last conversion block are written together every time a conversion happens.

Pending

#2 optimization Issue
Small recurring inefficiencies on the hottest execution path
UtilityToken.sol
L227
L290
L309
Description

The transfer hook reads the owner from storage on every transfer, including the very large majority of transfers where the result cannot change the outcome. The conversion counter uses a post increment where the previous value is discarded, and a pre increment would be marginally cheaper. The outbound call in the rescue helper builds its payload with the selector based encoding helper rather than the type safe alternative, which offers no gas benefit and loses compile time checking of the argument types.

Pending

#3 optimization Issue
Repeated numeric literals and a non payable constructor
UtilityToken.sol
L110
L127-142
L250-256
L320-333
Description

The values one hundred and ten thousand appear as bare literals throughout the tax and validation logic, in more than twenty places. Because the contract mixes two different scales, expressing some rates as whole percentages and others in basis points, these bare numbers are the only thing distinguishing the two and it is easy to misread which is in play. Naming them would remove that ambiguity at no cost. The constructor is also not marked payable, which adds a small unnecessary check to deployment.

informational Issues | 9 findings

Pending

#1 informational Issue
Name and symbol are declared twice and the inherited copies are written to storage but never read
Orchard.sol
L25-26
L34-40
Description

The token defines its name and symbol as constants and overrides the corresponding accessors to return them, while the inherited implementation also stores its own copies in contract storage during construction. The stored copies are never read again, because the overrides bypass them entirely. Inspecting the live contract's storage confirms this: the third and fourth slots hold the strings Orchard and SEED and nothing ever reads them. The effect is two wasted storage writes at deployment, worth roughly forty four thousand units of gas, and a duplicated definition that anyone reading the storage layout has to reconcile against the accessors.

Pending

#2 informational Issue
Events carry no indexed parameters and some state changes emit nothing at all
UtilityToken.sol
L64-70
Description

None of the events declared by the contract mark any parameter as indexed, which makes it awkward for monitoring and accounting systems to filter them. The rescue event in particular carries a token address that cannot be filtered on. Separately, several paths change nothing and emit nothing while still reporting success: the ETH withdrawal and the manual conversion both return quietly when the relevant balance is zero, and the function that accepts incoming ETH records nothing at all. Anyone reconstructing the contract's history from its logs will find these gaps.

Pending

#3 informational Issue
No documentation comments on any public or external function
UtilityToken.sol
L1-343
Description

The contract carries no documentation comments at all. There is nothing describing the intended tax schedule, the meaning of the seventeen constructor parameters, the trust assumptions attached to the operator wallet, or the expected sequence for bringing the token to market. For a contract whose tax logic depends on the interaction of several counters and thresholds this is a meaningful obstacle to review and to safe reuse, and the absence of a stated intent made it harder to distinguish deliberate behaviour from defects during this assessment.

Pending

#4 informational Issue
Block based logic behaves differently on this network than the source suggests
UtilityToken.sol
L234
L282-291
Description

This network reports the parent chain block number to contracts rather than its own, which was confirmed by measurement: the value the contract sees advances roughly once every sixteen seconds while the network produces about one hundred and fifty blocks in the same period. Every piece of logic keyed to a block therefore covers a much longer span than the source implies. The opening window in which size limits are skipped lasts about sixteen seconds rather than a fraction of a second, which is the reason the related finding is rated as it is. The limit of three tax conversions per block likewise applies per sixteen seconds, making conversions roughly one hundred and fifty times less frequent than the parameter suggests and allowing tax to build up longer between payouts.

Pending

#5 informational Issue
Several constructor parameters are accepted without any validation
UtilityToken.sol
L317-340
Description

Parameter validation covers the tax rates, the supply, the decimals and the percentage caps, but leaves four parameters entirely unchecked: the thresholds at which the buy and sell tax step down, the number of purchases required before conversions may begin, and the cap on purchases in the first block. A first block purchase cap of zero would cause every purchase during the opening window to be rejected, temporarily preventing anyone from buying. Thresholds set high enough would keep the opening tax rate in force indefinitely, which matters because that rate is not bound by the declared ceiling. The values used for this deployment are all sensible, so nothing is wrong with the live token, but the validation does not prevent a bad configuration.

Pending

#6 informational Issue
Floating compiler version and two functions with high branching complexity
UtilityToken.sol
L2
L219-267
L317-340
Description

The source declares an open ended compiler version rather than a fixed one. The deployed artifact was built with a specific release and its bytecode matches the published source exactly, so there is no discrepancy today, but an open version leaves room for a future build to differ. Separately, the transfer hook and the parameter validation routine both carry a high number of independent branches, fourteen and fifteen respectively. The transfer hook in particular combines the purchase, sell and plain transfer cases with the tax schedule and the size limits in one deeply nested block, and that structure is directly responsible for two of the defects reported here going unnoticed.

Pending

#7 informational Issue
The contract accepts ETH from anyone and only the operator wallet can retrieve it
UtilityToken.sol
L342
Description

The contract accepts ETH from any sender without restriction and without emitting anything. Once received, the only way for those funds to leave is the withdrawal function, which pays exclusively to the fixed operator wallet. Anyone who sends ETH to the contract by mistake has therefore made an irreversible transfer to that wallet. The mechanism is needed so the contract can be funded before liquidity is added, so the behaviour is intentional, but it deserves to be stated because the address is a plausible target for accidental transfers.

Pending

#8 informational Issue
Only one trading venue is taxed
UtilityToken.sol
L87
L233
L252
Description

The contract recognises a single trading pair, fixed at construction and immutable thereafter. Buy and sell taxation, the size limits and the purchase counter all key off that one address. Anyone may permissionlessly create a second pair for this token, against a different quote asset or through a different factory, and trades routed through it are seen by the token as ordinary wallet-to-wallet transfers. Those are charged the configured wallet transfer rate, which on this deployment is zero, so the alternate venue is untaxed. The size limits and the counter are bypassed along with it. This is inherent to how single-pair tax tokens are built and it costs holders nothing directly - what it costs is fee income, and it means the advertised three percent is optional for anyone willing to trade elsewhere.

Pending

#9 informational Issue
The wallet cap is measured before tax rather than after
UtilityToken.sol
L245
Description

The wallet size check compares the recipient's existing balance plus the full pre-tax amount against the cap, but the recipient is only credited with the amount that remains after tax has been withheld. The effective ceiling is therefore slightly lower than the configured one, by whatever tax has accumulated, and a buyer is turned away marginally before genuinely reaching the limit. The direction of the error is conservative - it can never allow a wallet to exceed the cap - so this is a correctness and documentation point rather than a security weakness. It is recorded because the cap does not behave exactly as its name suggests.