Althea Liquid Infrastructure - zhaojohnson's results

Liquid Infrastructure.

General Information

Platform: Code4rena

Start Date: 13/02/2024

Pot Size: $24,500 USDC

Total HM: 5

Participants: 84

Period: 6 days

Judge: 0xA5DF

Id: 331

League: ETH

Althea

Findings Distribution

Researcher Performance

Rank: 27/84

Findings: 4

Award: $121.23

🌟 Selected for report: 0

🚀 Solo Findings: 0

Judge has assessed an item in Issue #308 as 3 risk. The relevant finding follows:

  • [L03] Addresses can be pushed into holders even if balanceOf(addr) is zero. For example, Alice sends 0 LiquidInfrastructureERC20 Token to Bob. Although Bob owns 0 Token, Bob addr can still be added in holders.
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual override {
        require(!LockedForDistribution, "distribution in progress");
        if (!(to == address(0))) {
            require(
                isApprovedHolder(to),
                "receiver not approved to hold the token"
            );
        }
        if (from == address(0) || to == address(0)) {
            _beforeMintOrBurn();
        }
        bool exists = (this.balanceOf(to) != 0);
        if (!exists) {
            holders.push(to);
        }
    }

#0 - c4-judge

2024-03-08T14:06:34Z

0xA5DF marked the issue as duplicate of #77

#1 - c4-judge

2024-03-08T14:06:38Z

0xA5DF marked the issue as satisfactory

#2 - c4-judge

2024-03-08T14:06:42Z

0xA5DF marked the issue as partial-50

Awards

80.5583 USDC - $80.56

Labels

bug
2 (Med Risk)
downgraded by judge
satisfactory
duplicate-703

External Links

Lines of code

https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L116-L119 https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L214-L231

Vulnerability details

Impact

revenue tokens cannot be distributed completely because of disapproved holders

Proof of Concept

In current implementation, disapproved holders cannot receive any more underlying ERC20 tokens. And they may hold some LiquidInfrastructureERC20 tokens, which may influence erc20EntitlementPerUnit calculation.

For example:

  • There are three holders: Alice(10 Tokens), Bob(10 Tokens), Cathy(10 Tokens)
  • In normal case, all three holders are approved holders and everyone will get some profits.
  • If Bob and Cathy are disapproved, Alice, as the only approved holder, she should earn all revenue tokens. However, in the calculation of entitlement, we use the totalSupply(), so Alice can only get 1/3 rewards.
    uint256 supply = this.totalSupply();
    for (uint i = 0; i < distributableERC20s.length; i++) {
        uint256 balance = IERC20(distributableERC20s[i]).balanceOf(
            address(this)
        );
        uint256 entitlement = balance / supply;
        erc20EntitlementPerUnit.push(entitlement);
    }

Tools Used

Manual

We need to add one state to monitor activeSupply for all approved holders.

Assessed type

Math

#0 - c4-pre-sort

2024-02-21T02:38:05Z

0xRobocop marked the issue as duplicate of #703

#1 - c4-judge

2024-03-04T14:37:53Z

0xA5DF marked the issue as satisfactory

#2 - c4-judge

2024-03-08T15:15:56Z

0xA5DF changed the severity to 2 (Med Risk)

Awards

25.7286 USDC - $25.73

Labels

bug
2 (Med Risk)
downgraded by judge
satisfactory
duplicate-87

External Links

Lines of code

https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L441-L445 https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L257-L281 https://github.com/code-423n4/2024-02-althea-liquid-infrastructure/blob/bd6ee47162368e1999a0a5b8b17b701347cf9a7d/liquid-infrastructure/contracts/LiquidInfrastructureERC20.sol#L214-L231

Vulnerability details

Impact

owner can call setDistributableERC20s in the distribution, which may cause dos

Proof of Concept

In the start of distribution, system will calculate erc20EntitlementPerUnit based on current distributableERC20s. If owner changes distributableERC20s in the process of distribution, token distribution may be wrong.

For example,

  • Current distribution ERC20s are [USDC]
  • Alice starts one distribute(x) and now length of erc20EntitlementPerUnit is 1
  • Owner calls setDistributableERC20s() to update ERC20s from [USDC] to [USDC, USDT]
  • Anyone want to go on finishing distribute(), distribute() will revert because of missing erc20EntitlementPerUnit[1]
                for (uint j = 0; j < distributableERC20s.length; j++) {
                    IERC20 toDistribute = IERC20(distributableERC20s[j]);
                    uint256 entitlement = erc20EntitlementPerUnit[j] *
                        this.balanceOf(recipient);
                    if (toDistribute.transfer(recipient, entitlement)) {
                        receipts[j] = entitlement;
                    }
                }

