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

Security review · January 2025

ANIME Claimer, Part 1

for Animecoin

Animecoin engaged Guardian to review the security of their cross-chain token claimer. From the 19th of December to the 24th of December, a team of 4 auditors reviewed the source code in scope.

Published
Review window
December 19 to 24, 2025
Language
Solidity
Chains
Arbitrum
Sector
Token launches
  • 1 Critical
  • 2 High
  • 3 Medium
  • 15 Low
  • 0 Informational

14 resolved · 7 acknowledged

Scope

Overview

Animecoin engaged Guardian to review the security of their cross-chain token claimer. From the 19th of December to the 24th of December, a team of 4 auditors reviewed the source code in scope.

Issues Detected Throughout the engagement 3 High/Critical issues were uncovered and promptly remediated by the Animecoin team. Several issues impacted the fundamental behavior of the protocol, following their remediation Guardian believes the protocol to uphold the functionality described for the claimer.

Findings 21

  1. C-01 Critical Claims By The Collector Prevented Logical Error Resolved
    Location
    AnimeClaimer.sol: 815

    Description

    In the _getClaimChecksFromVestingConfigs function the checks.collectors array is assigned a length based on the numCollectors which is purely the number of configs which satisfy c.collector = address(0).

    However the criteria for an address to be added to the checks.collectors array is isForCollector & co.collector = msg.sender.Therefore the checks.collectors array will have empty entries for the configs that are claimed by the collector address themselves.

    The checks.collectors array with empty entries is then used to verify the claim with the ClaimChecker contract on L1. The _checkCollectorClaim(claimer, collectors[i]) check will trivially fail because no claimer can be approved in the delegate registry for the zero address.

    Therefore these claims cannot be verified and cannot be completed.

    Recommendation

    Shrink the length of the checks.collectors array after writing to it or adjust the way the numCollectors is calculated so that it is only incremented in the case where isForCollector && c.collector = msg.sender.

    Resolution

    Animecoin Team: The issue was resolved in PR#10.

  2. H-01 High Users Incorrectly Credited WIth NFT Ownership At lzReceive Time Logical Error Resolved
    Location
    AnimeClaimer.sol: 844

    Description

    In the _claimBatch function after the claim has been validated by the l1 state at the timestamp of the requestClaim call, the claimer is awarded with the vested amount computed by the _vested function.

    However the _vested function determines the user’s vested amount based upon the current block.timestamp of the lzReceive action. This is however not the same timestamp that the claimer was verified to be authorized to claim at.

    The claimer could have sold their NFT to another user after the block.timestamp of the requestClaim call and thus received vested amounts which should have gone to the new owner of the NFT.

    There is a 8 block confirmation threshold currently configured, meaning that roughly 96 seconds will have to occur at minimum between the requestClaim call and the lzReceive which fulfills the claim.

    This delay period could be made even more severe during a DVN outage or if a claim has passed all DVN checks but the lzReceive function reverts for some time until the revert is resolved (e.g. claim contract is paused and unpaused, or the daily withdrawal threshold has been met for the day) and then the lzReceive function is invoked again successfully passing a significant amount of time later.

    Recommendation

    Include the timestamp of the claim request in the ClaimParameters for the corresponding claimNonce if the claimer is shown to be validated for all of the claims then the claims should be carried out up to the stored timestamp of the claim request.

    Resolution

    Animecoin Team: The issue was resolved in PR#22.

  3. H-02 High Collector Allocations Errantly Validated Logical Error Resolved
    Location
    AnimeClaimer.sol: 814

    Description

    In the AnimeClaimer contract when the claimer is the collector for a collector vest then there is no further validation performed on the vest and the vested amount is granted to the collector address on the L2.

    However the owner of the collector address on the L2 may be different then the owner of the collector address on the L1.

    For example, a smart contract wallet/multisig could have been transferred from Alice to Bob on the L1, but Alice may have kept ownership of a smart contract wallet/multisig deployed at the same address on the L2.

    Recommendation

    Ensure that all collector addresses which are awarded are EOAs on both chains. If this is not the case then a more adept verification process is necessary.

    Resolution

    Animecoin Team: The issue was resolved in PR#14.

  4. M-01 Medium Insufficient Gas Estimated Logical Error Resolved
    Location
    AnimeClaimer.sol: 308

    Description

    In the requestClaim function the checks.numNFTs and checks.numCollectors values are used to compute the gas that should be provided to the lzReceive function on the L2.

    However the checks.numCollectors value only includes the number of collector claims that need to be verified on the L1, which does not include claims where the collector is the claimer.

    These claims will still have to be iterated over in the lzReceive function though, and thus should be accounted for in the gas estimation.

    Recommendation

    Consider basing the gas estimation in the _lzOptions function on the configs.length instead as this more closely represents the iterations that must be made in the lzReceive function.

    Resolution

    Animecoin Team: The issue was resolved in PR#11.

  5. M-02 Medium Incompatible Types Logical Error Resolved
    Location
    AnimeClaimer.sol

    Description

    A VestingConfig object contains uint256 streamId but the VestingConfigStorage contains uint8 streamId. Consequently, some user configurations will be unclaimable when the uint256 type is cast to uint8 within _saveClaimParameters, as it will revert with error Overflow().

    Recommendation

    Consider keeping types consistent or clearly document this behavior.

    Resolution

    Animecoin Team: The issue was resolved in PR#25.

  6. M-03 Medium Sanctions Can Be Avoided Unexpected Behavior Resolved
    Location
    AnimeClaimer.sol

    Description

    Currently checkClaims only validates sanctions against the claimer: if (isSanctioned(claimer)) return (claimNonce, false); However, there's is no validation that the actual owner of the NFT is not sanctioned in the case that the claimer is a delegate.

    Recommendation

    Consider adding sanctions validation on the NFT owner, otherwise clearly document this behavior.

    Resolution

    Animecoin Team: The issue was resolved in PR#13.

  7. L-01 Low Missing _logAdminAccess Calls Events Resolved
    Location
    AnimeClaimer.sol: 460, 467

    Description

    In the setReadChannel and setReadConfirmations onlyOwner functions there is no call to the _logAdminAccess function to emit an event for these admin updates.

    Additionally, in the OAppCore contract there is a setDelegate onlyOwner function which is not overridden and thus will not emit a AdminAccessed event.

    Recommendation

    Consider adding a _logAdminAccess invocation to these functions.

    Resolution

    Animecoin Team: The issue was resolved in PR#15.

  8. L-01 Low Last Withdrawn Day Getter Missing Composability Acknowledged
    Location
    AnimeClaimer.sol

    Description

    The AnimeClaimer includes many getters to query the state of the system but is missing a getter for lastWithdrawnDay.

    Recommendation

    Consider adding a getter for lastWithdrawnDay.

    Resolution

    Animecoin Team: Acknowledged.

  9. L-02 Low Typo Typo Resolved
    Location
    AnimeClaimer.sol: 288

    Description

    In the comment for the requestClaim function the _lzReceive function is referred to as _lsReceive.

    Recommendation

    Correct this to _lzReceive.

    Resolution

    Animecoin Team: The issue was resolved in PR#16.

  10. L-02 Low View Functions Not Accurate Typo Acknowledged
    Location
    AnimeClaimer.sol

    Description

    Function dailyTotalWithdrawn() aims to return the current dailyTotalWithdrawn, however the value returned may be stale since the current day be different from the lastWithdrawnDay but _resetDailyTotalWithdrawnIfNewDay hasn't been triggered yet.

    This can be problematic for integrators reading this value and assuming the current day has already had withdraws when it hasn't.

    Recommendation

    Consider adding logic within dailyTotalWithdrawn() to reflect the start of a new day.

    Resolution

    Animecoin Team: Acknowledged.

  11. L-03 Low Incorrect Comment Documentation Resolved
    Location
    AnimeClaimer.sol: 144

    Description

    In the comment for the uuidSigner value in the AnimeClaimerStorage struct it is mentioned that this value SHOULD be configured, however this value must be configured along with the other values that are documented as such.

    This is because the uuidSigner must be configured to a nonzero address for the readyForClaim function to return true.

    Recommendation

    Correct the comment to indicate that the uuidSigner qualifies as a variable which MUST be configured.

    Resolution

    Animecoin Team: The issue was resolved in PR#17.

  12. L-04 Low Refund Receiver Is Always The Sender Unexpected Behavior Acknowledged
    Location
    Global

    Description

    The requestClaim function assigns the msg.sender as the refund receiver for a native refund. As a result, Smart Contracts which receive a collector allocation, do not have the functionality to delegate, and do not have a receive function cannot claim their vest.

    Recommendation

    This scenario is unlikely to occur, especially for a contract which is able to claim from the claimer. However if this issue is desired to be solved, or additional utility should be added, a refundReceiver parameter can be added to and used within the requestClaim function.

    Resolution

    Animecoin Team: Acknowledged.

  13. L-04 Low Delegate Registry On Multiple Chains Documentation Acknowledged
    Location
    Global

    Description

    Currently delegation is validated solely through the DelegateRegistry contract deployed on Ethereum within function checkClaims. Consequently, delegations through the DelegateRegistry contract deployed on Arbitrum will not allow for the receipt of allocations.

    Recommendation

    Clearly document this behavior to users.

    Resolution

    Animecoin Team: Acknowledged.

  14. L-05 Low Claims Must Always Go To The Claimer Unexpected Behavior Acknowledged
    Location
    AnimeClaimer.sol

    Description

    In the previous version of the AnimeClaimer contract there was a to address which allowed users to claimBatch to their desired address on Arbitrum.

    However in the new AnimeClaimer contract the allocations are always sent to the claimer who calls the requestClaim function.

    This may decrease the simplicity of integrations/user interactions with the AnimeClaimer, especially for claims that are made on behalf of another collector using the delegate registry.

    Recommendation

    Consider adding a to parameter to the requestClaim function so that a claim can be sent to the configured address rather than always to the claimer.

    Resolution

    Animecoin Team: Acknowledged.

  15. L-05 Low Sanctions Could Be Validated On Arbitrum Documentation Acknowledged
    Location
    Global

    Description

    The ChainAnalysis oracle for sanctioned addresses is also deployed on Arbitrum. Consequently, sanctions can be verified at the start of a requestClaim to prevent the message from even being sent, as well as _lzReceive to account for sanctions list changes in the case of delayed receipt (e.g. if there is a DVN outage).

    Recommendation

    Consider querying the Arbitrum oracle as well and accounting for any necessary gas usage increases, otherwise document this behavior.

    Resolution

    Animecoin Team: Acknowledged.

  16. L-06 Low Overallocated Expected Calldata Optimization Resolved
    Location
    AnimeClaimer.sol: 278

    Description

    In the AnimeClaimer constructor the expectedCalldataSize which is denominated in calladata bytes is assigned as 256.

    This is ultimately used as the calldata size option in the lz options blob passed to the send function. This is meant to estimate the cost of the additional calldata which is provided from the result of the read function on the L1 chain.

    However the checkClaims function result will not return a payload of 256 bytes of calldata to the lzReceive function, it returns a payload of 64 bytes of calldata to the lzReceive function. One 32 byte word for the claimNonce and one padded 32 byte word for the authorized boolean.

    This results in overpaying the executor for the additional calldata that is estimated to be required for the lzRead response payload on every claim.

    Recommendation

    Update the expectedCalldataSize to 64 bytes.

    Resolution

    Animecoin Team: The issue was resolved in PR#24.

  17. L-07 Low Runbook Typo Documentation Resolved
    Location
    Runbook

    Description

    The provided runbook contains a typo in the instructions for deploying the ClaimChecker contract on Ethereum. The first step mentions Only select “ethereum”, unselect “ethereum” and others. However it should read Only select “ethereum”, unselect “arbitrum” and others.

    Recommendation

    Correct the runbook step to Only select “ethereum”, unselect “arbitrum” and others.

    Resolution

    Animecoin Team: Resolved.

  18. L-08 Low Misleading Comment Documentation Resolved
    Location
    AnimeClaimer.sol: 415, 421

    Description

    In the comment for both implementations of the quoteForClaim function it is mentioned that the purpose of the function is to:

    Returns the amount of ETH in wei that needs to be passed into claimBatch. However the quoteForClaim function is intended to return the amount of ETH in wei that needs to be passed into the requestClaim function.

    Recommendation

    Correct the comment above both implementations of the quoteForClaim functions to:

    Returns the amount of ETH in wei that needs to be passed into requestClaim.

    Resolution

    Animecoin Team: The issue was resolved in PR#23.

  19. L-09 Low recoverCalldata Optimization Optimization Resolved
    Location
    AnimeClaimer.sol: 734

    Description

    In the _validateRequestClaim function the ECDSA library recover function is used. However the config object with the signature to be verified is declared as calldata, therefore the recoverCalldata function should be used to save a couple hundred gas.

    Recommendation

    Use recoverCalldata instead of recover.

    Resolution

    Animecoin Team: The issue was resolved in PR#26.

  20. L-10 Low Unnecessary Ownership Timestamp Check Optimization Resolved
    Location
    ClaimChecker.sol: 70

    Description

    In the ClaimChecker contract the _checkNFTClaim function implements specific logic for Azuki NFTs which asserts that ownership of the NFT being verified has not begun at this block.timestamp.

    This is to prevent NFT flash loans from being used to flash loan an NFT and claim it’s outstanding vest amount. However it is impossible to use an intra-transaction flash loan to prove ownership of an NFT because the DVNs will not check the state of the L1 at an intermediate transaction state.

    Therefore this check for Azuki NFT ownership is no longer necessary and is instead preventing users from claiming their vest at the timestamp of when they actually began their ownership. This case is unlikely to affect UX, however it can be removed for simplicity.

    Recommendation

    Consider removing the specific logic for Azuki NFTs in the _checkNFTClaim function.

    Resolution

    Animecoin Team: The issue was resolved in PR#29.

  21. L-11 Low Fixed Term Loans Can Claim Outstanding Vests Documentation Acknowledged
    Location
    Global

    Description

    There exist platforms such as Blur which support fixed term loans of NFTs which will be awarded allocations, such as Azukis.

    For Azukis which have accrued an unclaimed vest allocation a fixed term loan can be made to allow the loaner to claim the outstanding vested amount even though they did not hold ownership over the NFT for the previous vesting period.

    Recommendation

    Be aware of this risk and document it for users who have their NFTs available for loan.

    Resolution

    Animecoin Team: Acknowledged.

More from Animecoin

  1. ANIME Claimer, Part 2

    12 findings1 high 12 findings: 1 high, 3 medium, 8 low
  2. ANIME Claimer, Part 3

    12 findings 12 findings: 12 low
  3. ANIME Claimer

    23 findings2 critical · 1 high 23 findings: 2 critical, 1 high, 4 medium, 16 low

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