Quantum Audit Logo

Is MAGIC Safe?

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

MAGIC MAGIC
0x539b…0342
Arbitrum Not verifiedLast checked 3d ago 1 audit on record
Executive SummaryAI Copilot

The audit of the StandardArbERC20 implementation contract, used via a ClonableBeaconProxy, identified a High-severity access control vulnerability related to the initialization of cloned token instances. Additionally, Medium and Low severity issues were found concerning ERC20 standard compliance and parsing logic. The contract generally demonstrates good architectural patterns for an L2 bridged token, leveraging OpenZeppelin libraries and a beacon proxy for upgradeability. However, critical attention is required for the initialization process of new token clones to prevent unauthorized control.

1 High1 Medium1 Low2 Informational
Volume 24h
$8.9K
Liquidity
$177.4K
Price
$0.04486
Token Age
4y
Top 10 Holders
66.3%

Security Findings

High

Unprotected `bridgeInit` Allows Malicious Initialization of Clones

H-01The `bridgeInit` function, which serves as the initializer for each cloned token instance, is declared as `public` without any access control. While `L2GatewayToken._initialize` prevents re-initialization of state variables within a single clone, the *first* caller to `bridgeInit` on a newly deployed clone can set the `l2Gateway` and `l1Address`. A malicious actor could front-run the legitimate gateway deployment or initialization, setting themselves as the `l2Gateway`, thereby gaining unauthorized control over `bridgeMint` and `bridgeBurn` functions for that specific token instance. This compromises the integrity of the token supply for affected clones (7.3 Access Control, 7.2 Code Securit…
IssueThe `bridgeInit` function, which serves as the initializer for each cloned token instance, is declared as `public` without any access control. While `L2GatewayToken._initialize` prevents re-initialization of state variables within a single clone, the *first* caller to `bridgeInit` on a newly deployed clone can set the `l2Gateway` and `l1Address`. A malicious actor could front-run the legitimate gateway deployment or initialization, setting themselves as the `l2Gateway`, thereby gaining unauthorized control over `bridgeMint` and `bridgeBurn` functions for that specific token instance. This compromises the integrity of the token supply for affected clones (7.3 Access Control, 7.2 Code Securit…
FixImplement an `onlyInitializing` or similar modifier to restrict calls to `bridgeInit` to a trusted entity (e.g., the deployer, a factory contract, or a designated initializer role). This ensures that only the intended party can configure the critical `l2Gateway` and `l1Address` for new token clones.
StatusUnresolved
Medium

ERC20 Standard Functions (`name`, `symbol`, `decimals`) Can Revert

M-01The `name()`, `symbol()`, and `decimals()` functions can revert if the corresponding `ignore` flags (`ignoreName`, `ignoreSymbol`, `ignoreDecimals`) are set to `true` during `bridgeInit`. These flags are set if the `BytesParser` fails to parse the provided `_data` for name, symbol, or decimals. This behavior deviates from the standard ERC20 interface, where these functions are expected to always return a value. Integrations relying on these functions might break if they do not handle reverts gracefully, leading to a denial of service for metadata retrieval (7.2 Code Security).
IssueThe `name()`, `symbol()`, and `decimals()` functions can revert if the corresponding `ignore` flags (`ignoreName`, `ignoreSymbol`, `ignoreDecimals`) are set to `true` during `bridgeInit`. These flags are set if the `BytesParser` fails to parse the provided `_data` for name, symbol, or decimals. This behavior deviates from the standard ERC20 interface, where these functions are expected to always return a value. Integrations relying on these functions might break if they do not handle reverts gracefully, leading to a denial of service for metadata retrieval (7.2 Code Security).
FixConsider returning default or empty values (e.g., empty string for name/symbol, 0 for decimals) instead of reverting when parsing fails. Alternatively, ensure robust validation of `_data` during `bridgeInit` to prevent these flags from being set unnecessarily, or provide a mechanism for a privileged role to correct the metadata if parsing initially fails.
StatusUnresolved
Low

`BytesParser.toString` Edge Case for 32-Byte Strings

L-01The `BytesParser.toString` function has specific logic for `input.length == 32`. If `input[31]` is not `bytes1(0x00)`, it returns `(false, res)`, indicating a parsing failure. This means a valid 32-byte string that happens to have a non-zero last byte (e.g., 'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEF') would be considered a parsing failure, leading to `ignoreName` or `ignoreSymbol` being set to `true`. This could cause the `name()` or `symbol()` functions to revert unnecessarily, even for valid inputs (7.2 Code Security).
IssueThe `BytesParser.toString` function has specific logic for `input.length == 32`. If `input[31]` is not `bytes1(0x00)`, it returns `(false, res)`, indicating a parsing failure. This means a valid 32-byte string that happens to have a non-zero last byte (e.g., 'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEF') would be considered a parsing failure, leading to `ignoreName` or `ignoreSymbol` being set to `true`. This could cause the `name()` or `symbol()` functions to revert unnecessarily, even for valid inputs (7.2 Code Security).
FixReview the `BytesParser.toString` logic for 32-byte strings to ensure it correctly handles all valid string representations. If the intent is to treat 32-byte inputs as `bytes32` and only convert if they are null-padded, this should be clearly documented or handled differently to avoid false negatives for valid strings.
StatusUnresolved
Info

Indirect `abi.decode` for String/Uint8 Parsing

I-01In `bridgeInit`, the `_data` is first `abi.decode`d into three `bytes` variables, which are then passed to `BytesParser.toString` and `BytesParser.toUint8`. If the `_data` is consistently encoded as `abi.encode(name, symbol, decimals)`, directly decoding to `(string, string, uint8)` might simplify the code. The current approach adds an extra parsing layer that handles specific byte representations (7.2 Code Security).
IssueIn `bridgeInit`, the `_data` is first `abi.decode`d into three `bytes` variables, which are then passed to `BytesParser.toString` and `BytesParser.toUint8`. If the `_data` is consistently encoded as `abi.encode(name, symbol, decimals)`, directly decoding to `(string, string, uint8)` might simplify the code. The current approach adds an extra parsing layer that handles specific byte representations (7.2 Code Security).
FixDocument the expected encoding format for `_data`. If the `_data` is always `abi.encode(name, symbol, decimals)`, consider direct `abi.decode(_data, (string, string, uint8))` for simplicity. If the `bytes` inputs are expected to be arbitrary byte sequences requiring specific parsing logic (e.g., 32-byte fixed-length strings), the current approach is justified, but the parsing logic should be robust and well-documented.
StatusUnresolved
Info

`TransferAndCallToken` Uses `extcodesize` for `isContract` Check

I-02The `isContract` function in `TransferAndCallToken` uses `extcodesize` to determine if an address is a contract. A known edge case with `extcodesize` is that it returns 0 during a contract's constructor execution. This means if `transferAndCall` is called to an address that is currently in its constructor, `isContract` would return `false`, and the `onTokenTransfer` callback would not be invoked (7.2 Code Security).
IssueThe `isContract` function in `TransferAndCallToken` uses `extcodesize` to determine if an address is a contract. A known edge case with `extcodesize` is that it returns 0 during a contract's constructor execution. This means if `transferAndCall` is called to an address that is currently in its constructor, `isContract` would return `false`, and the `onTokenTransfer` callback would not be invoked (7.2 Code Security).
FixBe aware of this EVM quirk. For most scenarios, this is not an issue as `transferAndCall` is typically used with already deployed contracts. If interaction with contracts during their construction phase is a critical use case, alternative checks or design patterns might be necessary.
StatusUnresolved

Category Ratings

TechnicalLow8/10

The StandardArbERC20 contract implements an L2 bridged ERC20 token, utilizing OpenZeppelin's upgradeable ERC20 standard and a `Cloneable` pattern for efficient deployment. Key strengths include the `onlyGateway` modifier for `bridgeMint` and `bridgeBurn` functions, ensuring controlled token supply management (7.3 Access Control). However, a significant vulnerability exists in the `bridgeInit` function, which is public and unprotected, allowing any caller to initialize a new clone and potentially set a malicious `l2Gateway` (7.3 Access Control, 7.2 Code Security). Furthermore, the `name()`, `symbol()`, and `decimals()` functions can revert if parsing fails during initialization, deviating from standard ERC20 behavior and potentially breaking integrations (7.2 Code Security).

GovernanceHigh1/10

This contract primarily functions as an L2 token implementation and does not contain direct governance mechanisms or complex economic models. Its economic security is largely tied to the integrity of the Arbitrum bridge and the L2 Gateway, which are external components. The `bridgeMint` and `bridgeBurn` functions are appropriately restricted to the `l2Gateway`, mitigating direct economic manipulation within the token contract itself (7.4 Economic, 7.5 Governance).

UpgradesHigh1/10

The contract is designed as an implementation for a ClonableBeaconProxy, enabling efficient deployment of multiple token instances and facilitating future upgrades via the beacon. The `_initialize` function includes a check (`ALREADY_INIT`) to prevent re-initialization of critical state variables within a single clone (7.7 Upgrades). However, the `bridgeInit` function, which triggers this initialization, lacks access control, creating a window for malicious actors to front-run the legitimate initializer for new clones (7.7 Upgrades, 7.3 Access Control).

Security Checklist

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

Proxy Upgrade Controls

Proxy TypeBeacon
ImplementationVerified source
Upgrades (30d)0 · stable

Holder Composition

66.3% in wallets0.0% in contracts
Effective Concentration66.3%

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 4 more pairsShow less

The 4 remaining pairs hold $23 between them and are not listed.

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 Holder55.2%
Top-3 Unlocked82.9%

Key Addresses

Deployer
0x3fe3…000f
Unlocked LP Held By
0x3887…7f1c0xdb6a…e2330x8f28…514e0x4e3b…d42e0x7c2d…d4370xf2e1…048e0x0856…0af80x378f…ec920xd510…20e40x84d9…39ca

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)
  • Proxy contract (upgradeable — admin can replace logic)
  • Complex proxy pattern (BEACON)
  • Top-10 concentration > 50% (66.3% total → 66.3% effective; 66.3% in EOAs, 0.0% in contracts — heavy)
  • Liquidity not locked, but no owner/deployer address holds LP — market-depth risk, not rug risk
  • LP top1 unlocked holder = 55.2% (independent LP — depth risk, pool = 79% of DEX liquidity)
  • LP top3 unlocked holders = 82.9% (independent LP — depth risk, pool = 79% of DEX liquidity)
  • 1 High finding(s) from audit
  • 1 Medium finding(s) from audit
  • 1 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

NOXCAT (NOX)High RiskWrapped BTC (WBTC)High RiskNolaHigh RiskGains Network (GNS)High RiskWINRHigh RiskEquilibria Token (EQB)High Risk

Would You Like a More Detailed Audit of MAGIC?

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

Get Detailed Audit