Skip to content
$1,000,000 in security audit grants are live now, Apply here →

Security review · July 2025

Push Chain Migration

for Push Chain

Guardian's review of Push Chain Migration for Push Chain, published July 2025. The report records 19 findings across 2 review rounds, including 12 low and 7 informational.

Published
Review window
June 20 to 30, 2025
Rounds
Main Review, Remediation Review
Language
Solidity, JavaScript
Chains
Ethereum
Sector
Infrastructure
  • 0 Critical
  • 0 High
  • 0 Medium
  • 12 Low
  • 7 Informational

12 resolved · 7 acknowledged

Scope

2 files in scope · 171 nSLOC
FilenSLOCLines
src/MigrationRelease.sol117181
src/MigrationLocker.sol5493

Findings 19

Main Review

17 findings · June 20 to 23, 2025
  1. L-01 Low Redundant Boolean Check Superfluous Code Resolved
    Location
    src/MigrationRelease.sol#L124C9-L124C41
    Round
    Main Review

    Description

    The releaseVested function verifies if user already claimed by checking claimedvested[leaf] = true. As claimedvested is a boolean, there is no need to compare it to the true param.

    Recommendation

    Consider removing the comparison strict equality operator: if (claimedvested[leaf] )

  2. L-02 Low Unused State Variable Superfluous Code Resolved
    Location
    src/MigrationRelease.sol:30
    Round
    Main Review

    Description

    The isClaimPaused state variable is defined in MigrationRelease.sol but it is never used. Additionally, the whenNotPaused modifier already handles this feature.

    Recommendation

    Remove isClaimPaused state variable.

  3. L-03 Low Redundant Leaf Derivation Gas Optimization Resolved
    Round
    Main Review

    Description

    bytes32 leaf = keccak256(abi.encodePacked(_recipient, _amount, _epoch));require(verifyAddress(_recipient, _amount, _epoch, _merkleProof) & instantClaimTime[leaf] = 0,"Not Whitelisted or already Claimed");
    

    The leaf is locally constructed using _recipient, _amount, and _epoch, then passed to verifyAddress, which re-computes the same hash again. This is redundant and introduces unnecessary computation.

    Recommendation

    Consider computing the leaf once and passing it directly to the Merkle verification.

  4. L-04 Low Unnecessary Return Keyword Superfluous Code Resolved
    Location
    src/MigrationRelease.sol:166
    Round
    Main Review

    Description

    The explicit return statement at the end of this conditional block is unnecessary, as the function execution will naturally return at this point.

    if (_token = address(0)) {transferFunds(_to, _amount);return;}
    

    Recommendation

    Consider removing the explicit return;

  5. L-05 Low Flawed Contract Detection Logic May Limit UX DoS Acknowledged
    Round
    Main Review

    Description

    The Push team intends to prevent smart contracts from being recipient by checking the code size using the following logic:

    uint256 codeLength;assembly {codeLength := extcodesize(_recipient) // @audit What is this codeLength check supposed to prevent?}
    

    This check would block legitimate smart contract wallets (e.g., Gnosis Safe, ERC-4337 wallets) from interacting, which could limit usability.

    Recommendation

    Consider removing the extcodesize check.

  6. L-06 Low Users May Lock Tokens In Wrong Contract Validation Acknowledged
    Location
    src/MigrationLocker.sol:72
    Round
    Main Review

    Description

    The MigrationLocker is an upgradeable contract. Although some safety checks where enforced to avoid users initializing the implementation, nothing prevents a users from locking PUSH tokens in this contract by mistake. This is possible as the PUSH token is a constant and the contract is unpaused by default.

    Recommendation

    Consider calling _pause() in the constructor so future calls to the lock function in the implementation contract reverts.

  7. L-07 Low Missing SafeERC20 Implementation Best Practices Resolved
    Location
    src/MigrationLocker.sol
    Round
    Main Review

    Description

    The MigrationRelease uses SafeERC20 in the transfer function for recoverFunds. However, the MigrationLocker also contains a recoverFunds function but the SafeERC20 lib is not used.

    Recommendation

    Consider, using SafeERC20 library in the MigrationLocker contract.

  8. L-08 Low Missing New Epoch Event Events Resolved
    Location
    src/MigrationLocker.sol:52
    Round
    Main Review

    Description

    Owner will call initiateNewEpoch to start a new epoch for locking PUSH tokens. This is an important state change in the contract that should emit an event to notify off chain services.

    Recommendation

    Consider emitting an epoch change event in the initiateNewEpoch function.

  9. L-09 Low Locks May Not Be Included In Merkle Root Warning Acknowledged
    Location
    script/utils/fetchAndStoreEvents.js:39
    Round
    Main Review

    Description

    The fetchAndStoreEvents will fetch all Locked events between two blocks. This script is meant to be used once per epoch, at the end of each epoch.

    If the locker contract is not paused before calling initiateNewEpoch, the following edge case might appear:

    • epoch = 1, started at block 0
    • Alice submits lock tx at block 100, Locked event uses epoch 1
    • owner also submits initiateNewEpoch at block 100, epoch = 2
    • team runs fetchAndStoreEvents filtering epoch 1 events.
    • startBlock = epochStartBlock(1) = 0
    • endBlock = epochStartBlock(1 + 1) - 1= 100 - 1 = 99
    • locker.queryFilter will not include Alice's lock event.

    Recommendation

    Consider checking blocks up to and including the next block epoch's start block in the fetchAndStoreEvents script.

  10. L-10 Low Validation For Leaf Sum Matching Best Practices Resolved
    Location
    Global
    Round
    Main Review

    Description

    Currently, there is no validation in fetchAndStoreEvents to ensure that the sum of all leaves equals the actual amount locked on the contract for that epoch.

    Theoretically, issues with the RPC (e.g., null values due to rate limits or internal errors) could result in the script omitting some lock events during Merkle root construction.

    Since this is a one-way migration for the Push token itself, it is critical to include post-execution validations to ensure consistency with the on-chain state.

    Recommendation

    Consider tracking the amount locked for each epoch on-chain. After constructing the Merkle tree off-chain, sum the leaves and compare the result with the on-chain locked amount to verify that the off-chain state has been correctly captured.

  11. L-11 Low Missing Permit Functionality Informational Resolved
    Location
    src/MigrationLocker.sol:72
    Round
    Main Review

    Description

    The lock functions enforces users to first approve their PUSH tokens to be spent my the MigrationLocker contract. However, the PUSH token has a permit functionality, which can't be used in the locker contract.

    Recommendation

    Consider adding a function to lock tokens with permit signature.

  12. I-01 Informational Typo In releaseInstant Function Best Practices Resolved
    Location
    src/MigrationRelease.sol:102
    Round
    Main Review

    Description

    relaese instead of release

    Recommendation

    Consider correcting it

  13. I-02 Informational Release Time Not Emitted In Events Events Resolved
    Location
    src/MigrationRelease.sol#L120C14-L120C95
    Round
    Main Review

    Description

    The MigrationRelease contract emits the ReleasedInstant and ReleasedVested when users claim their PUSH tokens. These events emit the recipient's address, amount released, and epoch.

    However, the natspec incorrectly states emits a ReleasedVested event with the recipient address, amount, and release time, but the epoch value is emitted instead.

    Recommendation

    Update the natspec to describe the correct parameters emitted in the release events, as the release time or block.timestamp will be included in the events anyways.

  14. I-03 Informational Users May Double Claim Warning Acknowledged
    Location
    Global
    Round
    Main Review

    Description

    If a user locks a, claims a, and then locks b, the script currently tracks their total as a + b. This could allow them to claim twice in the same epoch.

    Recommendation

    Beware that there should be only one merkle root update per epoch.

  15. I-04 Informational Locked Event Does Not Have Unique Identifiers Documentation Resolved
    Location
    src/MigrationLocker.sol:71
    Round
    Main Review

    Description

    The current natspec of the lock functions states: Emits a Locked event with the recipient address, amount, and a unique identifier

    However, this was an old version of the contract, where an id was used in the Locked event, instead of the epoch value, which is not unique.

    Recommendation

    Update the natspec to correctly describe the parameters emitted in the Locked Event.

  16. I-05 Informational Readme Addresses setToggleLock Function Informational Resolved
    Location
    README.md
    Round
    Main Review

    Description

    The README file key features states: Safety toggles to prevent/allow locking - Owner Controlled

    However, the setToggleLock function was removed, and the lock is now based on the pause/unpause functionality

    Recommendation

    Make sure README clearly addresses the features contained in the smart contracts.

  17. I-06 Informational Redundant Tree Construction In proofArray.js Best Practices Acknowledged
    Round
    Main Review

    Description

    A tree is constructed for each user, and then again twice for both getProof and verify in proofArray.js.

    This is incredibly inefficient, as the tree remains the same for each user.

    Recommendation

    Consider constructing the tree once and passing it as an object instead.

Remediation Review

2 findings · June 30, 2025
  1. L-01 Low Unnecessary Indexed Event Params Events Acknowledged
    Round
    Remediation Review

    Description

    event NewEpoch(uint256 indexed epoch, uint256 indexed startBlock);

    This event will only be emitted once per epoch, which will likely be a different block number.

    Recommendation

    Remove the indexed event params.

  2. I-01 Informational Offchain Balance Checks Affected By Donations Logical Error Acknowledged
    Location
    [push-chain-migration/script/utils/fetchAndStoreEvents.js at 21-audit-fixes · pushchain/push-chain-migration](https://github.com/pushchain/push-chain-migration/blob/21-audit-fixes/script/utils/fetchAndStoreEvents.js#L139)
    Round
    Remediation Review

    Description

    Push has added strict balance validations in the fetch script.

    However, if even 1 wei is accidentally or deliberately sent (donated) to the contract, these validations may cause the script to revert or throw an error.

    Recommendation

    Be aware of this edge case during debugging or triage.

    If a throw is observed, consider the possibility of donations as a potential cause.

More from Push Chain

  1. Fix Review

    5 findings 5 findings: 2 medium, 1 low, 2 informational

Put your code through the same review.

This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.

Get a quote