Quantum Audit Logo

Is Spell Token Safe?

On-chain security analysis — is it a scam or legit?

Spell Token SPELL
0x3e66…d2af
Arbitrum
Not verifiedThis record has not gone through deep verification and is not being monitored. The score is a dated snapshot — the token’s risk can change at any time.
Last checked today 1 audit on record
Executive SummaryAI Copilot

This audit covers the `StandardArbERC20` implementation contract, deployed via a `ClonableBeaconProxy` on Arbitrum. The contract functions as an L2 ERC20 token within the Arbitrum token bridge. Key findings include a critical re-initialization vulnerability in `bridgeInit` and a high-severity design flaw in the application of the `Cloneable` pattern within a proxy context. Several other issues of medium, low, and informational severity were identified, primarily related to initialization, metadata handling, and library usage.

1 Critical1 High1 Medium2 Low1 Informational
Volume 24h
$37.5K
Liquidity
$131.4K
Price
$0.0001027
Token Age
5y
Top 10 Holders
80.6%

Security Findings

Critical

Critical: `bridgeInit` Lacks Proper Initialization Guard

C-01The `bridgeInit` function in `StandardArbERC20` is `public virtual` and does not implement an `initializer` modifier or a similar 'only once' protection. While the internal `_initialize` function (called by `bridgeInit`) prevents `l2Gateway` from being set multiple times, `bridgeInit` itself can be called repeatedly. Subsequent calls would overwrite the `availableGetters` struct, potentially allowing an attacker to intentionally provide malformed `_data` to set `ignoreName`, `ignoreSymbol`, or `ignoreDecimals` to `true`. This would cause the `name()`, `symbol()`, or `decimals()` functions to revert for all users, leading to a denial-of-service for basic ERC20 metadata functionality (7.2 Cod…
IssueThe `bridgeInit` function in `StandardArbERC20` is `public virtual` and does not implement an `initializer` modifier or a similar 'only once' protection. While the internal `_initialize` function (called by `bridgeInit`) prevents `l2Gateway` from being set multiple times, `bridgeInit` itself can be called repeatedly. Subsequent calls would overwrite the `availableGetters` struct, potentially allowing an attacker to intentionally provide malformed `_data` to set `ignoreName`, `ignoreSymbol`, or `ignoreDecimals` to `true`. This would cause the `name()`, `symbol()`, or `decimals()` functions to revert for all users, leading to a denial-of-service for basic ERC20 metadata functionality (7.2 Cod…
FixImplement a robust 'only once' initialization guard for `bridgeInit`. This can be achieved by inheriting from OpenZeppelin's `Initializable` contract and applying the `initializer` modifier to `bridgeInit`. Ensure that `bridgeInit` can only be called once throughout the contract's lifecycle.
StatusUnresolved
High

High: Misapplication of `Cloneable` Pattern in Proxy Context

H-01The `StandardArbERC20` contract inherits from `Cloneable`, which has a constructor that sets `isMasterCopy = true`. In a proxy pattern (like `ClonableBeaconProxy`), the constructor of the implementation contract is never executed for the proxy instances. Consequently, `isMasterCopy` will always retain its default value of `false` in all deployed proxy instances. While the `safeSelfDestruct` function is `internal` and thus not directly callable, this design flaw indicates a misunderstanding or misapplication of the `Cloneable` pattern within a proxy context. If future logic or an upgrade were to rely on `isMasterCopy` being `true` for the master copy or `false` for clones, this setup would l…
IssueThe `StandardArbERC20` contract inherits from `Cloneable`, which has a constructor that sets `isMasterCopy = true`. In a proxy pattern (like `ClonableBeaconProxy`), the constructor of the implementation contract is never executed for the proxy instances. Consequently, `isMasterCopy` will always retain its default value of `false` in all deployed proxy instances. While the `safeSelfDestruct` function is `internal` and thus not directly callable, this design flaw indicates a misunderstanding or misapplication of the `Cloneable` pattern within a proxy context. If future logic or an upgrade were to rely on `isMasterCopy` being `true` for the master copy or `false` for clones, this setup would l…
FixRe-evaluate the necessity and implementation of the `Cloneable` contract. If the `isMasterCopy` distinction is required, it must be managed via an initializer function that is called by the proxy, not a constructor. Alternatively, if `Cloneable`'s functionality is not critical for this specific proxy setup, consider removing it to simplify the codebase and avoid potential confusion.
StatusUnresolved
Medium

Medium: Denial of Service for ERC20 Metadata via Malformed `_data`

M-01The `bridgeInit` function parses `_data` to set the token's name, symbol, and decimals. If `BytesParser.toString` or `BytesParser.toUint8` fails to parse the input (e.g., due to incorrect length or format), the corresponding `ignoreName`, `ignoreSymbol`, or `ignoreDecimals` flag is set to `true`. When these flags are `true`, the respective ERC20 metadata functions (`name()`, `symbol()`, `decimals()`) will revert. If `bridgeInit` is called with malformed data, either initially or maliciously (if C-01 is not resolved), it can lead to a permanent denial of service for these fundamental ERC20 functions (7.2 Code Security).
IssueThe `bridgeInit` function parses `_data` to set the token's name, symbol, and decimals. If `BytesParser.toString` or `BytesParser.toUint8` fails to parse the input (e.g., due to incorrect length or format), the corresponding `ignoreName`, `ignoreSymbol`, or `ignoreDecimals` flag is set to `true`. When these flags are `true`, the respective ERC20 metadata functions (`name()`, `symbol()`, `decimals()`) will revert. If `bridgeInit` is called with malformed data, either initially or maliciously (if C-01 is not resolved), it can lead to a permanent denial of service for these fundamental ERC20 functions (7.2 Code Security).
FixBeyond resolving C-01 to prevent re-initialization, consider stricter validation of the `_data` within `bridgeInit`. Instead of setting `ignore` flags, `bridgeInit` should revert if parsing of essential metadata fails. This ensures that the token is only initialized with valid and functional metadata.
StatusUnresolved
Low

Low: Missing `_disableInitializers` Call in Implementation Constructor

L-01The `StandardArbERC20` contract uses OpenZeppelin's upgradeable pattern, which typically requires calling `_disableInitializers()` in the constructor of the implementation contract. This prevents the implementation contract from being initialized directly, which could lead to storage collisions or unexpected behavior if a malicious actor were to call `_initialize` on the implementation directly, corrupting its storage layout for proxy instances (7.7 Upgrades).
IssueThe `StandardArbERC20` contract uses OpenZeppelin's upgradeable pattern, which typically requires calling `_disableInitializers()` in the constructor of the implementation contract. This prevents the implementation contract from being initialized directly, which could lead to storage collisions or unexpected behavior if a malicious actor were to call `_initialize` on the implementation directly, corrupting its storage layout for proxy instances (7.7 Upgrades).
FixAdd `_disableInitializers()` to the constructor of `StandardArbERC20` (or its base `L2GatewayToken` or `aeERC20` if they have constructors) to ensure the implementation cannot be initialized directly.
StatusUnresolved
Low

Low: `BytesParser.toString` Edge Case and Assembly Usage

L-02The `BytesParser.toString` function contains complex logic for handling 32-byte inputs, including a check `if (input[31] != bytes1(0x00)) return (false, res);` which assumes null-termination for 32-byte strings. This could lead to incorrect parsing for non-null-terminated 32-byte strings. Additionally, the function uses an `assembly` block (`res := inputTruncated`) for converting `bytes` to `string`. While `string` is essentially `bytes`, direct assembly for this conversion can be less safe and harder to reason about than `abi.decode(inputTruncated, (string))` (7.2 Code Security).
IssueThe `BytesParser.toString` function contains complex logic for handling 32-byte inputs, including a check `if (input[31] != bytes1(0x00)) return (false, res);` which assumes null-termination for 32-byte strings. This could lead to incorrect parsing for non-null-terminated 32-byte strings. Additionally, the function uses an `assembly` block (`res := inputTruncated`) for converting `bytes` to `string`. While `string` is essentially `bytes`, direct assembly for this conversion can be less safe and harder to reason about than `abi.decode(inputTruncated, (string))` (7.2 Code Security).
FixSimplify the `BytesParser.toString` logic. Consider using `abi.decode(input, (string))` directly for all valid string lengths, allowing the EVM to handle padding and decoding. If custom truncation is strictly necessary, ensure the logic is robust and avoid direct assembly where higher-level Solidity constructs suffice for safety and readability.
StatusUnresolved
Info

Informational: `TransferAndCallToken` Not Inherited

I-01The `TransferAndCallToken` contract, which provides `transferAndCall` functionality, is included in the provided source code but `StandardArbERC20` does not inherit from it. This means the `transferAndCall` feature is not available in the deployed token. This is not a vulnerability but an observation regarding potential intended functionality that is not present (7.1 Architecture).
IssueThe `TransferAndCallToken` contract, which provides `transferAndCall` functionality, is included in the provided source code but `StandardArbERC20` does not inherit from it. This means the `transferAndCall` feature is not available in the deployed token. This is not a vulnerability but an observation regarding potential intended functionality that is not present (7.1 Architecture).
FixClarify if `transferAndCall` functionality is intended for this token. If so, ensure `StandardArbERC20` (or one of its base contracts like `aeERC20`) inherits `TransferAndCallToken`.
StatusUnresolved

Category Ratings

TechnicalLow7/10

The technical architecture (7.1) leverages OpenZeppelin's upgradeable patterns and custom libraries for L2 token bridging. Code security (7.2) benefits from standard ERC20 practices, but custom logic in `bridgeInit` introduces a critical re-initialization vulnerability. Access control (7.3) for `bridgeMint` and `bridgeBurn` is appropriately restricted to the `l2Gateway` via an `onlyGateway` modifier, which is a strong point. However, the `BytesParser` library contains complex string parsing logic that could lead to unexpected behavior or metadata denial-of-service (7.2).

GovernanceHigh1/10

This contract serves as an L2 token implementation, with its economic model (7.4) inherently tied to its L1 counterpart and the L2 Gateway. The `onlyGateway` modifier effectively restricts critical functions like `bridgeMint` and `bridgeBurn` to a designated L2 Gateway, ensuring controlled supply management. No direct governance mechanisms (7.5) are present within this specific token contract, simplifying its operational scope.

UpgradesMedium5/10

The contract is designed for upgradeability using a ClonableBeaconProxy pattern (7.1, 7.7), allowing for efficient updates across multiple proxy instances. However, the `Cloneable` base contract's constructor sets `isMasterCopy = true`, which is not called in proxy instances, leading to `isMasterCopy` always being `false` and misapplying the pattern's intent (7.7). Additionally, the implementation contract lacks a call to `_disableInitializers()` in its constructor, which could lead to storage collisions if the implementation were ever directly initialized (7.7).

Security Checklist

Contract VerifiedPass
Ownership Renounced?
No Mint FunctionPass
Liquidity LockedFail
Not a ProxyPass
HoneypotNoneBuy Tax0.0%Sell Tax0.0%

Holder Composition

4.0% in wallets76.6% in contracts
Effective Concentration34.7%

Share held by contracts — treasury, vesting, bridge or staking — is discounted against share held by wallets when the score is computed: a contract cannot decide to sell the way an anonymous holder can, though it can still be drained or voted to sell. Effective concentration is the figure the risk score is actually calculated from.

Liquidity Depth

Show 2 more pairsShow less

The risk score reads depth across every pair. The volume figure and the volume-to-liquidity ratio elsewhere on this page describe only the pair this audit analysed, so the two are not directly comparable.

LP Distribution

Top-1 Unlocked Holder72.8%
Top-3 Unlocked96.9%

Key Addresses

Deployer
0x3fe3…000f
Unlocked LP Held By
0x3887…7f1c0x66be…b29a0x2ca9…2cb90x024d…bae80x3cad…17380x402d…6bb70xcc77…33490x895f…9ada0x3da4…1df10x2004…fcf4

No privileged address appears among these holders: the unlocked liquidity sits with independent providers, not with the deployer.

What Raised This Score

  • Ownership status UNKNOWN (owner could not be resolved)
  • Top-10 concentration > 30% (80.6% total → 34.7% effective; 4.0% in EOAs, 76.6% in contracts — moderate)
  • Liquidity not locked, but no owner/deployer address holds LP — market-depth risk, not rug risk
  • LP top1 unlocked holder = 72.8% (independent LP — depth risk, pool = 99% of DEX liquidity)
  • LP top3 unlocked holders = 96.9% (independent LP — depth risk, pool = 99% of DEX liquidity)
  • 1 Critical finding(s) from audit
  • 1 High finding(s) from audit
  • 1 Medium finding(s) from audit
  • 2 Low finding(s) from audit

Each factor is an on-chain fact recorded at the time of this analysis. The score is computed from them by a deterministic function, so the same contract returns the same score for anyone who runs the audit. How scores are computed

Related Audits

Espresso (ESP)High RiskLivepeer Token (LPT)High RiskOrderly Network (ORDER)High RiskGraph Token (GRT)High RiskCurve DAO Token (CRV)High RiskODYSHigh Risk

Would You Like a More Detailed Audit of Spell Token?

Our AI-powered scanner gives you a deeper, real-time smart contract analysis — free, with every scoring factor shown.

Get Detailed Audit