The audit of the Rain Token (implementation Rain) reveals significant architectural and design concerns. A critical initialization flaw means the contract's core functionalities (ERC20, ownership) are not explicitly initialized within the provided source code, relying on opaque external calls. Furthermore, the contract includes extensive unused variables and errors suggesting a USD-pegged inflation mechanism, which is entirely absent from the actual dailyMinting logic, leading to a misleading economic model. Other findings include hardcoded initial values, an unused initial supply constant, and explicitly disabled UUPS upgrades.
On-chain security analysis — is it a scam or legit?
Is RAIN Safe?
Not verified
This 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 →The audit of the Rain Token (implementation Rain) reveals significant architectural and design concerns.
- Ownership not renounced — Multisig
- Proxy contract
- Top-10 concentration > 30%
- Liquidity not locked, but no owner/deployer address holds LP — market-depth risk, not rug risk
- LP top1 unlocked holder = 100.0%
- LP top3 unlocked holders = 100.0%
- 2 Critical finding(s) from audit
- 1 Medium finding(s) from audit
- 2 Low finding(s) from audit
Exact factors
- Ownership NOT renounced — Multisig (2-of-4)
- Proxy contract (upgradeable — admin can replace logic)
- Top-10 concentration > 30% (86.2% total → 36.1% effective; 2.7% in EOAs, 83.6% in contracts — moderate)
- 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 = 59% of DEX liquidity)
- LP top3 unlocked holders = 100.0% (independent LP — depth risk, pool = 59% of DEX liquidity)
- 2 Critical 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
Security Checklist
as of audit · Sep 4Holders
as of audit · Sep 4, 2026- Contractstreasury, vesting, bridge, staking83.6%
- Walletsanonymous holders, free to sell2.7%
- Everyone elsethe rest of the supply13.8%
How holder types affect the score
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.
Holder balances as recorded by the audit of Sep 4, 2026
Liquidity
as of audit · Sep 4The risk score reads depth across every pair.
The LP tokens of the analysed pool are not locked or burned, so whoever holds them can withdraw that liquidity.
0x7706…53aa0x0963…3df6No privileged address appears among these holders: the unlocked liquidity sits with independent providers, not with the deployer.
Security Findings
CriticalArchitectural Flaw: Missing Chained Initialization Function
The Rain contract, despite inheriting from multiple OpenZeppelin upgradeable contracts (ERC20Upgradeable, Ownable2StepUpgradeable, etc.), lacks a primary initialize() function that explicitly calls the __<ContractName>_init() functions of its base contracts. While external deployment scripts might have called these _init functions directly on the proxy, the absence of this chained initialization in the contract's source code is an architectural flaw. It makes the contract's initialization process opaque, harder to audit, and prone to errors if not handled perfectly by external tooling. The initialize2() function, marked as a reinitializer(2), further complicates this, as it impl…C-01
Issue
The Rain contract, despite inheriting from multiple OpenZeppelin upgradeable contracts (ERC20Upgradeable, Ownable2StepUpgradeable, etc.), lacks a primary initialize() function that explicitly calls the __<ContractName>_init() functions of its base contracts. While external deployment scripts might have called these _init functions directly on the proxy, the absence of this chained initialization in the contract's source code is an architectural flaw. It makes the contract's initialization process opaque, harder to audit, and prone to errors if not handled perfectly by external tooling. The initialize2() function, marked as a reinitializer(2), further complicates this, as it impl…
Fix
Implement a comprehensive initialize() function in the Rain contract that explicitly calls all necessary __<ContractName>_init() functions from its inherited OpenZeppelin base contracts. Ensure proper chaining and versioning of initializers, and consider moving initialize2's logic into the primary initialize if it's intended for initial setup.
Status
Unresolved
CriticalMisleading/Unused Inflation Mechanism
The contract defines several state variables (DAILY_INFLATION_USD, oraclePriceFeed, deploymentTimestamp, LOCKUP_PERIOD) and custom errors (MintingNotAllowedYet, InvalidPrice, OraclePriceFeedNotSet) that strongly suggest a USD-pegged, time-locked inflation mechanism. However, the dailyMinting function, which is the sole minting mechanism, does not utilize any of this logic. Instead, it mints a fixed percentage (10%) of dailyBurned tokens. This significant discrepancy between declared intent/variables and actual implementation is highly misleading, indicates incomplete development or a major design change, and could lead to incorrect assumptions about the token's economic mo…C-02
Issue
The contract defines several state variables (DAILY_INFLATION_USD, oraclePriceFeed, deploymentTimestamp, LOCKUP_PERIOD) and custom errors (MintingNotAllowedYet, InvalidPrice, OraclePriceFeedNotSet) that strongly suggest a USD-pegged, time-locked inflation mechanism. However, the dailyMinting function, which is the sole minting mechanism, does not utilize any of this logic. Instead, it mints a fixed percentage (10%) of dailyBurned tokens. This significant discrepancy between declared intent/variables and actual implementation is highly misleading, indicates incomplete development or a major design change, and could lead to incorrect assumptions about the token's economic mo…
Fix
Either fully implement the USD-pegged inflation mechanism using the declared variables and oracle, or remove all unused variables, errors, and comments related to it. Ensure the contract's code accurately reflects its intended economic model to avoid confusion and potential misinterpretations by users or integrators.
Status
Unresolved
MediumHardcoded Initial Burned Values in initialize2
The initialize2 function directly assigns large, specific values to lifetimeBurned and dailyBurned. While this might be intended for a specific initial state or migration, hardcoding such values in an initialization function reduces flexibility and makes the contract brittle. If the contract were to be redeployed or initialized under different circumstances, these hardcoded values might be inappropriate, leading to unexpected or incorrect minting behavior. (7.2 Code Security, 7.4 Economic, 7.8 Operations)M-01
initialize2Issue
The initialize2 function directly assigns large, specific values to lifetimeBurned and dailyBurned. While this might be intended for a specific initial state or migration, hardcoding such values in an initialization function reduces flexibility and makes the contract brittle. If the contract were to be redeployed or initialized under different circumstances, these hardcoded values might be inappropriate, leading to unexpected or incorrect minting behavior. (7.2 Code Security, 7.4 Economic, 7.8 Operations)
Fix
Consider making the initial values for lifetimeBurned and dailyBurned configurable parameters during initialization, or derive them dynamically based on the contract's actual state or a trusted external source. This improves flexibility and reduces the risk of incorrect initial states.
Status
Unresolved
LowUnused INITIAL_SUPPLY Constant
The contract defines INITIAL_SUPPLY as a public constant with a value of 1_150_000_000_000 * WEI. However, there is no corresponding _mint call in any initialization function to actually create and distribute this initial supply. Without an explicit minting operation, the token will have a total supply of zero upon deployment (assuming the critical initialization flaw is fixed), rendering the INITIAL_SUPPLY constant effectively unused and misleading. (7.1 Architecture, 7.4 Economic)L-01
INITIAL_SUPPLY ConstantIssue
The contract defines INITIAL_SUPPLY as a public constant with a value of 1_150_000_000_000 * WEI. However, there is no corresponding _mint call in any initialization function to actually create and distribute this initial supply. Without an explicit minting operation, the token will have a total supply of zero upon deployment (assuming the critical initialization flaw is fixed), rendering the INITIAL_SUPPLY constant effectively unused and misleading. (7.1 Architecture, 7.4 Economic)
Fix
If an initial supply is intended, ensure that the initialize() function (or an appropriate setup function) includes a call to _mint(recipient, INITIAL_SUPPLY) to create and distribute the tokens. Otherwise, remove the INITIAL_SUPPLY constant if it is not meant to be used.
Status
Unresolved
LowUUPS Upgrades Explicitly Disabled
The _authorizeUpgrade function, which is part of the UUPSUpgradeable pattern, is explicitly overridden to revert("UUPS upgrades disabled"). While this clearly signals immutability and prevents unauthorized upgrades, it contradicts the use of the UUPSUpgradeable base contract, which is designed for upgradeability. If future protocol changes require contract logic updates, a full redeployment of the token would be necessary, incurring significant operational overhead and potentially disrupting integrations. (7.7 Upgrades, 7.8 Operations)L-02
Issue
The _authorizeUpgrade function, which is part of the UUPSUpgradeable pattern, is explicitly overridden to revert("UUPS upgrades disabled"). While this clearly signals immutability and prevents unauthorized upgrades, it contradicts the use of the UUPSUpgradeable base contract, which is designed for upgradeability. If future protocol changes require contract logic updates, a full redeployment of the token would be necessary, incurring significant operational overhead and potentially disrupting integrations. (7.7 Upgrades, 7.8 Operations)
Fix
Confirm that the decision to disable upgrades is intentional and understood by all stakeholders. If future upgradeability is a possibility, reconsider this override. If immutability is the goal, ensure this is clearly documented and communicated to users and integrators.
Status
Unresolved
InfoUnused PriceFeed Interface and Related Logic
The PriceFeed interface is imported, and an oraclePriceFeed state variable is declared, along with a PriceFeedSet event. However, the oraclePriceFeed variable is never set (no setPriceFeed function) and its getPrice() method is never called within the contract's logic. This indicates dead code or an incomplete feature related to an external price oracle. (7.1 Architecture, 7.2 Code Security)I-01
PriceFeed Interface and Related LogicIssue
The PriceFeed interface is imported, and an oraclePriceFeed state variable is declared, along with a PriceFeedSet event. However, the oraclePriceFeed variable is never set (no setPriceFeed function) and its getPrice() method is never called within the contract's logic. This indicates dead code or an incomplete feature related to an external price oracle. (7.1 Architecture, 7.2 Code Security)
Fix
Remove the unused PriceFeed interface, oraclePriceFeed variable, and related event/errors if the feature is not intended for implementation. If it is a planned feature, ensure it is fully implemented, including a function to set the oracle address and integrate its price data into relevant logic.
Status
Unresolved
Infoconstructor calls _disableInitializers() but initialize2 is reinitializer(2)
The constructor correctly calls _disableInitializers() for upgradeable contracts. However, the initialize2 function is marked as reinitializer(2). This implies that an initialize function (version 1) should have been called first. Given the critical initialization flaw (C-01) where the primary initialize is missing, the versioning for initialize2 is either incorrectly applied or part of the larger initialization issue. (7.1 Architecture, 7.2 Code Security)I-02
constructor calls _disableInitializers() but initialize2 is reinitializer(2)Issue
The constructor correctly calls _disableInitializers() for upgradeable contracts. However, the initialize2 function is marked as reinitializer(2). This implies that an initialize function (version 1) should have been called first. Given the critical initialization flaw (C-01) where the primary initialize is missing, the versioning for initialize2 is either incorrectly applied or part of the larger initialization issue. (7.1 Architecture, 7.2 Code Security)
Fix
Ensure that the initialization versioning is correctly implemented. If initialize2 is intended as the first or only initialization, adjust its modifier accordingly (e.g., initializer). If it is truly a reinitializer, ensure that initialize (version 1) is properly defined and called first.
Status
Unresolved
Analysis
AI Copilotas of audit · Sep 4The technical architecture leverages OpenZeppelin's upgradeable contracts, including ERC20, Ownable2Step, and UUPS. However, a critical architectural flaw exists where the contract lacks a primary initialize() function to properly chain the initialization of these base contracts (7.1 Architecture, 7.2 Code Security). This makes the contract's operational state dependent on external, non-auditable setup. The dailyMinting function is properly access-controlled by onlyOwner and includes a MINT_COOLDOWN_PERIOD (7.3 Access Control). UUPS upgrades are explicitly disabled, providing immutability but limiting future flexibility (7.7 Upgrades).
The economic model for token minting is based on a percentage of burned tokens, controlled by the owner (7.4 Economic). However, the contract contains extensive declarations for a USD-pegged inflation mechanism with an oracle and lockup periods, which are entirely unused in the dailyMinting logic. This discrepancy is highly misleading and creates ambiguity regarding the token's intended economic behavior (7.4 Economic). Initial burned values are hardcoded in initialize2, which could lead to unexpected behavior if not carefully managed (7.8 Operations). The owner is a 2/4 multisig, providing a degree of decentralized control over critical functions like dailyMinting and updateTreasury (7.5 Governance).
The contract utilizes the UUPSUpgradeable pattern but explicitly disables upgrades by reverting in _authorizeUpgrade (7.7 Upgrades). This design choice ensures immutability of the contract logic, preventing any future on-chain upgrades. While this provides a clear and immutable state, it means any future protocol changes requiring contract modifications would necessitate a full redeployment, incurring significant operational overhead and potential disruption to integrations (7.8 Operations). The initialize2 function is a reinitializer(2), implying a versioning strategy that is not fully supported by the contract's missing primary initialize function.
Address the critical initialization flaw by implementing a comprehensive initialize() function that properly chains all inherited OpenZeppelin _init() functions.
Clarify and align the contract's economic model by either implementing the USD-pegged inflation logic or removing the unused variables and comments to prevent misleading expectations. Review the hardcoded initial burned values and consider making them configurable or derived dynamically for better flexibility and robustness.
Contract & Record
Free for any token on the record. It links to this page and always shows the current score and the date it was checked.
- Sep 4, 202643Medium
- Jul 27, 202640Medium
- Jul 27, 202666High
Score changes are recorded as dated snapshots — history is append-only and cannot be edited.
Frequently Asked Questions
Is RAIN a scam?
Based on automated analysis, RAIN scores 66/100 (High Risk) on our risk scale. No honeypot was detected, but always verify independently before investing.
Is RAIN safe to buy?
Our scanner flagged a risk score of 66/100. Ownership has not been renounced, which is a risk factor. DYOR before purchasing any token.
Has RAIN been audited?
The contract has not been verified on-chain. Verification is not the same as a full security audit. Use Quantum Audit's free tool to run a deeper analysis of the contract code.
Would You Like a More Detailed Audit of RAIN?
Our AI-powered scanner gives you a deeper, real-time smart contract analysis — free, with every scoring factor shown.
Get Detailed Audit Is this your token? Publish your own audit on this page