Nested Finance contest - WatchPug's results

The one-stop Defi app to build, manage and monetize your portfolio.

General Information

Platform: Code4rena

Start Date: 11/11/2021

Pot Size: $50,000 USDC

Total HM: 9

Participants: 27

Period: 7 days

Judge: alcueca

Total Solo HM: 5

Id: 53

League: ETH

Nested Finance

Findings Distribution

Researcher Performance

Rank: 3/27

Findings: 5

Award: $6,092.37

🌟 Selected for report: 7

🚀 Solo Findings: 1

Findings Information

🌟 Selected for report: GreyArt

Also found by: WatchPug

Labels

bug
duplicate
2 (Med Risk)

Awards

1169.9215 USDC - $1,169.92

External Links

Handle

WatchPug

Vulnerability details

When an operator is removed and rebuildCache() is called, isResolverCached() should return true. It returns false in the current implemenbtation.

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/MixinOperatorResolver.sol#L45-L56

/// @notice Check the state of addressCache
function isResolverCached() external view returns (bool) {
    bytes32[] memory requiredAddresses = resolverAddressesRequired();
    for (uint256 i = 0; i < requiredAddresses.length; i++) {
        bytes32 name = requiredAddresses[i];
        // false if our cache is invalid or if the resolver doesn't have the required address
        if (resolver.getAddress(name) != addressCache[name] || addressCache[name] == address(0)) {
            return false;
        }
    }
    return true;
}

Beacuse when an operator is removed, requiredAddresses will includes empty items, so that addressCache[name] will be address(0), and return false.

Recommendation

Change to:

/// @notice Check the state of addressCache
function isResolverCached() external view returns (bool) {
    bytes32[] memory requiredAddresses = resolverAddressesRequired();
    for (uint256 i = 0; i < requiredAddresses.length; i++) {
        bytes32 name = requiredAddresses[i];
        // false if our cache is invalid or if the resolver doesn't have the required address
        if (resolver.getAddress(name) != addressCache[name]) {
            return false;
        }
    }
    return true;
}

#0 - maximebrugel

2021-11-22T21:38:35Z

Same issue as #139 but not pointing removeOperator. The issue will be fixed in #58, so i see it as a replicate.

#1 - alcueca

2021-12-03T11:07:52Z

Duplicate of #139, then

Findings Information

🌟 Selected for report: WatchPug

Labels

bug
2 (Med Risk)
sponsor disputed

Awards

2599.8256 USDC - $2,599.83

External Links

Handle

WatchPug

Vulnerability details

When executing orders, the actual amountSpent + feesAmount can be lower than _inputTokenAmount, the unspent amount should be returned to the user.

However, in the current implementation, the unspent amount will be taken as part of the fee.

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/NestedFactory.sol#L285-L309

function _submitInOrders(
    uint256 _nftId,
    IERC20 _inputToken,
    uint256 _inputTokenAmount,
    Order[] calldata _orders,
    bool _reserved,
    bool _fromReserve
) private returns (uint256 feesAmount, IERC20 tokenSold) {
    _inputToken = _transferInputTokens(_nftId, _inputToken, _inputTokenAmount, _fromReserve);
    uint256 amountSpent;
    for (uint256 i = 0; i < _orders.length; i++) {
        amountSpent += _submitOrder(address(_inputToken), _orders[i].token, _nftId, _orders[i], _reserved);
    }
    feesAmount = _calculateFees(_msgSender(), amountSpent);
    assert(amountSpent <= _inputTokenAmount - feesAmount); // overspent

    // If input is from the reserve, update the records
    if (_fromReserve) {
        _decreaseHoldingAmount(_nftId, address(_inputToken), _inputTokenAmount);
    }

    _handleUnderSpending(_inputTokenAmount - feesAmount, amountSpent, _inputToken);

    tokenSold = _inputToken;
}

Recommendation

Change to:

