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

Security review · January 2025

ANIME Claimer, Part 3

for Animecoin

Animecoin engaged Guardian to review the security of its review of their cross-chain token claimer. From the 14th of January to the 16th of January, a team of 3 auditors reviewed the source code in scope.

Published
Review window
January 14 to 16, 2025
Language
Solidity
Chains
Arbitrum
Sector
Token launches
  • 0 Critical
  • 0 High
  • 0 Medium
  • 12 Low
  • 0 Informational

4 resolved · 1 partially resolved · 7 acknowledged

Scope

Overview

Animecoin engaged Guardian to review the security of its review of their cross-chain token claimer. From the 14th of January to the 16th of January, a team of 3 auditors reviewed the source code in scope.

Findings 12

  1. L-01 Low Unused Import Best Practices Resolved
    Location
    AnimeClaimer.sol

    Description

    The EVMCallComputeV1 struct is imported in the AnimeClaimer however it is unused.

    Recommendation

    Consider removing the EVMCallComputeV1 import.

    Resolution

    Animecoin Team: Resolved.

  2. L-02 Low Unused Errors Optimization Resolved
    Location
    AnimeClaimer.sol

    Description

    The InvalidClaimerType and ClaimIsZeroAddress errors in the AnimeClaimer contract are unused.

    Recommendation

    Remove the InvalidClaimerType and ClaimIsZeroAddress errors.

    Resolution

    Animecoin Team: Resolved.

  3. L-03 Low isForNFT Optimization Optimization Acknowledged
    Location
    AnimeClaimer.sol

    Description

    In the _validateRequestClaim function the isForNFT variable is declared outside of the UUID case, however it is only used inside of the UUID case. Therefore an optimization for non-UUID cases is to move the isForNFT declaration inside of the UUID case.

    Recommendation

    Move the isForNFT inside of the UUID case in the _validateRequestClaim function.

    Resolution

    Animecoin Team: Acknowledged.

  4. L-04 Low Unnecessary Cast Optimization Acknowledged
    Location
    AnimeClaimer.sol: 365

    Description

    In the _claimBatch function the s.withdrawn variable is cast to a uint256 type when calculating the withdrawAmount. However the s.withdrawn variable is already a uint256 type and therefore does not need to be cast.

    Recommendation

    Remove the unnecessary cast for the s.withdrawn variable.

    Resolution

    Animecoin Team: Acknowledged.

  5. L-05 Low Compromised Signer Griefing Attack Griefing Acknowledged
    Location
    AnimeClaimer.sol: 766

    Description

    In the _validateRequestClaim for UUID related claims the uuidToPackedNftID and nftToUUID mappings are written to and validate that the corresponding UUID to tokenId pairing is the only one used for either the UUID or tokenId in future claims.

    This is to prevent a compromised UUID signer from being able to steal more than the amount of the unrevealed elemental’s vests. However now that the uuidToPackedNftID mapping has been introduced a compromised signer gains another potentially harmful attack.

    Consider the following scenario:

    • Normal Azuki with ID 1 has the highest allocation out of all NFTs of 50 Million $ANIME tokens
    • Alice does not own the Azuki with ID 1
    • Alice gains access to the UUID signer
    • Alice forges a signature that shows that Azuki with ID 1 corresponds to a UUID claim with only

    10,000 $ANIME tokens

    • Alice submits a requestClaim call, the nftToUUID and uuidToPackedNftID mappings are written with

    the errant pairing

    • Alice’s claim verification fails the claimChecker, since she does not own the Azuki with ID 1
    • However Alice’s requestClaim transaction was successful, so the mapping values remain
    • The actual owner of Azuki ID 1 cannot make their normal claim and thus loses out on 49,990,000

    $ANIME tokens

    The compromised signer may repeat this attack for many of the largest token allocations, until low allocation UUID related claims run out.

    Recommendation

    Consider restricting UUID claims to only Azuki Elemental collection claims to limit this griefing attack vector from affecting large allocations from other NFT collections.

    Otherwise be aware of this secondary exploit that can happen with a compromised UUID signer. The contract has the sufficient owner methods to repair this griefing attack if it were to take place.

    Resolution

    Animecoin Team: Acknowledged.

  6. L-06 Low Delayed Claim Warning Warning Partially resolved
    Location
    AnimeClaimer.sol

    Description

    The maximum age for a read response to be executed allowed in the AnimeClaimer contract is 1 hour. This significantly limits the risk of stale read requests being executed far past the time at which ownership/delegation was verified at. However there is still some risk that should be communicated with users.

    For example, consider the following scenario:

    • Alice has Azuki #7 with 10 ANIME tokens vested on day 10 at 11:30pm
    • The day 10 withdrawal limit has already been met on Arbitrum
    • Alice intentionally submits a claim request that will fail upon lzReceive due to the withdrawal limit,

    but validates Alice as the owner of Azuki #7 up to block.timestamp of day 10 at 11:30pm, allowing Alice to claim 10 ANIME tokens

    • On day 11 at 12:10am Alice lists their Azuki #7 for sale under the pretense of the buyer being able

    to claim the 10 vested ANIME tokens

    • Azuki #7 is sold to Bob on day 11 at 12:20am
    • Alice retries her lzReceive on Arbitrum by invoking the lzReceive function through the endpoint

    herself. The read result is still in the message channel so this action is allowed.

    • Alice claims the 10 vested ANIME tokens that had accrued up to day 10 at 11:30pm
    • Bob purchased Azuki #7 under the pretense that he would also receive these 10 vested ANIME

    tokens, however after his purchase Alice took those 10 ANIME tokens from him

    Recommendation

    This finding serves merely to document this risk for users.

    Resolution

    Animecoin Team: Partially Resolved.

  7. L-07 Low Lacking Zero Configs Validation Validation Resolved
    Location
    AnimeClaimer.sol

    Description

    In the requestClaim function there is no validation that the configs array is a nonzero length.

    Recommendation

    To avoid any unexpected behavior, consider validating that the configs array is a nonzero length.

    Resolution

    Animecoin Team: Resolved.

  8. L-08 Low Anime Coin Trapped Ether Trapped Ether Acknowledged
    Location
    Animecoin.sol: 53

    Description

    The registerTokenOnL2 function accepts ether but does not explicitly use all of the msg.value sent nor refund additional msg.value to the caller. If excess ether is sent it will be trapped in the Animecoin contract .

    Recommendation

    Consider validating that exactly the valueForGateway and valueForRouter is sent in the registerTokenOnL2 or refund additional ether to the caller. Alternatively consider adding an owner function to withdraw ether from the contract.

    Resolution

    Animecoin Team: Acknowledged.

  9. L-09 Low Unused Contract Best Practices Acknowledged
    Location
    AnimeClaimer.sol: 26

    Description

    AnimeClaimer contract inherits from OAppOptionsType3 but does not use anything from it.

    Recommendation

    Consider removing the OAppOptionsType3 inherited contract.

    Resolution

    Animecoin Team: Acknowledged.

  10. L-10 Low Misleading Comment Best Practices Resolved
    Location
    AnimeClaimer.sol: 138

    Description

    The expectedCalldataSize in the AnimerClaimer contract is set to 128, although the size of the return data from ClaimChecker:checkClaims is 64 bytes.

    The comment above the expectedCalldataSize storage variable states that "Any extra overallocated is returned to the sender anyways".

    The calldata parameter is used inside the Executor to determine the amount to pay for the LZ message and if it is larger than the expected size, the extra gas is not returned to the sender.

    Recommendation

    The additional gas cost is negligible but consider changing the comment to reflect the actual behavior.

    Resolution

    Animecoin Team: Resolved.

  11. L-11 Low Merkle Tree Entries Validation Acknowledged
    Location
    AnimeClaimer.sol

    Description

    The AnimeClaimer contract stores vesting state using a hash key:

    bytes32 hash = EfficientHashLib.hash(uint160(nft), tokenId, uint160(collector), streamId);

    When creating multiple allocations in the Merkle tree for:

    • Same NFT/tokenId pair, or
    • Same collector address

    Each allocation must use a unique streamId to prevent storage collisions in the vesting state.

    Recommendation

    When generating the Merkle tree, ensure unique streamId values for each allocation to the same recipient (NFT or collector).

    Resolution

    Animecoin Team: Acknowledged.

  12. L-12 Low Incompatible ClockMode Compatibility Acknowledged
    Location
    Animecoin.sol

    Description

    The Animecoin contract on Arbitrum is an ERC20Votes token, however uses the default clock mode of block.number. This means the voting system will rely on checkpoints based on L1 blocks rather than L2 blocks or timestamps.

    This can lead to integration issues with the governance system that integrates with the ERC20Votes token.

    Recommendation

    The recommended clock mode for Arbitrum governance tokens is timestamp.

    Resolution

    Animecoin Team: Acknowledged.

More from Animecoin

  1. ANIME Claimer, Part 1

    21 findings1 critical · 2 high 21 findings: 1 critical, 2 high, 3 medium, 15 low
  2. ANIME Claimer, Part 2

    12 findings1 high 12 findings: 1 high, 3 medium, 8 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