VTVL contest - 0xbepresent's results

Building no-code token management tools to empower web3 founders and investors, starting with token vesting.

General Information

Platform: Code4rena

Start Date: 20/09/2022

Pot Size: $30,000 USDC

Total HM: 12

Participants: 198

Period: 3 days

Judge: 0xean

Total Solo HM: 2

Id: 164

League: ETH

VTVL

Findings Distribution

Researcher Performance

Rank: 79/198

Findings: 3

Award: $28.69

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

0.7375 USDC - $0.74

Labels

bug
duplicate
2 (Med Risk)
sponsor confirmed

External Links

Lines of code

https://github.com/code-423n4/2022-09-vtvl/blob/f68b7f3e61/contracts/token/VariableSupplyERC20Token.sol#L40

Vulnerability details

Impact

If maxSupply was assigned different to 0, the mint function should limit the token creation. Comment in the code says:

@param maxSupply_ - What's the maximum supply. The contract won't allow minting over this amount. Set to 0 for no limit.

The problem is that even when the maxSupply was assigned different to 0, the restriction was ignored in the mint function because the function will reduce the mintableSupply until 0 and zero also means "no limit".

An unlimited supply will make the token inflationary, behavior that is not correct if the creator setting up the max supply

Proof of Concept

Tools used

VisualStudio/Python

Consider a standard implementation https://docs.openzeppelin.com/contracts/2.x/erc20-supply#fixed-supply

#0 - 0xean

2022-09-24T00:38:27Z

dupe of #3

Lines of code

https://github.com/code-423n4/2022-09-vtvl/blob/f68b7f3e61/contracts/AccessProtected.sol#L39

Vulnerability details

Impact

If a wrong/invalid address is given when calling setAdmin, some contract's functions could be lost access. While setAdmin checks for zero address, there is no validation of the new address being correct.

Also, there is no validation that the contract has at least one admin.

The design of the setAdmin function can block the contract's functions forever, so functions like "createClaim", "createClaimsBatch", "withdrawAdmin", "revokeClaim", "withdrawOtherToken", "setAdmin", "mint" would be locked.

contracts/AccessProtected.sol#L39 function setAdmin(address admin, bool isEnabled) public onlyAdmin {

Proof of Concept

  • Alice, the admin, executes setAdmin to an address which is accidentally invalid.
setAdmin(invalid_address, True)
  • Alice, who is giving up being admin, will invalidate her own access.
setAdmin(alice_address, False)
  • Now, nobody has access to critical functions.

Tools used

VisualStudio Code

Change the single-step admin assignation to a two-step process where the current Admin first approves a new address as a pendingAdmin. That pendingAdmin has to then claim the ownership in a separate transaction. An incorrectly set pendingAdmin can be reset by changing it again to the correct one who can then successfully claim it in the second step.

Also, in the admin assignation process make sure that the contract has at least one admin.

#0 - 0xean

2022-09-23T23:27:24Z

2 step process should be QA, but will mark as a duplicate of #469 re: the check to ensure the final admin doesn't remove themselves.

#1 - 0xean

2022-10-09T22:59:10Z

downgrading to QA per #496.

Awards

9.086 USDC - $9.09

Labels

bug
G (Gas Optimization)
edited-by-warden

External Links

1 - Public functions that are never called from within the contracts should be declared external to save gas

Insatance of this issue:

contracts/VTVLVesting.sol#L398 function withdrawAdmin(uint112 _amountRequested) public onlyAdmin {

2 - Unused imports

Import of unnecessary files costs deployment gas

contracts/VTVLVesting.sol#L6 import "@openzeppelin/contracts/access/Ownable.sol"; contracts/AccessProtected.sol#L5 import "@openzeppelin/contracts/access/Ownable.sol";

3 - Multiple require statements instead of &&

Require statements including conditions with the && operator can be broken down in multiple require statements to save gas.

contracts/VTVLVesting.sol#L344 require(_startTimestamps.length == length && _endTimestamps.length == length && _cliffReleaseTimestamps.length == length && _releaseIntervalsSecs.length == length && _linearVestAmounts.length == length && _cliffAmounts.length == length, "ARRAY_LENGTH_MISMATCH" );

4 - Unnecessary checked arithmetic in for loop

There is no risk that the loop counter can overflow, using solidity's unchecked block saves gas.

Instance of this issue:

contracts/VTVLVesting.sol#L353 for (uint256 i = 0; i < length; i++) {

Unchecked implementation example:

for (uint256 i; i < 10;) { j++; unchecked { ++i; } }

5 - x =+ y costs more gas than x = x + y for state variables

Instance of this issue:

contracts/VTVLVesting.sol#L302 numTokensReservedForVesting += allocatedAmount; // track the allocated amount

6 - Amount is not validated

Implement the required statement that amount should be greater than 0 in order to save gas.

Instances of this issue:

contracts/token/VariableSupplyERC20Token.sol#L36 function mint(address account, uint256 amount) public onlyAdmin {
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