function _submitInOrders(
    uint256 _nftId,
    IERC20 _inputToken,
    uint256 _inputTokenAmount,
    Order[] calldata _orders,
    bool _reserved,
    bool _fromReserve
) private returns (uint256 feesAmount, IERC20 tokenSold) {
    _inputToken = _transferInputTokens(_nftId, _inputToken, _inputTokenAmount, _fromReserve);
    uint256 amountSpent;
    for (uint256 i = 0; i < _orders.length; i++) {
        amountSpent += _submitOrder(address(_inputToken), _orders[i].token, _nftId, _orders[i], _reserved);
    }
    feesAmount = _calculateFees(_msgSender(), amountSpent);
    assert(amountSpent <= _inputTokenAmount - feesAmount); // overspent

    // If input is from the reserve, update the records
    if (_fromReserve) {
        _decreaseHoldingAmount(_nftId, address(_inputToken), amountSpent+feesAmount);
    }

    ExchangeHelpers.setMaxAllowance(_token, address(feeSplitter));
    feeSplitter.sendFees(_token, feesAmount);

    if (_inputTokenAmount > amountSpent + feesAmount) {
        _inputToken.transfer(_fromReserve ? address(reserve) : _msgSender(), _inputTokenAmount - amountSpent - feesAmount);
    }

    tokenSold = _inputToken;
}

#0 - adrien-supizet

2021-11-23T14:48:50Z

Rationale

We don't consider this an issue as this was done on purpose. We wanted to treat the positive slippage as regular fees. Most times, the dust of positive slippage will cost more to the user if they are transferred rather than passed along fees.

We made it possible for us to retrieve overcharged amounts in case of mistakes to give them back to users.

New behavior

But for the sake of transparency, and in the spirit of DeFi, we have reviewed the business model of the protocol and decided to transfer back any amount that was unspent and which exceeds the 1% fixed fee.

Resolution

Selecting "disputed" for now but I'll let a judge review if this should be included in the report, and if the severity was correct, as we were able to give back tokens to users if they made a mistake calling the protocol.

#1 - alcueca

2021-12-03T15:23:05Z

If stated in the README or comments, the issue would be invalid. The sponsor can choose whichever behaviour suits their business model. When not stated in the README, any asset loss to users or protocol is a valid issue. User losses are expected to be a severity 3, but in this case, and given that those losses are inferior to the gas, the issue is downgraded to severity 2.

In the future, please state in the code any asset losses that are accepted by the protocol.

Findings Information

🌟 Selected for report: GreyArt

Also found by: WatchPug

Labels

bug
duplicate
2 (Med Risk)

Awards

1169.9215 USDC - $1,169.92

External Links

Handle

WatchPug

Vulnerability details

Given that importOperators() will change operators, and addressCache will not be updated until rebuildCaches() is called separately.

To ensure addressCache is up-to-date, importOperators() should be run atomically with rebuildCaches().

Recommendation

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/OperatorResolver.sol#L42-L51

Consider changing importOperators() to:

function importOperators(bytes32[] calldata names, address[] calldata operators, MixinOperatorResolver[] calldata destinations) external override onlyOwner {
    require(names.length == operators.length, "OperatorResolver::importOperators: Input lengths must match");

    for (uint256 i = 0; i < names.length; i++) {
        bytes32 name = names[i];
        address operator = operators[i];
        operators[name] = operator;
        emit OperatorImported(name, operator);
    }
    _rebuildCaches(destinations);
}

#0 - maximebrugel

2021-11-23T16:30:59Z

Maybe we want to import the Operators (in multiple transactions) without rebuilding straight away. So we can give MixinOperatorResolver[] calldata destinations as an empty array.

#1 - alcueca

2021-12-03T10:22:51Z

Duplicate of #217

Findings Information

🌟 Selected for report: PierrickGT

Also found by: WatchPug, gzeon, harleythedog, pants

Labels

bug
duplicate
1 (Low Risk)

Awards

113.7164 USDC - $113.72

External Links

#0 - maximebrugel

2021-11-18T08:49:51Z

Duplicated : #50

Findings Information

🌟 Selected for report: elprofesor

Also found by: WatchPug, cmichel, loop, palina, pauliax

Labels

bug
duplicate
1 (Low Risk)

Awards

85.2873 USDC - $85.29

External Links

Handle

WatchPug

