AI Arena - donkicha'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: 149/283

Findings: 1

Award: $13.63

🌟 Selected for report: 0

πŸš€ Solo Findings: 0

Awards

13.6293 USDC - $13.63

Labels

bug
G (Gas Optimization)
grade-b
sufficient quality report
G-08

External Links

Gas Optimizations Report

Note to the Judge:

I want to assure you that all gas optimizations in question were researched manually without the use of any automated tools or bots. I researched each of the gas issues below mannually to provide you with complete reports about each issue. Your understanding on this matter is greatly appreciated.

IDOptimization
[G-01]Redundant Code in the constructor of the AiArenaHelper contract
[G-02]Use of Increment Operator in the pickWinner function of the MergingPool contract
[G-03]Optimizing uint32 Declaration in the claimRewards function of the MergingPool contract

[G-01] Redundant Code in the constructor of the AiArenaHelper contract

Description In the provided Solidity code for the AiArenaHelper contract, there exists a redundant assignment within the constructor function. The line attributeProbabilities[0][attributes[i]] = probabilities[i]; duplicates the task performed by the addAttributeProbabilities function.

Code:


constructor(uint8[][] memory probabilities) {
    _ownerAddress = msg.sender;

    // Initialize the probabilities for each attribute
    addAttributeProbabilities(0, probabilities);

    uint256 attributesLength = attributes.length;
    for (uint8 i = 0; i < attributesLength; i++) {
-       attributeProbabilities[0][attributes[i]] = probabilities[i]; // Redundant assignment
        attributeToDnaDivisor[attributes[i]] = defaultAttributeDivisor[i];
    }
}

Gas report before the optimization:

src/AiArenaHelper.sol:AiArenaHelper contract
Deployment CostDeployment Size
17412798445
Function Nameminavgmedianmax# calls
addAttributeDivisor36122686826868501242
addAttributeProbabilities1292841292841292841292841
attributeToDnaDivisor9211921192129212
createPhysicalAttributes104368269836878546353
deleteAttributeProbabilities25363429533895668544
dnaToIndex90389038903890381
getAttributeProbabilities36986364769876983
transferOwnership24914027402755642

Gas report after the optimization:

src/AiArenaHelper.sol:AiArenaHelper contract
Deployment CostDeployment Size
16616238308
Function Nameminavgmedianmax# calls
addAttributeDivisor36122686826868501242
addAttributeProbabilities1292841292841292841292841
attributeToDnaDivisor9211921192129212
createPhysicalAttributes104368269836878546353
deleteAttributeProbabilities25363429533895668544
dnaToIndex90389038903890381
getAttributeProbabilities36986364769876983
transferOwnership24914027402755642

You can see the difference in the gas usage

[G-02] Use of Increment Operator in the pickWinner function of the MergingPool contract

Description In the provided smart contract function pickWinner, there is a gas optimization opportunity regarding the method used to increment the roundId variable. Currently, the code uses the roundId += 1 statement to increment the roundId variable after selecting winners for the current round. However, replacing it with the roundId++ increment operator can offer a slight gas optimization.

Code:


    function pickWinner(uint256[] calldata winners) external {
        require(isAdmin[msg.sender]);
        require(
            winners.length == winnersPerPeriod, // G? `winners.length` can be declared as a variable before the `require` statement?
            "Incorrect number of winners"
        );
        require(!isSelectionComplete[roundId], "Winners are already selected");
        uint256 winnersLength = winners.length;
        address[] memory currentWinnerAddresses = new address[](winnersLength);
        for (uint256 i = 0; i < winnersLength; i++) {
            currentWinnerAddresses[i] = _fighterFarmInstance.ownerOf(
                winners[i]
            );
            totalPoints -= fighterPoints[winners[i]];
            fighterPoints[winners[i]] = 0;
        }
        winnerAddresses[roundId] = currentWinnerAddresses;
        isSelectionComplete[roundId] = true;
-       roundId += 1;
+       roundId++;
    }