Tools Used

Manual

Disable setDistributableERC20s() in the process of distribute

Assessed type

DoS

#0 - c4-pre-sort

2024-02-21T02:40:46Z

0xRobocop marked the issue as duplicate of #260

#1 - c4-judge

2024-03-04T15:30:50Z

0xA5DF marked the issue as satisfactory

#2 - c4-judge

2024-03-08T15:26:19Z

0xA5DF changed the severity to 2 (Med Risk)

Findings Information

Labels

bug
grade-b
high quality report
QA (Quality Assurance)
sponsor acknowledged
Q-07

Awards

11.348 USDC - $11.35

External Links

  • [L01] invalid require() in function releaseManagedNFT()

    • Need to add some conditions in require() to check whether this is valid release or not.
      function releaseManagedNFT(
          address nftContract,
          address to
      ) public onlyOwner nonReentrant {
          LiquidInfrastructureNFT nft = LiquidInfrastructureNFT(nftContract);
          nft.transferFrom(address(this), to, nft.AccountId());
    
          // Remove the released NFT from the collection
          for (uint i = 0; i < ManagedNFTs.length; i++) {
              address managed = ManagedNFTs[i];
              if (managed == nftContract) {
                  // Delete by copying in the last element and then pop the end
                  ManagedNFTs[i] = ManagedNFTs[ManagedNFTs.length - 1];
                  ManagedNFTs.pop();
                  break;
              }
          }
          // By this point the NFT should have been found and removed from ManagedNFTs
          require(true, "unable to find released NFT in ManagedNFTs");
    
          emit ReleaseManagedNFT(nftContract, to);
      }
  • [L02] Add some check in constructor about _managedNFTs. In Function addManagedNFT(), if we want to add one managedNFT, we have one check to make sure the ownership of NFT. In order to the consistency, we'd better to add similar check in constructor()

    constructor(
        string memory _name,
        string memory _symbol,
        address[] memory _managedNFTs,
        address[] memory _approvedHolders,
        uint256 _minDistributionPeriod,
        address[] memory _distributableErc20s
    ) ERC20(_name, _symbol) Ownable() {
        //@johnson,[L] in addManagedNFT, when add one NFT, we need to check ownership, suggest do the same check here
        ManagedNFTs = _managedNFTs;
  • [L03] Addresses can be pushed into holders even if balanceOf(addr) is zero. For example, Alice sends 0 LiquidInfrastructureERC20 Token to Bob. Although Bob owns 0 Token, Bob addr can still be added in holders.
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual override {
        require(!LockedForDistribution, "distribution in progress");
        if (!(to == address(0))) {
            require(
                isApprovedHolder(to),
                "receiver not approved to hold the token"
            );
        }
        if (from == address(0) || to == address(0)) {
            _beforeMintOrBurn();
        }
        bool exists = (this.balanceOf(to) != 0);
        if (!exists) {
            holders.push(to);
        }
    }

#0 - c4-pre-sort

2024-02-22T16:57:24Z

0xRobocop marked the issue as high quality report

#1 - ChristianBorst

2024-03-02T02:29:10Z

L-01

Acknowledged: This line was left in by mistake.

L-02

Confirmed. I believe this is also a duplicate in another QA.

L-03

Confirmed. I believe this is also a duplicate somewhere else.

#2 - c4-sponsor

2024-03-02T02:29:16Z

ChristianBorst (sponsor) acknowledged

#3 - 0xA5DF

2024-03-08T11:16:22Z

+L from #90 +L from #288

#4 - 0xA5DF

2024-03-08T14:06:51Z

4L +L from #286 =5L

#5 - c4-judge

2024-03-08T14:27:30Z

0xA5DF marked the issue as grade-c

#6 - c4-judge

2024-03-09T08:04:58Z

0xA5DF marked the issue as grade-b

#7 - c4-judge

2024-03-09T08:32:41Z

0xA5DF removed the grade

#8 - c4-judge

2024-03-09T09:38:20Z

0xA5DF 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