Vulnerability details

Calling ERC20.transfer() without handling the returned value is unsafe.

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/NestedFactory.sol#L271-L273

function unlockTokens(IERC20 _token) external override onlyOwner {
    _token.transfer(owner(), _token.balanceOf(address(this)));
}

Recommendation

Consider using OpenZeppelin's SafeERC20 library with safe versions of transfer functions.

#0 - adrien-supizet

2021-11-17T15:11:14Z

Duplicated : #78

Findings Information

🌟 Selected for report: WatchPug

Also found by: PierrickGT, hack3r-0m, hyh, loop, palina, ye0lde

Labels

bug
1 (Low Risk)
sponsor confirmed

Awards

65.793 USDC - $65.79

External Links

Handle

WatchPug

Vulnerability details

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/NestedFactory.sol#L553-L559

/// @dev Calculate the fees for a specific user and amount (1%)
/// @param _user The user address
/// @param _amount The amount
/// @return The fees amount
function _calculateFees(address _user, uint256 _amount) private view returns (uint256) {
    return _amount / 100;
}

The function _calculateFees() is a rather simple function, replacing it with inline expression _amount / 100 can save some gas.

#0 - maximebrugel

2021-11-17T15:07:07Z

Also, making this function inline will remove the unused parameter _user. Issues pointing this unused parameter (or change to pure) will be linked here.

#1 - alcueca

2021-12-03T10:14:50Z

