AI Arena - pontifex's results

In AI Arena you train an AI character to battle in a platform fighting game. Imagine a cross between PokΓ©mon and Super Smash Bros, but the characters are AIs, and you can train them to learn almost any skill in preparation for battle.

General Information

Platform: Code4rena

Start Date: 09/02/2024

Pot Size: $60,500 USDC

Total HM: 17

Participants: 283

Period: 12 days

Judge:

Id: 328

League: ETH

AI Arena

Findings Distribution

Researcher Performance

Rank: 64/283

Findings: 2

Award: $111.91

🌟 Selected for report: 0

πŸš€ Solo Findings: 0

Awards

111.676 USDC - $111.68

Labels

bug
3 (High Risk)
satisfactory
sufficient quality report
upgraded by judge
:robot:_49_group
duplicate-68

External Links

Lines of code

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L370

Vulnerability details

Impact

Users can't re-roll fighters with tokenId more than type(uint8).max but should be able. It is a medium issue because an important part of the payable functionality of the protocol does not work properly.

Proof of Concept

Users should be able to re-roll their fighters at least maxRerollsAllowed[fighterType] times via the FighterFarm.reRoll for the rerollCost amount of NRN tokens:

    /// @notice Rolls a new fighter with random traits.
    /// @param tokenId ID of the fighter being re-rolled.
    /// @param fighterType The fighter type.
    function reRoll(uint8 tokenId, uint8 fighterType) public {
        require(msg.sender == ownerOf(tokenId));
        require(numRerolls[tokenId] < maxRerollsAllowed[fighterType]);
        require(_neuronInstance.balanceOf(msg.sender) >= rerollCost, "Not enough NRN for reroll");

But the max tokenId which the reRoll function can accept is type(uint8).max. So fighters with tokenId more than type(uint8).max can't be re-rolled.

Tools Used

Manual review

Consider using type uint256 instead of uint8:

-   function reRoll(uint8 tokenId, uint8 fighterType) public {
+   function reRoll(uint256 tokenId, uint8 fighterType) public {

Assessed type

Error

#0 - c4-pre-sort

2024-02-22T02:47:06Z

raymondfam marked the issue as sufficient quality report

#1 - c4-pre-sort

2024-02-22T02:47:12Z

raymondfam marked the issue as duplicate of #68

#2 - c4-judge

2024-03-05T02:01:29Z

HickupHH3 marked the issue as satisfactory

#3 - c4-judge

2024-03-05T02:04:51Z

HickupHH3 changed the severity to 3 (High Risk)

Lines of code

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/MergingPool.sol#L139-L152

Vulnerability details

Impact

In case the amount of unclaimed rewards for the user exceeds MAX_FIGHTERS_ALLOWED the user's rewards will be blocked permanently. The user won't be able receives rewards at all.

Proof of Concept

Users are allowed to claim butch rewards for multiple rounds using the MergingPool.claimRewards function but they can't specify the number of round or reward to be claimed:

    function claimRewards(
        string[] calldata modelURIs,
        string[] calldata modelTypes,
        uint256[2][] calldata customAttributes
    )
        external
    {
        uint256 winnersLength;
        uint32 claimIndex = 0;
        uint32 lowerBound = numRoundsClaimed[msg.sender];
        for (uint32 currentRound = lowerBound; currentRound < roundId; currentRound++) {
            numRoundsClaimed[msg.sender] += 1;
            winnersLength = winnerAddresses[currentRound].length;
            for (uint32 j = 0; j < winnersLength; j++) {
                if (msg.sender == winnerAddresses[currentRound][j]) {
                    _fighterFarmInstance.mintFromMergingPool(            

There is also no function for claiming rewards for a specified round at the contract. In case the amount of rewards for the user exceeds the MAX_FIGHTERS_ALLOWED the user won't be able to claim rewards at all because of revert in the FighterFarm._createNewFighter function:

    function mintFromMergingPool(
        address to,
        string calldata modelHash,
        string calldata modelType,
        uint256[2] calldata customAttributes
    )
        public
    {
        require(msg.sender == _mergingPoolAddress);
        _createNewFighter(

https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/FighterFarm.sol#L313

    function _createNewFighter(
        address to,
        uint256 dna,
        string memory modelHash,
        string memory modelType,
        uint8 fighterType,
        uint8 iconsType,
        uint256[2] memory customAttributes
    )
        private
    {  
        require(balanceOf(to) < MAX_FIGHTERS_ALLOWED);

Even if the user transfers all his fighters to another address it will still be impossible to claim rewards. It is also inpossible to change the winnerAddresses mapping for previous rounds because the MergingPool.pickWinner function allows the admin to pick the winners only for the current round: https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/MergingPool.sol#L111-L118

Tools Used

Manual review

Consider checking the acceptable amount of rewards which user can receive and bound the for loop with the amount:

    function claimRewards(
        string[] calldata modelURIs,
        string[] calldata modelTypes,
        uint256[2][] calldata customAttributes
    )
        external
    {
        uint256 winnersLength;
        uint32 claimIndex = 0;
+       uint256 claimable = _fighterFarmInstance.MAX_FIGHTERS_ALLOWED - _fighterFarmInstance.balanceOf(msg.sender);
        uint32 lowerBound = numRoundsClaimed[msg.sender];
-       for (uint32 currentRound = lowerBound; currentRound < roundId; currentRound++) {
+       for (uint32 currentRound = lowerBound; currentRound < roundId && claimIndex < claimable; currentRound++) {
            numRoundsClaimed[msg.sender] += 1;
            winnersLength = winnerAddresses[currentRound].length;
            for (uint32 j = 0; j < winnersLength; j++) {
                if (msg.sender == winnerAddresses[currentRound][j]) {
                    _fighterFarmInstance.mintFromMergingPool(     

Assessed type

Loop

#0 - c4-pre-sort

2024-02-22T09:32:09Z

raymondfam marked the issue as sufficient quality report

#1 - c4-pre-sort

2024-02-22T09:32:19Z

raymondfam marked the issue as duplicate of #216

#2 - c4-judge

2024-03-11T12:53:35Z

HickupHH3 marked the issue as satisfactory

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax Β© 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter