58719 sc insight insight gas optimization save gas by using the cached fee amount in burn and repay in alchemist sol

Submitted on Nov 4th 2025 at 08:45:35 UTC by @chief_hunter888 for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58719

  • Report Type: Smart Contract

  • Report severity: Insight

  • Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistV3.sol

  • Impacts:

Description

Brief Summary:

The current burn() implementation in AlchemistV3.sol does not cache the fee amount; instead, it recalculates it three separate times throughout the function. This redundancy increases gas consumption unnecessarily. Similarly, the repay() function calculates and stores the fee amount, but does not use the cached amount and recalculates it multiple times.

We recommend computing the fee once, caching it in a local uint256 variable, and reusing that value wherever needed. This simple optimization reduces redundant arithmetic operations and improves overall execution efficiency.

As Table 1 illustrates, this change yields tangible savings of approximately 45,000–113,000 gas units per successful burn() transaction. Similarly, Table 2 illustrates gas savings of about 1,300-65,000 gas units. Naturally, these savings do not apply to reverted transactions, as they never reach this computation.

Given the central role of AlchemistV3.sol as the core protocol contract, optimizing frequently used functions like burn() offers meaningful long-term benefits - improving performance, lowering transaction costs, and enhancing the protocol’s operational efficiency.

Table 1:

Test Name
Without Caching (gas)
With Caching (gas)
Gas Saved

testBurn()

830,776

786,048

44,728

testBurnNoLimit()

2,213,896

2,145,395

68,501

testBurnSameBlock()

746,431

746,431

0

testBurnWithEarmarkedDebt()

2,873,567

2,760,338

113,229

testBurnWithEarmarkedDebtFullyEarmarked()

2,094,367

2,021,995

72,372

testBurnWithFee()

884,800

840,072

44,728

testBurnZeroAmount()

744,652

744,652

0

testBurnZeroIdRevert()

745,248

745,248

0

testBurn_variable_burn_amounts(uint256) (μ gas)

1,143,370

1,097,548

45,822

testBurn_variable_burn_amounts(uint256) (~ gas)

1,195,836

1,151,108

44,728

Table 2:

Test Name
Without Caching (gas)
With Caching (gas)
Gas Saved

testRepayInvalidIdRevert(uint256) (μ gas)

718,902

718,902

0

testRepayInvalidIdRevert(uint256) (~ gas)

718,902

718,902

0

testRepaySameBlock()

719,774

719,774

0

testRepayUnearmarkedDebtOnly()

936,689

935,391

1,298

testRepayUnearmarkedDebtOnly_Variable_Amount(uint256) (μ)

924,633

918,543

6,090

testRepayUnearmarkedDebtOnly_Variable_Amount(uint256) (~)

916,299

915,001

1,298

testRepayWithDifferentPrice()

1,029,090

1,028,052

1,038

testRepayWithEarmarkedDebt()

2,150,419

2,100,873

49,546

testRepayWithEarmarkedDebtPartial()

2,263,798

2,221,994

41,804

testRepayWithEarmarkedDebtWithFee()

2,229,805

2,180,259

49,546

testRepayWithEarmarkedDebt_MultiplePoke_Broken()

2,322,198

2,257,168

65,030

testRepayZeroAmount()

713,995

713,995

0

testRepayZeroTokenIdRevert()

713,865

713,865

0

Vulnerability Details

The inefficient code for burn and repay can be found in theAlchemistV3.sol in the range of lines line 483-486 and lines 534-542. In case of burn convertDebtTokensToYield(credit) * protocolFee / BPS; is unnecessarily calculated 3 times instead of once and stored as a variable. Similarly, for repay, the feeAmount is calculated 4 times and stored the first time calculated, but the variable is never used creditToYield * protocolFee / BPS we recommend to use the cached variable instead.

Experimental Setup POC Details

Step 1: Run burn test with the code as is to extract initial reference values:

Similarly for repay it would be,

Before:

Sample output for burn

Step 2: Modify the AlchemistV3.sol to use caching to save gas and improve legibility:

E.g. modify the burn() function to look like this

For repay() it would be:

Step 3: Run tests again:

Recommendation

Make the recommended changes to the burn and repay function of the AlchemistV3.sol contract and review the code base for further instances where substantial amounts of gas could be saved and increase usability of the protocol.

Implementing these optimizations in such a core contract not only yields tangible gas savings, that could be obtained via very easy and quick fixes, but also reinforces the protocol’s long-term efficiency, maintainability, code clarity, and trustworthiness. All key qualities for a system at the heart of user interactions.

Proof of Concept

Proof of Concept

AlchemistV3.sol with gas optimized burn() and repay() function. Please follow the gas optimization outlined in the description above, if you doubt the impact on gas savings.

Was this helpful?