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
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
-
C-01 Critical Claims By The Collector Prevented Logical Error Resolved
Description
In the
_getClaimChecksFromVestingConfigsfunction thechecks.collectorsarray is assigned a length based on thenumCollectorswhich is purely the number of configs which satisfyc.collector =address(0).However the criteria for an address to be added to the
checks.collectorsarray isisForCollector & co.collector = msg.sender.Therefore thechecks.collectorsarray will have empty entries for the configs that are claimed by the collector address themselves.The
checks.collectorsarray with empty entries is then used to verify the claim with theClaimCheckercontract 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.collectorsarray after writing to it or adjust the way thenumCollectorsis calculated so that it is only incremented in the case whereisForCollector && c.collector = msg.sender.Resolution
Animecoin Team: The issue was resolved in PR#10.
-
H-01 High Users Incorrectly Credited WIth NFT Ownership At lzReceive Time Logical Error Resolved
Description
In the
_claimBatchfunction after the claim has been validated by the l1 state at the timestamp of therequestClaimcall, the claimer is awarded with the vested amount computed by the_vestedfunction.However the
_vestedfunction determines the user’s vested amount based upon the currentblock.timestampof thelzReceiveaction. 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.timestampof therequestClaimcall 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
requestClaimcall and thelzReceivewhich 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
lzReceivefunction 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 thelzReceivefunction is invoked again successfully passing a significant amount of time later.Recommendation
Include the timestamp of the claim request in the
ClaimParametersfor the correspondingclaimNonceif 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.
-
H-02 High Collector Allocations Errantly Validated Logical Error Resolved
Description
In the
AnimeClaimercontract 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.
-
M-01 Medium Insufficient Gas Estimated Logical Error Resolved
Description
In the
requestClaimfunction thechecks.numNFTsandchecks.numCollectorsvalues are used to compute the gas that should be provided to thelzReceivefunction on the L2.However the
checks.numCollectorsvalue 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
lzReceivefunction though, and thus should be accounted for in the gas estimation.Recommendation
Consider basing the gas estimation in the
_lzOptionsfunction on theconfigs.lengthinstead as this more closely represents the iterations that must be made in thelzReceivefunction.Resolution
Animecoin Team: The issue was resolved in PR#11.
-
M-02 Medium Incompatible Types Logical Error Resolved
Description
A
VestingConfigobject containsuint256 streamIdbut theVestingConfigStoragecontainsuint8streamId. Consequently, some user configurations will be unclaimable when theuint256type is cast touint8within_saveClaimParameters, as it will revert with errorOverflow().Recommendation
Consider keeping types consistent or clearly document this behavior.
Resolution
Animecoin Team: The issue was resolved in PR#25.
-
M-03 Medium Sanctions Can Be Avoided Unexpected Behavior Resolved
Description
Currently
checkClaimsonly 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.
-
L-01 Low Missing _logAdminAccess Calls Events Resolved
Description
In the
setReadChannelandsetReadConfirmations onlyOwnerfunctions there is no call to the_logAdminAccessfunction to emit an event for these admin updates.Additionally, in the
OAppCorecontract there is asetDelegate onlyOwnerfunction which is not overridden and thus will not emit aAdminAccessedevent.Recommendation
Consider adding a
_logAdminAccessinvocation to these functions.Resolution
Animecoin Team: The issue was resolved in PR#15.
-
L-01 Low Last Withdrawn Day Getter Missing Composability Acknowledged
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.
-
L-02 Low Typo Typo Resolved
Description
In the comment for the
requestClaimfunction the_lzReceivefunction is referred to as_lsReceive.Recommendation
Correct this to
_lzReceive.Resolution
Animecoin Team: The issue was resolved in PR#16.
-
L-02 Low View Functions Not Accurate Typo Acknowledged
Description
Function
dailyTotalWithdrawn()aims to return the currentdailyTotalWithdrawn, however the value returned may be stale since the current day be different from thelastWithdrawnDaybut_resetDailyTotalWithdrawnIfNewDayhasn'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.
-
L-03 Low Incorrect Comment Documentation Resolved
Description
In the comment for the
uuidSignervalue in theAnimeClaimerStoragestruct it is mentioned that this valueSHOULD be configured, however this value must be configured along with the other values that are documented as such.This is because the
uuidSignermust be configured to a nonzero address for thereadyForClaimfunction to return true.Recommendation
Correct the comment to indicate that the
uuidSignerqualifies as a variable whichMUST beconfigured.Resolution
Animecoin Team: The issue was resolved in PR#17.
-
L-04 Low Refund Receiver Is Always The Sender Unexpected Behavior Acknowledged
Description
The
requestClaimfunction assigns themsg.senderas 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
refundReceiverparameter can be added to and used within therequestClaimfunction.Resolution
Animecoin Team: Acknowledged.
-
L-04 Low Delegate Registry On Multiple Chains Documentation Acknowledged
Description
Currently delegation is validated solely through the
DelegateRegistrycontract deployed on Ethereum within functioncheckClaims. Consequently, delegations through theDelegateRegistrycontract deployed on Arbitrum will not allow for the receipt of allocations.Recommendation
Clearly document this behavior to users.
Resolution
Animecoin Team: Acknowledged.
-
L-05 Low Claims Must Always Go To The Claimer Unexpected Behavior Acknowledged
Description
In the previous version of the
AnimeClaimercontract there was atoaddress which allowed users toclaimBatchto their desired address on Arbitrum.However in the new
AnimeClaimercontract the allocations are always sent to the claimer who calls therequestClaimfunction.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
toparameter to therequestClaimfunction so that a claim can be sent to the configured address rather than always to the claimer.Resolution
Animecoin Team: Acknowledged.
-
L-05 Low Sanctions Could Be Validated On Arbitrum Documentation Acknowledged
Description
The
ChainAnalysisoracle for sanctioned addresses is also deployed on Arbitrum. Consequently, sanctions can be verified at the start of arequestClaimto prevent the message from even being sent, as well as_lzReceiveto 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.
-
L-06 Low Overallocated Expected Calldata Optimization Resolved
Description
In the
AnimeClaimerconstructor theexpectedCalldataSizewhich 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
checkClaimsfunction result will not return a payload of 256 bytes of calldata to thelzReceivefunction, it returns a payload of 64 bytes of calldata to thelzReceivefunction. One 32 byte word for theclaimNonceand one padded 32 byte word for theauthorizedboolean.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
expectedCalldataSizeto 64 bytes.Resolution
Animecoin Team: The issue was resolved in PR#24.
-
L-07 Low Runbook Typo Documentation Resolved
Description
The provided runbook contains a typo in the instructions for deploying the
ClaimCheckercontract on Ethereum. The first step mentionsOnly select “ethereum”, unselect “ethereum” and others. However it should readOnly select “ethereum”, unselect “arbitrum” and others.Recommendation
Correct the runbook step to
Only select “ethereum”, unselect “arbitrum” and others.Resolution
Animecoin Team: Resolved.
-
L-08 Low Misleading Comment Documentation Resolved
Description
In the comment for both implementations of the
quoteForClaimfunction it is mentioned that the purpose of the function is to:Returns the amount of ETH in wei that needs to be passed intoclaimBatch.However thequoteForClaimfunction is intended to return the amount of ETH in wei that needs to be passed into therequestClaimfunction.Recommendation
Correct the comment above both implementations of the
quoteForClaimfunctions 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.
-
L-09 Low recoverCalldata Optimization Optimization Resolved
Description
In the
_validateRequestClaimfunction the ECDSA libraryrecoverfunction is used. However the config object with thesignatureto be verified is declared ascalldata, therefore therecoverCalldatafunction should be used to save a couple hundred gas.Recommendation
Use
recoverCalldatainstead ofrecover.Resolution
Animecoin Team: The issue was resolved in PR#26.
-
L-10 Low Unnecessary Ownership Timestamp Check Optimization Resolved
Description
In the
ClaimCheckercontract the_checkNFTClaimfunction implements specific logic for Azuki NFTs which asserts that ownership of the NFT being verified has not begun at thisblock.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
_checkNFTClaimfunction.Resolution
Animecoin Team: The issue was resolved in PR#29.
-
L-11 Low Fixed Term Loans Can Claim Outstanding Vests Documentation Acknowledged
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.
No findings match.
More from Animecoin
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.
