Mey Network Info
Mey Network is an integrated blockchain ecosystem designed to bridge the gap between physical assets and the digital world. By combining the power of Meychain—a dedicated Layer 1 blockchain for Real-World Assets (RWAs)—and MeyFi, our decentralized nance platform, Mey Network enables seamless tokenization, trading, and management of assets in a secure, scalable environment.
TrustNet Score
The TrustNet Score evaluates crypto projects based on audit results, security, KYC verification, and social media presence. This score offers a quick, transparent view of a project's credibility, helping users make informed decisions in the Web3 space.
Real-Time Threat Detection
Real-time threat detection, powered by Cyvers.io,
is currently not
activated
for this project.
This advanced feature provides continuous monitoring and instant alerts to safeguard your assets from potential security threats. Real-time detection enhances your project's security by proactively identifying and mitigating risks.
For more information, click here.
Security Assessments
Summary and Final Words
No crucial issues found
The contract does not contain issues of high or medium criticality. This means that no known vulnerabilities were found in the source code.
Ownership is not renounced
The owner retains significant control, which could potentially be used to modify key contract parameters.
Contract is upgradeable
The contract uses a proxy pattern or similar mechanism, enabling future upgrades. This can introduce risks if the upgrade mechanism is not securely managed.
Scope of Work
This audit encompasses the evaluation of the files listed below, each verified with a SHA-1 Hash. The team referenced above has provided the necessary files for assessment.
The auditing process consists of the following systematic steps:
- Specification Review: Analyze the provided specifications, source code, and instructions to fully understand the smart contract's size, scope, and functionality.
- Manual Code Examination: Conduct a thorough line-by-line review of the source code to identify potential vulnerabilities and areas for improvement.
- Specification Alignment: Ensure that the code accurately implements the provided specifications and intended functionalities.
- Test Coverage Assessment: Evaluate the extent and effectiveness of test cases in covering the codebase, identifying any gaps in testing.
- Symbolic Execution: Analyze the smart contract to determine how various inputs affect execution paths, identifying potential edge cases and vulnerabilities.
- Best Practices Evaluation: Assess the smart contracts against established industry and academic best practices to enhance efficiency, maintainability, and security.
- Actionable Recommendations: Provide detailed, specific, and actionable steps to secure and optimize the smart contracts.
A file with a different Hash has been intentionally or otherwise modified after the security review. A different Hash may indicate a changed condition or potential vulnerability that was not within the scope of this review.
Final Words
The following provides a concise summary of the audit report, accompanied by insightful comments from the auditor. This overview captures the key findings and observations, offering valuable context and clarity.
Smart Contract Analysis Statement
Contract Analysis
This statement covers the two Mey Network token vesting contracts, TokenVestingUpgradeable and TokenVestingV2Upgradeable. Both distribute a configured ERC20 token to a whitelisted set of beneficiaries under a release schedule made of an initial percentage unlock, a cliff period, and linear unlocking across a fixed number of periods, together with a global pause window that can freeze the vesting clock. The first contract uses a single schedule and one allocation record per beneficiary, while the second lets a beneficiary hold several independent vesting entries, each with its own start date and allocation, sharing one global set of schedule parameters. Both are upgradeable contracts built on the proxy pattern and pay out the token as beneficiaries claim over time.
Ownership Privileges
The ownership of both contracts has been assigned to a single owner account through the standard single-step model, with an additional manager role that shares several of the day-to-day powers. The owner retains full privileges including:
- Setting and changing beneficiary allocations or vesting entries, and adding or removing beneficiaries at any time.
- Changing the core schedule parameters - first release, cliff, period count and period length, and in the first contract the global start time - even after vesting has started.
- Opening and closing a global pause window that freezes the vesting clock, including leaving it open-ended.
- Upgrading the implementation behind the proxy and withdrawing tokens, including the vesting token, from the contract.
- The contracts cannot mint, they have no transfer fees and no blacklist, and they do not act as tokens themselves.
- The owner cannot retroactively reduce a beneficiary's already-claimed amount within a surviving record, although removal or re-migration paths can reset that history.
- There is no on-chain timelock and no enforced multisig on the privileged functions at the code level.
- Ownership can be transferred or renounced, and renouncing would permanently disable all management functions.
Security Features
The contracts implement several positive security features:
- Claiming follows the checks-effects-interactions order and is protected by a reentrancy guard, with safe ERC20 transfer helpers.
- The implementations disable initializers in their constructors, and the second contract uses descriptive custom errors and a checked low-level call for native withdrawals.
- The claim math is bounded so a beneficiary cannot withdraw more than their allocation under normal operation, and the pause arithmetic was verified to be free of underflow.
- A storage gap is reserved in both contracts to support safe future upgrades.
Note - This Audit report consists of a security analysis of the TokenVestingUpgradeable and TokenVestingV2Upgradeable smart contracts, which implement token vesting schedules with a cliff and linear release. This analysis did not include economic analysis of the contracts' tokenomics. Moreover, we only audited the main contracts for the Mey Network team. Other contracts associated with the project were not audited by our team. We recommend investors do their own research before investing.
Files and details
Findings and Audit result
medium Issues | 7 findings
Pending
#1 medium Issue
Owner can withdraw the vesting token and strand beneficiaries
The rescue function transfers the entire balance of any token to the owner, including the vesting token itself. Beneficiaries are paid from the tokens held by the contract, so a single owner call can move out everything that is owed to them, including amounts that are already vested but not yet claimed. There is no carve-out that protects the outstanding allocations and no delay before the transfer takes effect, so the value users expect to receive depends entirely on the owner choosing not to call this function.
Resolved
#2 medium Issue
Removing then re-adding a user resets claim history and allows double claiming
removeWhitelistUser deletes the entire user record, which zeroes the tokensClaimed counter. If the same address is later added again through addWhitelistUser or the batch function, it starts from a clean slate and can claim its full allocation a second time, draining tokens that were meant for other beneficiaries. This contradicts the contract's own design intent, since the update functions contain explicit comments warning that tokensClaimed must never be reset to prevent exactly this double payment, yet the removal path performs that reset.
Pending
#3 medium Issue
Vesting schedule and pause are mutable at any time and apply retroactively
The owner can change firstRelease, startTime, cliff, totalPeriods, timePerPeriod and the vesting token at any moment, and a manager or the owner can open a pause window. Because claimable amounts are recomputed from the current parameters on every claim rather than from values fixed at allocation time, any change retroactively reshapes everyone's unlock curve. The pause can also be left open-ended, freezing all claims indefinitely until an end is set, so privileged accounts can delay or reshape distributions that beneficiaries were relying on.
Pending
#4 medium Issue
Migration function can lower the claimed baseline and reopen claiming
migrateUsers writes tokensClaimed directly from caller-supplied input and has no guard preventing it from being called more than once, even though it is documented as a one-time migration. An owner can call it again at any time with a lower claimedAmounts value for a beneficiary, which lowers their recorded claimed baseline and lets them claim tokens they have already received. This is a separate path from the removal-and-re-add issue and reaches the same double-claim outcome through the migration entry point.
Pending
#5 medium Issue
Owner can withdraw the vesting token and strand beneficiaries
The ERC20 withdrawal function lets the owner move any amount of any token out of the contract, including the vesting token that funds beneficiary claims. There is no carve-out protecting the outstanding allocations and no delay before the transfer takes effect, so a single owner call can remove tokens that users have already vested but not yet claimed, leaving claims to revert for lack of balance. The value beneficiaries expect therefore depends entirely on the owner choosing not to withdraw it.
Pending
#6 medium Issue
Removing then re-adding an entry resets claim history and allows double claiming
Removing an entry clears the uniqueness flag for its sign date and deletes the entry along with its claimed counter. The same user and sign date can then be added again, producing a fresh entry with claimed reset to zero, which lets the beneficiary claim the full allocation a second time and drains tokens meant for others. Because the per-user list and its claimed counters are the only record of past claims, any removal followed by re-creation discards the history that prevents double payment.
Pending
#7 medium Issue
Vesting parameters and pause are mutable at any time and apply retroactively
A manager or the owner can change the global first release, cliff, period count and period length at any time, and can open a pause window. Claimable amounts are recomputed from the current parameters on every claim rather than from values fixed when an entry was created, so any change retroactively reshapes the unlock curve for all entries at once. The pause can also be left open-ended, freezing claims indefinitely until an end is set, so privileged accounts can delay or reshape distributions that beneficiaries were relying on.
low Issues | 7 findings
Pending
#1 low Issue
Missing input validation in migration and configuration setters
Several state-changing functions accept values without sanity checks. migrateUsers writes tokensClaimed directly from the supplied claimedAmounts array without verifying that the claimed value does not exceed the allocation. If a migration row sets tokensClaimed above amount, that user can never claim because claimTokens requires amount to be greater than tokensClaimed, so the allocation is silently locked. In addition, startTime is never validated in initialize or updateStartTime, so a value of zero or a timestamp in the distant past makes the elapsed-time calculation treat the whole schedule as already passed.
Pending
#2 low Issue
Non-standard ERC20 tokens break claim accounting
The contract computes a claim amount and then transfers exactly that figure, assuming the token moves the full value. A fee-on-transfer token would deliver less than recorded, so later claimers eventually find the contract underfunded, while a rebasing token would desynchronise balances from the recorded allocations. Because updateToken lets the owner change the token address at any time, even a contract initially configured with a clean token can later be pointed at an asset with different transfer behaviour.
Pending
#3 low Issue
Single-step ownership and no timelock on privileged actions
Ownership uses the single-step model, so transferring control to a wrong address cannot be undone and a renounce would permanently disable management. There is no timelock between deciding a change and applying it, which applies both to the parameter setters and to the upgrade authorization. Combined with the broad powers the owner holds, the absence of delay and of a multisig requirement means a single key can immediately and silently reshape the vesting terms or replace the implementation.
Pending
#4 low Issue
Migration does not check claimed against allocation and breaks a view
migrateVestingEntries writes the claimed field directly from the supplied array without checking that it does not exceed the entry amount. When a migrated entry has claimed greater than amount, the summary view that reports a user's totals computes the locked amount as total allocated minus total claimed minus total claimable, which underflows and reverts. From that point the aggregate view reverts for that account, breaking front-ends and integrations that read it, even though the underlying claim logic itself still behaves safely.
Pending
#5 low Issue
Per-user entry list is unbounded and can make claiming run out of gas
Claiming iterates over every vesting entry a user holds, and managers can keep adding entries for the same address with no upper limit. A beneficiary who accumulates a very large number of entries could reach a point where iterating the whole list exceeds the block gas limit, which would make claiming and the aggregate read view revert and effectively lock that user's tokens. The same unbounded iteration applies to the summary view and the bulk removal function.
Pending
#6 low Issue
Non-standard ERC20 tokens break claim accounting
Claiming computes an amount and transfers exactly that figure, assuming the token delivers the full value. A fee-on-transfer token would deliver less than recorded and eventually leave the contract unable to satisfy later claims, while a rebasing token would desynchronise balances from the recorded entries. Because updateToken lets the owner change the token at any time, even a contract configured with a clean token can later be pointed at one with different transfer behaviour.
Pending
#7 low Issue
Single-step ownership and no timelock on privileged actions
Ownership uses the single-step model, so transferring control to a wrong address cannot be undone and a renounce would permanently disable management, including the ability to add or remove managers. There is no timelock between deciding a change and applying it for the parameter setters or the upgrade authorization. Combined with the broad powers held by the owner and the managers, the absence of delay and of a multisig requirement means a single key can immediately reshape the vesting terms or replace the implementation.
optimization Issues | 3 findings
Pending
#1 optimization Issue
Global accounting variable written on every loop iteration
The batch whitelist and migration functions update the global totalTokenNeed storage variable on each iteration of the loop. Storage writes are among the most expensive operations, so repeating them per element makes the gas cost grow unnecessarily with the batch size.
Pending
#2 optimization Issue
Revert strings and magic numbers increase gas and reduce clarity
The contract relies on revert strings rather than custom errors, repeats the literal 10000 for the basis-points denominator, reads array length on every loop iteration and increments loop counters with checked arithmetic. Each of these is a small but avoidable gas cost, and the repeated literal also reduces readability compared with a single named constant.
Pending
#3 optimization Issue
Loop bounds and magic numbers can be optimised
Several loops read the array length on each iteration and increment counters with checked arithmetic, and the basis-points denominator is repeated as the literal 10000. These are small but avoidable gas costs in the frequently used claim and view paths, and the repeated literal reduces readability compared with a single named constant.
informational Issues | 12 findings
Pending
#1 informational Issue
Floating pragma in interface while implementation is pinned
The shared types interface declares a floating compiler version while the main contract is pinned to a single exact version. Mixing a floating and a pinned pragma in the same codebase is inconsistent and could allow part of the project to be compiled with an unintended compiler release.
Pending
#2 informational Issue
First period of linear vesting unlocks at the cliff boundary
In the linear phase the number of elapsed periods is computed as the floor of the time after the cliff divided by the period length, plus one. This means a full period worth of the linear allocation becomes claimable at the exact moment the cliff ends, and the schedule reaches full unlock one period earlier than the nominal total duration after the cliff. This is a deliberate-looking design choice but it is not documented, and it changes when funds become available.
Pending
#3 informational Issue
Initialization and several setters lack NatSpec and configuration events
The initializer sets the token, first release, start time, cliff and period parameters without emitting any event, so the initial configuration cannot be reconstructed purely from logs. Several external functions also lack NatSpec documentation, which makes the intended behaviour and parameter meaning less clear to integrators and reviewers.
Pending
#4 informational Issue
Confirm proxy storage layout before any upgrade or migration
The contract is upgradeable and reserves a storage gap for future variables, which is good practice. Because the project also ships a second vesting contract with a different storage layout, it is important to confirm that this implementation is only ever used on a proxy whose storage matches its own layout. Initializing it over storage written by an incompatible implementation would corrupt the state.
Pending
#5 informational Issue
Batch whitelist update emits no event
addWhitelistMulti updates allocations for many users and adjusts the global accounting variable, but it does not emit any event. Every other allocation-changing function in the contract emits a dedicated event, so this path is a silent state change. Indexers, dashboards and monitoring that rely on events will miss allocations created or modified through the batch function, which makes it harder to audit who was granted what and complicates incident response.
Pending
#6 informational Issue
Native withdrawal uses transfer and lacks recipient check
The native-asset withdrawal function sends funds with the transfer primitive, which forwards only a fixed gas stipend and reverts if the recipient is a contract that needs more gas to accept funds, for example a multisig. The recipient address is also not checked against the zero address. The contract has no payable receive or fallback function, so it cannot normally hold a native balance, which makes this path effectively dead code today, but if it is kept it should follow the safer call pattern.
Pending
#7 informational Issue
Allocation setters do not reject the zero address
The single and batch allocation setters accept any address, including the zero address, while only the migration path rejects it. An allocation assigned to the zero address can never be claimed because that address cannot send a transaction, so it only pollutes the user list and the total accounting. The impact is limited to bookkeeping rather than fund loss.
Pending
#8 informational Issue
Floating pragma in interface while implementation is pinned
The types interface declares a floating compiler version while the main contract is pinned to a single exact version. Mixing a floating and a pinned pragma in the same codebase is inconsistent and could allow part of the project to be compiled with an unintended compiler release.
Pending
#9 informational Issue
First period of linear vesting unlocks at the cliff boundary
In the linear phase the number of elapsed periods is computed as the floor of the time after the cliff divided by the period length, plus one. A full period worth of the linear allocation therefore becomes claimable at the exact moment the cliff ends, and the schedule reaches full unlock one period earlier than the nominal total duration after the cliff. This looks intentional but is not documented and affects when funds become available.
Pending
#10 informational Issue
Native withdrawal path is effectively unreachable
The contract exposes a function to withdraw native funds but provides no payable receive or fallback, so under normal operation it cannot accumulate a native balance and the function has nothing to withdraw. The implementation itself is sound, using a checked low-level call, but the path is effectively dead unless funds are force-sent.
Pending
#11 informational Issue
Confirm proxy storage layout before any upgrade or migration
The contract is upgradeable and reserves a storage gap, which is good practice. Its storage layout differs from the first vesting contract in this project, so it must only ever run on a proxy whose storage matches its own layout. Despite the version in its name, it is a distinct contract rather than a drop-in upgrade of the first one, and initializing it over incompatible storage would corrupt the state.
Pending
#12 informational Issue
Vesting entries can be created with a zero amount
The entry creation functions validate the address, the sign date and uniqueness, but they do not reject a zero amount. A zero-amount entry contributes nothing to a beneficiary yet still occupies a position in the entry list, so it lengthens the loop that claiming and the summary view must traverse and produces a creation event that suggests an allocation was made when none was.