Quantum Audit Logo

Is Irys Safe?

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

Is this your token? Publish your own audit on this page →

Irys IRYS
0x9115…035d
BNB Chain
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.Own this token? Put it under verification →
Last checked today 1 audit on record
Executive SummaryAI Copilot

The IrysOFT contract, an upgradeable LayerZero Omnichain Fungible Token, was audited for security vulnerabilities. A critical vulnerability was identified in the `initialize` function, which would cause the contract's deployment to revert due to a double initialization of the Ownable component. This prevents the contract from being properly set up. Other findings include minor optimizations and best practice recommendations. The contract leverages well-audited OpenZeppelin and LayerZero libraries, and features a robust access control model with a multisig owner.

1 Critical1 Low2 Informational
Volume 24h
$2.16M
Liquidity
$407.0K
Price
$0.01702
Token Age
9mo
Top 10 Holders
90.5%

Security Findings

Critical

Initialization Revert Due to Double Ownable Initialization

C-01The `initialize` function calls `__Ownable_init(_delegate)` directly, and then subsequently calls `__OFT_init(_name, _symbol, _delegate)`. The `__OFT_init` function, inherited from `OFTUpgradeable`, internally calls `__Ownable_init` as part of its initialization chain. This results in `__Ownable_init` being called twice within the same `initialize` transaction. OpenZeppelin's `OwnableUpgradeable` prevents re-initialization by checking an internal `_initialized` flag, causing the second call to revert. Consequently, the `initialize` function will always fail, making the contract undeployable and unusable.
IssueThe `initialize` function calls `__Ownable_init(_delegate)` directly, and then subsequently calls `__OFT_init(_name, _symbol, _delegate)`. The `__OFT_init` function, inherited from `OFTUpgradeable`, internally calls `__Ownable_init` as part of its initialization chain. This results in `__Ownable_init` being called twice within the same `initialize` transaction. OpenZeppelin's `OwnableUpgradeable` prevents re-initialization by checking an internal `_initialized` flag, causing the second call to revert. Consequently, the `initialize` function will always fail, making the contract undeployable and unusable.
FixRemove the direct call to `__Ownable_init(_delegate)` from the `initialize` function. The `__OFT_init` function already handles the proper initialization of the `Ownable` component, ensuring the owner is set correctly. This will resolve the double initialization issue and allow the contract to be initialized successfully.
StatusUnresolved
Low

Unnecessary `_totalSupply` Check

L-01The `initialize` function includes a check `if (_totalSupply > type(uint256).max / 2) revert IrysOFT__SupplyTooLarge();`. While this check is often used to prevent multiplication overflows (e.g., if `_totalSupply` were to be doubled), there are no such operations in the current contract that would cause an overflow with `_totalSupply` itself. This check is overly cautious and not strictly necessary for the current logic.
IssueThe `initialize` function includes a check `if (_totalSupply > type(uint256).max / 2) revert IrysOFT__SupplyTooLarge();`. While this check is often used to prevent multiplication overflows (e.g., if `_totalSupply` were to be doubled), there are no such operations in the current contract that would cause an overflow with `_totalSupply` itself. This check is overly cautious and not strictly necessary for the current logic.
FixConsider removing this check unless a specific future operation is planned that would justify its presence. While harmless, it adds minor complexity without a clear current benefit.
StatusUnresolved
Info

Hardcoded String Length Limits

I-01The `initialize` function hardcodes maximum length limits for `_name` (50 characters) and `_symbol` (20 characters). While these limits are reasonable, hardcoding them makes them inflexible. If future requirements necessitate different lengths, the contract would require an upgrade.
IssueThe `initialize` function hardcodes maximum length limits for `_name` (50 characters) and `_symbol` (20 characters). While these limits are reasonable, hardcoding them makes them inflexible. If future requirements necessitate different lengths, the contract would require an upgrade.
FixConsider defining `MAX_NAME_LENGTH` and `MAX_SYMBOL_LENGTH` as `immutable` constants or, if dynamic control is desired, as owner-configurable state variables. This would improve flexibility without compromising security.
StatusUnresolved
Info

