#41682 [SC-Insight] Code can be optimized to use save a lot of gas.
Submitted on Mar 17th 2025 at 14:39:34 UTC by @k1k1 for Audit Comp | Yeet
Report ID: #41682
Report Type: Smart Contract
Report severity: Insight
Target: https://github.com/immunefi-team/audit-comp-yeet/blob/main/src/Reward.sol
Impacts:
Description
Brief/Intro
The code uses YeetGameSettings
contract for settings but the way the YeetGameSettings
is defined is unoptimized , as the game settings address doesn't get re-initialized it must have been declared as immutable
, as it's not optimized it consumes extra gas as a state variable would.
Vulnerability Details
look at line
YeetGameSettings public gameSettings;
and here also
function copySettings() internal {
YEET_TIME_SECONDS = gameSettings.YEET_TIME_SECONDS();
POT_DIVISION = gameSettings.POT_DIVISION();
TAX_PER_YEET = gameSettings.TAX_PER_YEET();
TAX_TO_STAKERS = gameSettings.TAX_TO_STAKERS();
TAX_TO_PUBLIC_GOODS = gameSettings.TAX_TO_PUBLIC_GOODS();
TAX_TO_TREASURY = gameSettings.TAX_TO_TREASURY();
YEETBACK_PERCENTAGE = gameSettings.YEETBACK_PERCENTAGE();
COOLDOWN_TIME = gameSettings.COOLDOWN_TIME();
BOOSTRAP_PHASE_DURATION = gameSettings.BOOSTRAP_PHASE_DURATION();
MINIMUM_YEET_POINT = gameSettings.MINIMUM_YEET_POINT();
}
now when ever restart
function gets called this copySettings
function gets triggered , which causes around 25841
more gas consumption for the transaction.
Impact Details
The caller has to pay more gas for the same operation which could be done using less gas.
References
Add any relevant links to documentation or code
Proof of Concept
Proof of Concept
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.28;
import {Test, console} from "forge-std/Test.sol";
import {YeetGameSettings} from "../../src/YeetGameSettings.sol";
contract copy_settings is Test {
YeetGameSettings _game_settings;
A a;
B b;
uint256 gas_consumed_1;
uint256 gas_consumed_2;
uint256 net_gas_consumed;
function setUp() public {
_game_settings = new YeetGameSettings();
a = new A(_game_settings);
b = new B(_game_settings);
}
function _getting_non_immutable_value() public {
gas_consumed_1 = a._copy_settings();
console.log(
"Gas consumed for non-immutable variable: ",
gas_consumed_1
);
}
function _getting_immutable_value() public {
gas_consumed_2 = b._copy_settings();
console.log("Gas consumed for immutable variable: ", gas_consumed_2);
}
function test_net_gas_consumed() public {
_getting_non_immutable_value();
_getting_immutable_value();
net_gas_consumed = gas_consumed_1 - gas_consumed_2;
console.log("Net gas consumed: %s", net_gas_consumed);
}
}
contract A {
YeetGameSettings public gameSettings;
// Settings from YeetGameSettings
/// @notice see `YeetGameSettings` for more information
uint256 public YEET_TIME_SECONDS; // The time between yeets
uint256 public POT_DIVISION; // 1/xth of the pot eg, x%, 1/200 = 0.5%
uint256 public TAX_PER_YEET; // The tax per yeet in %
uint256 public TAX_TO_STAKERS; // The tax to stakers in %
uint256 public TAX_TO_PUBLIC_GOODS; // The tax to public goods in %
uint256 public TAX_TO_TREASURY; // The tax to treasury in %
uint256 public YEETBACK_PERCENTAGE; // The percentage of the pot that goes to yeetback
uint256 public COOLDOWN_TIME; // The time between rounds
uint256 public BOOSTRAP_PHASE_DURATION; // The time between rounds
uint256 public MINIMUM_YEET_POINT; // The minimum amount needed to yeet
constructor(YeetGameSettings _game_settings) {
gameSettings = _game_settings;
}
function copySettings() internal {
YEET_TIME_SECONDS = gameSettings.YEET_TIME_SECONDS();
POT_DIVISION = gameSettings.POT_DIVISION();
TAX_PER_YEET = gameSettings.TAX_PER_YEET();
TAX_TO_STAKERS = gameSettings.TAX_TO_STAKERS();
TAX_TO_PUBLIC_GOODS = gameSettings.TAX_TO_PUBLIC_GOODS();
TAX_TO_TREASURY = gameSettings.TAX_TO_TREASURY();
YEETBACK_PERCENTAGE = gameSettings.YEETBACK_PERCENTAGE();
COOLDOWN_TIME = gameSettings.COOLDOWN_TIME();
BOOSTRAP_PHASE_DURATION = gameSettings.BOOSTRAP_PHASE_DURATION();
MINIMUM_YEET_POINT = gameSettings.MINIMUM_YEET_POINT();
}
function _copy_settings() public returns (uint256 gas_consumed) {
uint256 gas_in;
uint256 gas_out;
gas_in = gasleft();
copySettings();
gas_out = gasleft();
gas_consumed = gas_in - gas_out;
}
}
contract B {
YeetGameSettings public immutable gameSettings;
// Settings from YeetGameSettings
/// @notice see `YeetGameSettings` for more information
uint256 public YEET_TIME_SECONDS; // The time between yeets
uint256 public POT_DIVISION; // 1/xth of the pot eg, x%, 1/200 = 0.5%
uint256 public TAX_PER_YEET; // The tax per yeet in %
uint256 public TAX_TO_STAKERS; // The tax to stakers in %
uint256 public TAX_TO_PUBLIC_GOODS; // The tax to public goods in %
uint256 public TAX_TO_TREASURY; // The tax to treasury in %
uint256 public YEETBACK_PERCENTAGE; // The percentage of the pot that goes to yeetback
uint256 public COOLDOWN_TIME; // The time between rounds
uint256 public BOOSTRAP_PHASE_DURATION; // The time between rounds
uint256 public MINIMUM_YEET_POINT; // The minimum amount needed to yeet
constructor(YeetGameSettings _game_settings) {
gameSettings = _game_settings;
}
function copySettings() internal {
YEET_TIME_SECONDS = gameSettings.YEET_TIME_SECONDS();
POT_DIVISION = gameSettings.POT_DIVISION();
TAX_PER_YEET = gameSettings.TAX_PER_YEET();
TAX_TO_STAKERS = gameSettings.TAX_TO_STAKERS();
TAX_TO_PUBLIC_GOODS = gameSettings.TAX_TO_PUBLIC_GOODS();
TAX_TO_TREASURY = gameSettings.TAX_TO_TREASURY();
YEETBACK_PERCENTAGE = gameSettings.YEETBACK_PERCENTAGE();
COOLDOWN_TIME = gameSettings.COOLDOWN_TIME();
BOOSTRAP_PHASE_DURATION = gameSettings.BOOSTRAP_PHASE_DURATION();
MINIMUM_YEET_POINT = gameSettings.MINIMUM_YEET_POINT();
}
function _copy_settings() public returns (uint256 gas_consumed) {
uint256 gas_in;
uint256 gas_out;
gas_in = gasleft();
copySettings();
gas_out = gasleft();
gas_consumed = gas_in - gas_out;
}
}
Was this helpful?