Gas report before the optimization:

src/MergingPool.sol:MergingPool contract
Deployment CostDeployment Size
9122024340
Function Nameminavgmedianmax# calls
addPoints260341316482744827410
adjustAdminAccess25371669422773247733
claimRewards7807157889927889927972692
getFighterPoints10292011201129942
getUnclaimedRewards7833517351762512
isAdmin582124858225823
isSelectionComplete48598548524854
pickWinner4877970871121401290906
totalPoints385105138523853
transferOwnership24984034403455712
updateWinnersPerPeriod24324935493574392
winnerAddresses7497497497496
winnersPerPeriod3171317131723172

Gas report after the optimization:

src/MergingPool.sol:MergingPool contract
Deployment CostDeployment Size
9116024337
Function Nameminavgmedianmax# calls
addPoints260341316482744827410
adjustAdminAccess25371669422773247733
claimRewards7807157889927889927972692
getFighterPoints10292011201129942
getUnclaimedRewards7833517351762512
isAdmin582124858225823
isSelectionComplete48598548524854
pickWinner4877970771121281290786
totalPoints385105138523853
transferOwnership24984034403455712
updateWinnersPerPeriod24324935493574392
winnerAddresses7497497497496
winnersPerPeriod3171317131723172

You can see the difference in the gas usage

[G-03] Optimizing uint32 Declaration in the claimRewards function of the MergingPool contract

Description The claimRewards function in the provided code contains a gas optimization opportunity by directly initializing the uint32 variable currentRound within the for loop declaration instead of pre-declaring and initializing it outside the loop. This optimization reduces redundant variable declarations and enhances code readability.

Code:


    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;
+           uint32 currentRound = numRoundsClaimed[msg.sender];
            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(
                        msg.sender,
                        modelURIs[claimIndex],
                        modelTypes[claimIndex],
                        customAttributes[claimIndex]
                    );
                    claimIndex += 1;
                }
            }
        }
        if (claimIndex > 0) {
            emit Claimed(msg.sender, claimIndex);
        }
    }

Link To Code: https://github.com/code-423n4/2024-02-ai-arena/blob/cd1a0e6d1b40168657d1aaee8223dc050e15f8cc/src/MergingPool.sol#L148-L149

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

Gas report before the optimization:

src/MergingPool.sol:MergingPool contract
Deployment CostDeployment Size
9122024340
Function Nameminavgmedianmax# calls
addPoints260341316482744827410
adjustAdminAccess25371669422773247733
claimRewards7807157889927889927972692
getFighterPoints10292011201129942
getUnclaimedRewards7833517351762512
isAdmin582124858225823
isSelectionComplete48598548524854
pickWinner4877970871121401290906
totalPoints385105138523853
transferOwnership24984034403455712
updateWinnersPerPeriod24324935493574392
winnerAddresses7497497497496
winnersPerPeriod3171317131723172

Gas report after the optimization:

src/MergingPool.sol:MergingPool contract
Deployment CostDeployment Size
9112024335
Function Nameminavgmedianmax# calls
addPoints260341316482744827410
adjustAdminAccess25371669422773247733
claimRewards7807107889877889877972642
getFighterPoints10292011201129942
getUnclaimedRewards7753509350962432
isAdmin582124858225823
isSelectionComplete48598548524854
pickWinner4877970871121401290906
totalPoints385105138523853
transferOwnership24984034403455712
updateWinnersPerPeriod24324935493574392
winnerAddresses7497497497496
winnersPerPeriod3171317131723172

You can see the difference in the gas usage

#0 - raymondfam

2024-02-25T21:06:02Z

3 very good G.

#1 - c4-pre-sort

2024-02-25T21:06:08Z

raymondfam marked the issue as sufficient quality report

#2 - c4-judge

2024-03-19T07:56:33Z

HickupHH3 marked the issue as grade-b

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