Quantum Audit Logo

Is Lido DAO Token Safe?

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

Lido DAO Token LDO
0x13ad…fa60
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
How is this score calculated? → Medium Risk
Executive SummaryAI Copilot

The audit of the StandardArbERC20 implementation contract, used via a ClonableBeaconProxy, revealed a Medium risk issue related to ambiguous string parsing logic in the `BytesParser` library, which could lead to incorrect token metadata. A Low risk issue was identified regarding ERC20 getters that can revert, potentially causing compatibility problems. The contract demonstrates robust access control for critical functions and a secure upgrade pattern.

1 Medium1 Low2 Informational
Volume 24h
$34.2K
Liquidity
$71.9K
Price
$0.4472
Token Age
3y
Top 10 Holders
23.6%

Security Findings

Medium

Ambiguous and Inconsistent String Parsing in `BytesParser.toString`

M-01The `BytesParser.toString` function exhibits complex and inconsistent logic for parsing byte arrays into strings. Specifically, it has three distinct branches based on `input.length`: 1) `input.length == 0` returns `(false, "")`. 2) `input.length == 32` checks for null-termination at `input[31]` and then truncates trailing null bytes. If `input[31]` is not `0x00`, it returns `(false, "")`. This implies a specific, non-standard encoding for 32-byte strings. 3) `else` (any other length) directly uses `abi.decode(input, (string))`. This is problematic because `abi.decode` expects ABI-encoded data, which includes a length prefix for dynamic types like `string`. If `input` contains raw bytes (e.…
IssueThe `BytesParser.toString` function exhibits complex and inconsistent logic for parsing byte arrays into strings. Specifically, it has three distinct branches based on `input.length`: 1) `input.length == 0` returns `(false, "")`. 2) `input.length == 32` checks for null-termination at `input[31]` and then truncates trailing null bytes. If `input[31]` is not `0x00`, it returns `(false, "")`. This implies a specific, non-standard encoding for 32-byte strings. 3) `else` (any other length) directly uses `abi.decode(input, (string))`. This is problematic because `abi.decode` expects ABI-encoded data, which includes a length prefix for dynamic types like `string`. If `input` contains raw bytes (e.…
FixRefactor `BytesParser.toString` to use a single, clear, and standard method for decoding strings from bytes. If raw bytes are expected, implement a robust decoding logic that handles length and padding explicitly, rather than relying on `abi.decode` for non-ABI-encoded data. Ensure consistency across all possible input lengths and document the expected byte encoding format.
StatusUnresolved
Low

ERC20 Getters Can Revert

L-01The `name()`, `symbol()`, and `decimals()` functions in `StandardArbERC20` are designed to revert if the corresponding `ignoreName`, `ignoreSymbol`, or `ignoreDecimals` flags are set during `bridgeInit`. This behavior deviates from the standard ERC20 interface, where these functions are expected to always return a value (even an empty string or zero for decimals if not applicable). Integrations such as wallets, block explorers, or other DeFi protocols often assume these getters will not revert, and their unexpected failure could lead to broken displays, failed transactions, or compatibility issues.
IssueThe `name()`, `symbol()`, and `decimals()` functions in `StandardArbERC20` are designed to revert if the corresponding `ignoreName`, `ignoreSymbol`, or `ignoreDecimals` flags are set during `bridgeInit`. This behavior deviates from the standard ERC20 interface, where these functions are expected to always return a value (even an empty string or zero for decimals if not applicable). Integrations such as wallets, block explorers, or other DeFi protocols often assume these getters will not revert, and their unexpected failure could lead to broken displays, failed transactions, or compatibility issues.
FixConsider modifying these getter functions to return default or empty values (e.g., `""` for name/symbol, `0` for decimals) instead of reverting when the `ignore` flag is set. Alternatively, provide comprehensive documentation for all external integrators, clearly outlining this non-standard behavior and its implications.
StatusUnresolved
Info

Partial Re-initialization Possible for `availableGetters`

I-01The `bridgeInit` function, which serves as the initializer for the proxy, includes a check `require(l2Gateway == address(0), "ALREADY_INIT");` within `L2GatewayToken._initialize` to prevent re-initialization of `l2Gateway` and `l1Address`. However, the `availableGetters` struct in `StandardArbERC20` is set directly within `bridgeInit` *after* this check and is not protected by a similar one-time initialization guard. This means `availableGetters` can be modified if `bridgeInit` is called again by the `l2Gateway` (which is the `msg.sender` during initialization). While this only affects the behavior of the ERC20 getters (name, symbol, decimals) and is not a critical vulnerability, it represe…
IssueThe `bridgeInit` function, which serves as the initializer for the proxy, includes a check `require(l2Gateway == address(0), "ALREADY_INIT");` within `L2GatewayToken._initialize` to prevent re-initialization of `l2Gateway` and `l1Address`. However, the `availableGetters` struct in `StandardArbERC20` is set directly within `bridgeInit` *after* this check and is not protected by a similar one-time initialization guard. This means `availableGetters` can be modified if `bridgeInit` is called again by the `l2Gateway` (which is the `msg.sender` during initialization). While this only affects the behavior of the ERC20 getters (name, symbol, decimals) and is not a critical vulnerability, it represe…
FixImplement a boolean flag (e.g., `_initialized`) in `StandardArbERC20` to ensure that the `availableGetters` struct can only be set once during the initial `bridgeInit` call.
StatusUnresolved
Info

Use of `extcodesize` for `isContract` Check

I-02The `isContract` function in `TransferAndCallToken` uses `extcodesize` to determine if an address belongs to a contract. While a common pattern, `extcodesize` returns zero during a contract's constructor execution, meaning a contract currently being deployed would be incorrectly identified as an EOA. Additionally, it can be manipulated in certain reentrancy scenarios where a contract calls itself during construction. In the context of `transferAndCall`, this check is performed on the `_to` address *after* the token transfer, mitigating most immediate risks.
IssueThe `isContract` function in `TransferAndCallToken` uses `extcodesize` to determine if an address belongs to a contract. While a common pattern, `extcodesize` returns zero during a contract's constructor execution, meaning a contract currently being deployed would be incorrectly identified as an EOA. Additionally, it can be manipulated in certain reentrancy scenarios where a contract calls itself during construction. In the context of `transferAndCall`, this check is performed on the `_to` address *after* the token transfer, mitigating most immediate risks.
FixBe aware of the limitations of `extcodesize`. For the current use case in `transferAndCall`, the risk is low. If more robust contract detection is needed in other contexts, consider alternative methods or ensure the timing of the check avoids constructor-related issues.
StatusUnresolved

Category Ratings

TechnicalLow9/10

The contract's architecture (7.1) is well-structured, utilizing OpenZeppelin upgradeable patterns and a `Cloneable` base for beacon proxy compatibility. Code security (7.2) is generally good, with `BytesLib` assembly code including bounds checks and `transferAndCall` preventing reentrancy on token transfers. Access control (7.3) for `bridgeMint` and `bridgeBurn` is robustly enforced via the `onlyGateway` modifier. However, the `BytesParser.toString` function exhibits ambiguous and inconsistent logic for handling string inputs of different lengths, particularly for 32-byte inputs, which could lead to incorrect token metadata. Additionally, ERC20 getters (name, symbol, decimals) can revert (7.2), deviating from standard behavior and potentially impacting integrations.

GovernanceHigh3/10

The contract functions as an L2 ERC20 token, with its economic model (7.4) being a standard fungible token. Minting and burning are controlled by a designated `l2Gateway` address, ensuring centralized but controlled supply management. There are no direct governance mechanisms (7.5) within this contract, as it primarily serves as a token representation. External interactions (7.6) are limited to the L2 gateway and potential `ITransferAndCallReceiver` contracts, with `transferAndCall` designed to prevent reentrancy on the token itself.

UpgradesHigh1/10

The contract is designed as an implementation for a `ClonableBeaconProxy`, indicating a robust upgradeability pattern (7.7). The `Cloneable` base contract correctly distinguishes between the master copy and clones, preventing the master from being self-destructed. The `_initialize` function ensures that critical state variables are set only once, preventing re-initialization attacks. Standard OpenZeppelin upgradeable contracts are utilized, which generally handle storage slot management effectively.

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

6.5% in wallets17.1% in contracts
Effective Concentration13.4%

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

Key Addresses

Deployer
0x3fe3…000f

What Raised This Score

  • Ownership status UNKNOWN (owner could not be resolved)
  • Proxy contract (upgradeable — admin can replace logic)
  • Complex proxy pattern (BEACON)
  • Liquidity NOT locked (owner can withdraw — rug-pull risk)
  • 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

PearMedium RiskPendleMedium RiskArbitrum (ARB)Medium RiskLayerZero (ZRO)Medium RiskChainLink Token (LINK)Medium RiskPepeMedium Risk

Would You Like a More Detailed Audit of Lido DAO Token?

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

Get Detailed Audit