Missing Event for `acceptOwnership` Completion

I-02The contract emits `OwnershipTransferStarted` and `OwnershipTransferCanceled` events for the two-step ownership transfer process. However, the `acceptOwnership` function, which finalizes the transfer, does not emit a specific event to signal its completion. While the underlying `_transferOwnership` function (from Ownable) emits `OwnershipTransferred`, a dedicated event for `acceptOwnership` would provide clearer off-chain visibility into the full lifecycle of the two-step transfer.
IssueThe contract emits `OwnershipTransferStarted` and `OwnershipTransferCanceled` events for the two-step ownership transfer process. However, the `acceptOwnership` function, which finalizes the transfer, does not emit a specific event to signal its completion. While the underlying `_transferOwnership` function (from Ownable) emits `OwnershipTransferred`, a dedicated event for `acceptOwnership` would provide clearer off-chain visibility into the full lifecycle of the two-step transfer.
FixConsider adding a specific event, such as `OwnershipAccepted(address indexed newOwner)`, within the `acceptOwnership` function after the `_transferOwnership` call. This would enhance transparency and ease of monitoring for off-chain systems.
StatusUnresolved

Category Ratings

TechnicalLow7/10

The contract is built upon established OpenZeppelin and LayerZero libraries, providing a solid foundation for its architecture (7.1). It incorporates standard security features like pausable functionality and a two-step ownership transfer mechanism (7.2, 7.3). However, a critical flaw in the `initialize` function (C-01) prevents the contract from being deployed and initialized correctly, as it attempts to initialize the `Ownable` component twice, leading to a revert. This fundamental issue significantly impacts the contract's operational readiness.

GovernanceHigh2/10

The contract's economic model (7.4) is straightforward, with a fixed token supply after initialization and no complex rebase or fee mechanisms. Governance (7.5) is robust, with ownership managed by a 2/4 multisig, which enhances security and decentralization for critical operations like pausing and upgrades. The `renounceOwnership` function is appropriately disabled, preventing accidental ownership loss.

UpgradesHigh1/10

The contract implements the UUPS upgradeability pattern (7.7), which is a secure and widely adopted standard. The `_authorizeUpgrade` function is correctly restricted to the `onlyOwner` modifier, ensuring that only the multisig owner can authorize new implementations. This setup provides a secure pathway for future contract enhancements or bug fixes while maintaining strong access control.

Security Checklist

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

Proxy Upgrade Controls

Proxy TypeEip1967 Uups
ImplementationVerified source

Holder Composition

26.1% in wallets64.3% in contracts
Effective Concentration51.9%

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

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 Holder100.0%
Top-3 Unlocked100.0%

Key Addresses

Deployer
0xa6df…9ef1
Unlocked LP Held By
0x2947…d9340x6458…2df5

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

What Raised This Score

  • Ownership NOT renounced — Multisig (2-of-4)
  • Proxy contract (upgradeable — admin can replace logic)
  • Top-10 concentration > 50% (90.5% total → 51.9% effective; 26.1% in EOAs, 64.3% in contracts — heavy)
  • Liquidity not locked, but no owner/deployer address holds LP — market-depth risk, not rug risk
  • LP top1 unlocked holder = 100.0% (independent LP — depth risk, pool = 99% of DEX liquidity)
  • LP top3 unlocked holders = 100.0% (independent LP — depth risk, pool = 99% of DEX liquidity)
  • 1 Critical 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

Starpower Network (STAR)High RiskXRP Token (XRP)High RiskBTR token (BTR)High RiskBNB Attestation (BAS)High RiskCaldera (ERA)High Risk0GHigh Risk

Would You Like a More Detailed Audit of Irys?

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

Get Detailed Audit