Also comments are wrong (#194)

Findings Information

🌟 Selected for report: WatchPug

Also found by: fatima_naz

Labels

bug
1 (Low Risk)

Awards

389.9738 USDC - $389.97

External Links

Handle

WatchPug

Vulnerability details

Contracts use assert() instead of require() in multiple places.

Per to Solidity’s documentation:

"Assert should only be used to test for internal errors, and to check invariants. Properly functioning code should never create a Panic, not even on invalid external input. If this happens, then there is a bug in your contract which you should fix. Language analysis tools can evaluate your contract to identify the conditions and function calls which will cause a Panic.”

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/NestedFactory.sol#L299-L299

assert(amountSpent <= _inputTokenAmount - feesAmount); // overspent

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/NestedFactory.sol#L341-L341

assert(amountSpent <= _inputTokenAmounts[i]);

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/operators/ZeroEx/ZeroExOperator.sol#L42-L43

assert(amountBought > 0);
assert(amountSold > 0);

Recommendation

Change to require().

#0 - alcueca

2021-12-03T10:18:36Z

I'm upgrading this to Low Risk because there is an actual loss to the users, and the potential for a massive headache for the protocol operators.

Findings Information

🌟 Selected for report: 0x0x0x

Also found by: WatchPug, defsec, gzeon, pants, pauliax, xYrYuYx

Labels

bug
duplicate
G (Gas Optimization)

Awards

8.0487 USDC - $8.05

External Links

Handle

WatchPug

Vulnerability details

Reading array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack.

Caching the array length in the stack saves around 3 gas per iteration.

Instances include:

#0 - maximebrugel

2021-11-17T15:20:32Z

Duplicated : #7

Findings Information

🌟 Selected for report: 0xngndev

Also found by: GiveMeTestEther, WatchPug, gzeon, pauliax, ye0lde

Labels

bug
duplicate
G (Gas Optimization)

Awards

10.4335 USDC - $10.43

External Links

Handle

WatchPug

Vulnerability details

Every reason string takes at least 32 bytes.

Use short reason strings that fits in 32 bytes or it will become more expensive.

Instances include:

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/NestedFactory.sol#L84-L84

require(i > 0, "NestedFactory::removeOperator: Cant remove non-existent operator");

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/NestedFactory.sol#L90-L90

require(address(reserve) == address(0), "NestedFactory::setReserve: Reserve is immutable");

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/NestedFactory.sol#L97-L97

require(address(_feeSplitter) != address(0), "NestedFactory::setFeeSplitter: Invalid feeSplitter address");

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/NestedFactory.sol#L109-L109

require(_orders.length > 0, "NestedFactory::create: Missing orders");

#0 - adrien-supizet

2021-11-18T12:43:41Z

duplicate #14

Findings Information

🌟 Selected for report: GiveMeTestEther

Also found by: WatchPug

Labels

bug
duplicate
G (Gas Optimization)

Awards

47.7068 USDC - $47.71

External Links

Handle

WatchPug

Vulnerability details

In FeeSplitter.sol#setRoyaltiesWeight(), totalWeights is being written 2 times. Combing them into one storage write can save gas.

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/FeeSplitter.sol#L94-L97

function setRoyaltiesWeight(uint256 _weight) public onlyOwner {
    totalWeights -= royaltiesWeight;
    royaltiesWeight = _weight;
    totalWeights += _weight;
}

Change to:

function setRoyaltiesWeight(uint256 _weight) public onlyOwner {
    totalWeights = totalWeights - royaltiesWeight + _weight;
    royaltiesWeight = _weight;
}

#0 - maximebrugel

2021-11-17T16:36:32Z

Duplicated : #28

Findings Information

🌟 Selected for report: GiveMeTestEther

Also found by: WatchPug, defsec, pauliax

Labels

bug
duplicate
G (Gas Optimization)

Awards

19.3212 USDC - $19.32

External Links

Handle

WatchPug

Vulnerability details

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/FeeSplitter.sol#L143-L146

function sendFees(IERC20 _token, uint256 _amount) external nonReentrant {
    uint256 weights = totalWeights - royaltiesWeight;
    _sendFees(_token, _amount, weights);
}

The local variable weights is used only once. Making the expression inline can save gas.

Recommendation

Change to:

function sendFees(IERC20 _token, uint256 _amount) external nonReentrant {
    _sendFees(_token, _amount, totalWeights - royaltiesWeight);
}

#0 - adrien-supizet

2021-11-22T09:41:00Z

duplicate #46

Findings Information

🌟 Selected for report: pmerkleplant

Also found by: WatchPug, cmichel, nathaniel, palina

Labels

bug
duplicate
G (Gas Optimization)

Awards

13.9113 USDC - $13.91

External Links

Handle

WatchPug

Vulnerability details

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/NestedBuybacker.sol#L100-L100

uint256 balance = _sellToken.balanceOf(address(this));

balance is unused. Removing this line can save gas.

#0 - maximebrugel

2021-11-22T13:33:35Z

Duplicated : #65

Findings Information

🌟 Selected for report: WatchPug

Labels

bug
G (Gas Optimization)
sponsor confirmed

Awards

106.015 USDC - $106.02

External Links

Handle

WatchPug

Vulnerability details

releaseToken() has nonReentrant modifier, making releaseTokens() to set storage _status multiple times in the for loop.

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/FeeSplitter.sol#L116-L129

function releaseToken(IERC20 _token) public nonReentrant {
    uint256 amount = _releaseToken(_msgSender(), _token);
    _token.safeTransfer(_msgSender(), amount);
    emit PaymentReleased(_msgSender(), address(_token), amount);
}

/// @notice Call releaseToken() for multiple tokens
/// @param _tokens ERC20 tokens to release
function releaseTokens(IERC20[] memory _tokens) external {
    for (uint256 i = 0; i < _tokens.length; i++) {
        releaseToken(_tokens[i]);
    }
}

Recommendation

Change to:

function releaseToken(IERC20 _token) public nonReentrant {
    _releaseTokenAndTransfer(_token);
}

/// @notice Call releaseToken() for multiple tokens
/// @param _tokens ERC20 tokens to release
function releaseTokens(IERC20[] memory _tokens) external {
    for (uint256 i = 0; i < _tokens.length; i++) {
        _releaseTokenAndTransfer(_tokens[i]);
    }
}

function _releaseTokenAndTransfer(IERC20 _token) private {
    uint256 amount = _releaseToken(_msgSender(), _token);
    _token.safeTransfer(_msgSender(), amount);
    emit PaymentReleased(_msgSender(), address(_token), amount);
}

#0 - adrien-supizet

2021-11-19T16:37:36Z

This is true, maybe the solution be to:

  • declare releaseToken as internal
  • Add the reentrancy guard to releaseTokens
  • only call releaseTokens from the outside world (with only 1 token in the array if needed)

Findings Information

🌟 Selected for report: WatchPug

Labels

bug
G (Gas Optimization)

Awards

106.015 USDC - $106.02

External Links

Handle

WatchPug

Vulnerability details

The for loop in rebuildCache() will cost more gas if removeOperator() wont decrease operators.length.

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/MixinOperatorResolver.sol#L29-L43

function rebuildCache() public {
    bytes32[] memory requiredAddresses = resolverAddressesRequired();
    // The resolver must call this function whenever it updates its state
    for (uint256 i = 0; i < requiredAddresses.length; i++) {
        bytes32 name = requiredAddresses[i];
        // Note: can only be invoked once the resolver has all the targets needed added
        address destination = resolver.getAddress(name);
        if (destination != address(0)) {
            addressCache[name] = destination;
        } else {
            delete addressCache[name];
        }
        emit CacheUpdated(name, destination);
    }
}

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/NestedFactory.sol#L78-L86

/// @inheritdoc INestedFactory
function removeOperator(bytes32 operator) external override onlyOwner {
    uint256 i = 0;
    while (operators[i] != operator) {
        i++;
    }
    require(i > 0, "NestedFactory::removeOperator: Cant remove non-existent operator");
    delete operators[i];
}

Recommendation

Change to:

/// @inheritdoc INestedFactory
function removeOperator(bytes32 operator) external override onlyOwner {
    uint256 length = operators.length;
    for (uint256 i = 0; i < length; i++) {
        if (operators[i] == operator) {
            operators[i] = operators[length - 1];
            operators.pop();
            return;
        }
    }
    revert("NestedFactory::removeOperator: Cant remove non-existent operator");
}

#0 - maximebrugel

2021-11-17T15:28:25Z

Duplicated : #58

#1 - alcueca

2021-12-03T11:07:12Z

Part of #220

#2 - alcueca

2021-12-03T11:12:20Z

Actually, even if caused by the same code, this is a different issue.

Findings Information

🌟 Selected for report: WatchPug

Also found by: xYrYuYx

Labels

bug
G (Gas Optimization)
sponsor acknowledged

Awards

47.7068 USDC - $47.71

External Links

Handle

WatchPug

Vulnerability details

For the arithmetic operations that will never over/underflow, using the unchecked directive (Solidity v0.8 has default overflow/underflow checks) can save some gas from the unnecessary internal over/underflow checks.

For example:

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/NestedFactory.sol#L285-L309

function _submitInOrders(
    uint256 _nftId,
    IERC20 _inputToken,
    uint256 _inputTokenAmount,
    Order[] calldata _orders,
    bool _reserved,
    bool _fromReserve
) private returns (uint256 feesAmount, IERC20 tokenSold) {
    _inputToken = _transferInputTokens(_nftId, _inputToken, _inputTokenAmount, _fromReserve);
    uint256 amountSpent;
    for (uint256 i = 0; i < _orders.length; i++) {
        amountSpent += _submitOrder(address(_inputToken), _orders[i].token, _nftId, _orders[i], _reserved);
    }
    feesAmount = _calculateFees(_msgSender(), amountSpent);
    assert(amountSpent <= _inputTokenAmount - feesAmount); // overspent

    // If input is from the reserve, update the records
    if (_fromReserve) {
        _decreaseHoldingAmount(_nftId, address(_inputToken), _inputTokenAmount);
    }

    _handleUnderSpending(_inputTokenAmount - feesAmount, amountSpent, _inputToken);

    tokenSold = _inputToken;
}

_inputTokenAmount - feesAmount at L306 will never underflow.

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/FeeSplitter.sol#L166-L174

function updateShareholder(uint256 _accountIndex, uint256 _weight) external onlyOwner {
    require(_accountIndex + 1 <= shareholders.length, "FeeSplitter: INVALID_ACCOUNT_INDEX");
    uint256 _totalWeights = totalWeights;
    _totalWeights -= shareholders[_accountIndex].weight;
    shareholders[_accountIndex].weight = _weight;
    _totalWeights += _weight;
    require(_totalWeights > 0, "FeeSplitter: TOTAL_WEIGHTS_ZERO");
    totalWeights = _totalWeights;
}

_accountIndex + 1 will never overflow.

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/NestedBuybacker.sol#L107-L113

function trigger() internal {
    uint256 balance = NST.balanceOf(address(this));
    uint256 toBurn = (balance * burnPercentage) / 1000;
    uint256 toSendToReserve = balance - toBurn;
    _burnNST(toBurn);
    NST.safeTransfer(nstReserve, toSendToReserve);
}

balance - toBurn will never underflow.

#0 - maximebrugel

2021-11-22T16:12:29Z

We will use unchecked blocks when the code remains readable (since it can't be used as an expression).

  • _inputTokenAmount - feesAmount at L306 will never underflow. Acknowledge : Won't be easy to read in this function, its better to keep _inputTokenAmount - feesAmount in the assert.
  • _accountIndex + 1 Duplicated : This will change in #207
  • balance - toBurn Acknowledge : No need to add this kind of optimization for an owner function

Findings Information

🌟 Selected for report: WatchPug

Labels

bug
G (Gas Optimization)
sponsor confirmed

Awards

106.015 USDC - $106.02

External Links

Handle

WatchPug

Vulnerability details

For the storage variables that will be accessed multiple times, cache and read from the stack can save ~100 gas from each extra read (SLOAD after Berlin).

For example:

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/FeeSplitter.sol#L152-L162

function sendFeesWithRoyalties(
    address _royaltiesTarget,
    IERC20 _token,
    uint256 _amount
) external nonReentrant {
    require(_royaltiesTarget != address(0), "FeeSplitter: INVALID_ROYALTIES_TARGET_ADDRESS");

    _sendFees(_token, _amount, totalWeights);
    _addShares(_royaltiesTarget, _computeShareCount(_amount, royaltiesWeight, totalWeights), address(_token));
}

Recommendation

Change to:

function sendFeesWithRoyalties(
    address _royaltiesTarget,
    IERC20 _token,
    uint256 _amount
) external nonReentrant {
    require(_royaltiesTarget != address(0), "FeeSplitter: INVALID_ROYALTIES_TARGET_ADDRESS");

    uint256 _totalWeights = totalWeights;

    _sendFees(_token, _amount, _totalWeights);
    _addShares(_royaltiesTarget, _computeShareCount(_amount, royaltiesWeight, _totalWeights), address(_token));
}

#0 - adrien-supizet

2021-11-22T18:02:28Z

✅ Before: | Contract · Method · Min · Max · Avg · # calls · usd (avg) │ | FeeSplitter · sendFeesWithRoyalties · 74018 · 159518 · 121043 · 4 · - │

After: | FeeSplitter · sendFeesWithRoyalties · 73923 · 159423 · 120948 · 4 · - │

Findings Information

🌟 Selected for report: ye0lde

Also found by: PierrickGT, WatchPug, hack3r-0m, pmerkleplant

Labels

bug
duplicate
G (Gas Optimization)

Awards

13.9113 USDC - $13.91

External Links

Handle

WatchPug

Vulnerability details

Unused local variables in contracts increase contract size and gas usage at deployment.

Instances include:

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/NestedFactory.sol#L379-L383

(uint256[] memory amounts, address[] memory tokens) = OperatorHelpers.decodeDataAndRequire(
    data,
    _inputToken,
    _outputToken
);

tokens is unused.

https://github.com/code-423n4/2021-11-nested/blob/f646002b692ca5fa3631acfff87dda897541cf41/contracts/NestedFactory.sol#L410-L414

(uint256[] memory amounts, address[] memory tokens) = OperatorHelpers.decodeDataAndRequire(
    data,
    _inputToken,
    _outputToken
);

tokens is unused.

#0 - adrien-supizet

2021-11-18T12:38:46Z

duplicate #195

#1 - maximebrugel

2021-11-30T16:19:21Z

also duplicate #67 & #66

#2 - alcueca

2021-12-03T09:58:07Z

#195 as the main

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