Lybra Finance - 0xAnah's results

A protocol building the first interest-bearing omnichain stablecoin backed by LSD.

General Information

Platform: Code4rena

Start Date: 23/06/2023

Pot Size: $60,500 USDC

Total HM: 31

Participants: 132

Period: 10 days

Judge: 0xean

Total Solo HM: 10

Id: 254

League: ETH

Lybra Finance

Findings Distribution

Researcher Performance

Rank: 75/132

Findings: 1

Award: $80.43

Gas:
grade-a

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

80.434 USDC - $80.43

Labels

bug
G (Gas Optimization)
grade-a
high quality report
sponsor acknowledged
G-12

External Links

GAS OPTIMIZATIONS

[G-01] Unused Imports

The following files were imported but were not used. These files would costs gas during deployment and is a bad coding practice .

[G-02] Use bit shifting for multiplication by 2

[G-03] Sort Solidity operations using short-circuit mode

Short-circuiting is a solidity contract development model that uses OR/AND logic to sequence different cost operations. It puts low gas cost operations in the front and high gas cost operations in the back, so that if the front is low If the cost operation is feasible, you can skip (short-circuit) the subsequent high-cost Ethereum virtual machine operation.

//f(x) is a low gas cost operation //g(y) is a high gas cost operation

//Sort operations with different gas costs as follows f(x) || g(y) f(x) && g(y)

The following instances could be restructured to use short-circuit mode

[G-04] Reorder the require statements to have the less gas consuming before the expensive one

Checks that involve constants should come before checks that involve state variables, function calls, and calculations. In scenarios where this cheap checks would fail the function is able to revert before wasting alot of gas in a function that may ultimately revert in the unhappy case.

Proof of concept

consider the following scenario:

function mint(address onBehalfOf, uint256 amount) external virtual {
        require(onBehalfOf != address(0), "TZA");
        require(amount > 0, "ZA");
        _mintPeUSD(msg.sender, onBehalfOf, amount, getAssetPrice());
}

The require(amount > 0) statement cost lesser gas than the require(onBehalfOf != address(0)) so in scenarios where the require(amount > 0) would fail the function would consume more a lot more gas than if it were re-arranged. so the above code snippet could be re-written as:

function mint(address onBehalfOf, uint256 amount) external virtual {
        require(amount > 0, "ZA");
        require(onBehalfOf != address(0), "TZA");
        _mintPeUSD(msg.sender, onBehalfOf, amount, getAssetPrice());
}

(7 Instances)

[G-05] Storage variables should be cached in stack variables rather than re-reading them from storage

In the scenarios below storage variables are read in a conditional statement. Depending on if the conditional statement results to a true or false the state variable could be re-read from storage thereby causing a Gwarmaccess which cost 100 gas. You should consider caching the storage variable before the conditionals this replaces each Gwarmaccess (100 gas) with a much cheaper stack read.

Proof of concept

if the condition results to true the storage value time2fullRedemption[msg.sender] would be read twice.

if (time2fullRedemption[msg.sender] > block.timestamp) {
            total += unstakeRatio[msg.sender] * (time2fullRedemption[msg.sender] - block.timestamp);
}

This can be done instead

uint time2fullR = time2fullRedemption[msg.sender];
if (time2fullR > block.timestamp) {
            total += unstakeRatio[msg.sender] * (time2fullR - block.timestamp);
}

Depending on if the conditionals result to true (13 instances)

Depending on if the conditionals resut to false (3 instances)

The below instances do not involve conditionals but the storage variables should be cached

[G-06] Make storage variable constant

storage variable constant whose values do not change should be constant

[G-07] Using function call to access a global variables is more expensive than using the actual variable

Using function calls to access a global variables is more expensive than using the actual variable use msg.sender instead of calling _msgSender().

Proof of concept

consider the code below:

contract myERC20 is ERC20{
    
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply = 1000;

    string private _name;
    string private _symbol;

    constructor() ERC20("MyERC20", "MyErc"){
        _mint(msg.sender, 200);
    }

    

    function transfer2(address to, uint256 amount) public virtual returns (bool) {
        address owner = msg.sender;  // msg.sender 
        _transfer(owner, to, amount);
        return true;
    }

    function warmStorage() public{
        transfer(0xfa458421F9E92B677D1724e2A0F0bF378940333e, 2);
    }
    
}

the test script:

const {ethers} = require('hardhat');
const assert = require('assert');


describe("Gas Tests", async function() {
    it("Comparing Transfer functions", async function(){

        const myErcFactory = await ethers.getContractFactory("myERC20");
        const myErc20 = await myErcFactory.deploy();

        await myErc20.warmStorage();
        await myErc20.transfer2("0xfa458421F9E92B677D1724e2A0F0bF378940333e", 10);
        await myErc20.transfer("0xfa458421F9E92B677D1724e2A0F0bF378940333e", 10); //inherited transfer function uses _msgSender() 
        
        
    })
})

the test result:

·············|···············|··············|·············|·············|···············|··············
|  Contract  ·  Method       ·  Min         ·  Max        ·  Avg        ·  # calls      ·  usd (avg)  │
·············|···············|··············|·············|·············|···············|··············
|  myERC20   ·  transfer     ·           -  ·          -  ·      35017  ·            1  ·          -  │
·············|···············|··············|·············|·············|···············|··············
|  myERC20   ·  transfer2    ·           -  ·          -  ·      34983  ·            1  ·          -  │
·············|···············|··············|·············|·············|···············|··············
|  myERC20   ·  warmStorage  ·           -  ·          -  ·      51027  ·            1  ·          -  │
·············|···············|··············|·············|·············|···············|··············
|  Deployments               ·                                          ·  % of limit   ·             │
·····························|··············|·············|·············|···············|··············
|  myERC20                   ·           -  ·          -  ·    1225730  ·        4.1 %  ·          -  │
·----------------------------|--------------|-------------|-------------|---------------|-------------·

transfer() = 35017; transfer2() = 34983 difference = 34 gas units

16 Instances

total gas saved = 34 * 16 = 544 gas units

#0 - c4-pre-sort

2023-07-27T21:21:28Z

JeffCX marked the issue as high quality report

#1 - c4-judge

2023-07-27T23:42:15Z

0xean marked the issue as grade-a

#2 - c4-sponsor

2023-07-29T09:18:16Z

LybraFinance marked the issue as sponsor acknowledged

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