Connext contest - 0xRajeev's results

The interoperability protocol of L2 Ethereum.

General Information

Platform: Code4rena

Start Date: 09/07/2021

Pot Size: $25,000 USDC

Total HM: 7

Participants: 10

Period: 3 days

Judge: ghoulsol

Total Solo HM: 2

Id: 19

League: ETH

Connext

Findings Distribution

Researcher Performance

Rank: 1/10

Findings: 6

Award: $7,790.90

🌟 Selected for report: 12

πŸš€ Solo Findings: 1

Findings Information

🌟 Selected for report: 0xRajeev

Also found by: cmichel, pauliax

Labels

bug
3 (High Risk)
sponsor acknowledged

Awards

1103.2637 USDC - $1,103.26

External Links

Handle

0xRajeev

Vulnerability details

Impact

The addLiquidity() function takes a router address parameter whose liquidity is increased (instead of assuming that router == msg.sender like done on removeLiquidity()) on this contract/chain by transferring the fund amount from router address to this contract if assetID != 0 i.e. ERC20 tokens. However, anyone can call this function on the router’s behalf. For assetID == 0, the Ether transfer via msg.value comes from msg.sender and hence is assumed to be the router itself.

Impact: This will allow anyone to call this function and arbitrarily move ERC20 tokens from router address to this contract, assuming router has given max approval to this contract and has assetID amount available for transfer. While the router can always remove the liquidity if it doesn’t want to maintain that level of liquidity, this lack of access control or flexibility for a relayer to add liquidity on router’s behalf may unnecessarily (and without authorization) increase the router’s exposure to protocol risk to more than it desires.

Proof of Concept

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L88-L98

Use of msg.sender in removeLiquidity: https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L88-L98

Tools Used

Manual Analysis

Consider the use of msg.sender in addLiquidity() or evaluate this risk otherwise.

#0 - LayneHaber

2021-07-13T15:31:29Z

The bigger issue here is the typo here, if we use the funds from msg.sender that means that people are donating funds to the router.

We will fix the msg.sender, but allow addLiquidity to be callable by anyone

#1 - ghoul-sol

2021-08-01T23:38:29Z

This is high risk because funds can be send to the wrong address.

Findings Information

🌟 Selected for report: 0xRajeev

Also found by: cmichel, gpersoon, pauliax, s1m0, shw

Labels

bug
3 (High Risk)
sponsor confirmed

Awards

402.1396 USDC - $402.14

External Links

Handle

0xRajeev

Vulnerability details

Impact

During fulfill() on the receiving chain, if the user has set up an external contract at txData.callTo, the catch blocks for both IFulfillHelper.addFunds() and IFulfillHelper.excute() perform transferAsset to the predetermined fallback address txData.receivingAddress.

If addFunds() has reverted earlier, toSend amount would already have been transferred to the receivingAddress. If execute() also fails, it is again transferred.

Scenario: User sets up receiver chain txData.callTo contract such that both addFunds() and execute() calls revert and that will let him get twice the toSend amount credited to the receivingAddress. So effectively, Alice locks 100 tokenAs on chain A and can get 200 tokenAs (or twice the amount of any token she is supposed to get on chainB from the router), minus relayer fee, on chainB. Router liquidity is double-dipped by Alice and router loses funds.

Proof of Concept

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L395-L409

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L413-L428

Tools Used

Manual Analysis

The second catch block for execute() should likely not have the transferAsset() call. It seems like a copy-and-paste bug unless there is some reason that is outside the specified scope and documentation for this contest.

#0 - LayneHaber

2021-07-14T18:01:44Z

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
3 (High Risk)
sponsor confirmed

Awards

4086.1617 USDC - $4,086.16

External Links

Handle

0xRajeev

Vulnerability details

Impact

The cancelling relayer is being paid in receivingAssetId on the sendingChain instead of in sendingAssetID. If the user relies on a relayer to cancel transactions and that receivingAssetId asset does not exist on the sending chain (assuming only sendingAssetID on the sending chain and receivingAssetId on the receiving chain are assured to be valid and present) then the cancel transaction from the relayer will always revert and user’s funds will remain locked on the sending chain.

Impact: Expired transfers can never be cancelled and user funds will be locked forever if user relies on a relayer.

Proof of Concept

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L510-L517

Tools Used

Manual Analysis

Change receivingAssetId to sendingAssetId in transferAsset() on L514.

#0 - LayneHaber

2021-07-14T01:24:32Z

Findings Information

🌟 Selected for report: 0xRajeev

Also found by: cmichel, shw

Labels

bug
2 (Med Risk)
sponsor acknowledged

Awards

330.9791 USDC - $330.98

External Links

Handle

0xRajeev

Vulnerability details

Impact

The signature check in recoverFulfillSignature() only uses transaction ID (along with the relayer fee) which can be accidentally reused by the user, in which case the older signatures with the older relayer fees can be replayed. The signature should be on the entire digest hashInvariantTransactionData(txData) as indicated in the comment on L306.

Impact: If the user signatures are indeed on the digest as indicated by the comment, the signature/address check in fulfill() will fail. If not, they may be accidentally/intentionally replayed with same transaction ID which also appears to be an outstanding question as indicated by the comment on L12.

recoverCancelSignature() similarly uses only tx ID.

Unless there is a good reason not to, it is safer to include hashInvariantTransactionData(txData) in signatures so that they cannot be replayed with different txData (but same tx ID) whose preparedBlockNumber is > 0.

Proof of Concept

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L598

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L324

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L306

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L12

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L616

Tools Used

Manual Analysis

Evaluate if the signature should contain only tx ID or the entire digest, and change logic appropriately.

#0 - LayneHaber

2021-07-12T20:23:17Z

User should be able to break up large transfers across multiple routers using the same transactionId to keep the transaction unlocking atomic. For example, say I want to transfer $100K, but there are only 8 routers who each have $60K available. I should be able to break up the single transaction into $20K transactions split across 5 of the routers. When unlocking this, I should only need to broadcast a single signature, so all of the transactions can be unlocked simultaneously.

#1 - ghoul-sol

2021-08-02T00:04:19Z

Bumping to medium risk as replay attack can have significant consequences

Findings Information

🌟 Selected for report: 0xRajeev

Also found by: pauliax

Labels

bug
1 (Low Risk)
sponsor confirmed

Awards

183.8773 USDC - $183.88

External Links

Handle

0xRajeev

Vulnerability details

Impact

The protocol appears to allow arbitrary assets, amounts and routers/users without an initial time-bounded whitelist of assets/routers/users or upper bounds on amounts. Also, there is no pause/unpause functionality. While this lack of ownership and control makes it completely permissionless, it is a risky design because if there are latent protocol vulnerabilities there is no fallback option.

Proof of Concept

Lack of owner, whitelisting, thresholds, pause/unpause in the protocol.

See https://medium.com/electric-capital/derisking-defi-guarded-launches-2600ce730e0a

Tools Used

Manual Analysis

Consider an initial guarded launch approach to owner-based whitelisting asset types, router/recipient addresses, amount thresholds and adding a pause/unpause functionality for emergency handling. The design should be able to make this owner configurable where the owner can renounce ownership at a later point when the protocol operation is sufficiently time-tested and deemed stable/safe.

#0 - LayneHaber

2021-07-15T01:02:17Z

Findings Information

🌟 Selected for report: shw

Also found by: 0xRajeev, cmichel, pauliax

Labels

bug
duplicate
1 (Low Risk)

Awards

74.4703 USDC - $74.47

External Links

Handle

0xRajeev

Vulnerability details

Impact

Connext allows the use any ERC20 assets to be used for cross-chain transfers. Some of these tokens could charge a fee, add a reward or rebase over time. However, the protocol does not have the required support to handle such tokens. The necessary checks include at least verifying the amount of tokens transferred to/from contracts before and after the actual transfer to infer any fees/interest/reward/rebasing. These seem to be absent in the various functions.

Impact: Users transferring rebasing/deflationary/inflationary tokens accidentally/maliciously will lead to miscalculations of balances in protocol.

Proof of Concept

See finding https://consensys.net/diligence/audits/2020/12/growth-defi-v1/#evaluate-all-tokens-prior-to-inclusion-in-the-system

See finding https://consensys.net/diligence/audits/2021/03/umbra-smart-contracts/#document-token-behavior-restrictions

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L97

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L128

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L369

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L378

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L406

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L425

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L504

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L514

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L525

Tools Used

Manual Analysis

Add necessary checks including at least verifying the amount of tokens transferred to/from contracts before and after the actual transfer to infer any fees/interest/reward/rebasing. Alternatively whitelist/blacklist allowed/disallowed assetIDs.

#0 - LayneHaber

2021-07-12T20:06:28Z

#68

Findings Information

🌟 Selected for report: 0xRajeev

Also found by: pauliax

Labels

bug
1 (Low Risk)
sponsor confirmed

Awards

183.8773 USDC - $183.88

External Links

Handle

0xRajeev

Vulnerability details

Impact

Zero-address checks are in general a best-practice. However, addLiquidity() and removeLiquidity() are missing zero-address checks on router and recipient addresses respectively.

addLiquidity() on Eth transfers will update the zero index balance and get logged as such in the event without the amount getting accounted for the correct router.

For ERC20 assets, token.transfer() generally implements this check but the Eth transfer using transferEth() does not have this check and calls addr.call(value) which will lead to burning in the case of removeLiquidity().

The checks may be more important because assetID is 0 for Eth. So a router may accidentally use 0 values for both assetID and router/recipient.

There is also a missing zero-address check on sendingChainFallback which is relevant for Eth transfers in cancel(). The comment on L178 indicates the need for this but the following check on L179 ends up checking receivingAddress instead (which is also necessary).

Proof of Concept

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L88 https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L101-L104

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L116 https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L128-L131

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L504

Tools Used

Manual Analysis

Add zero-address checks.

#0 - sanchaymittal

2021-07-14T09:09:49Z

Findings Information

🌟 Selected for report: 0xRajeev

Also found by: cmichel

Labels

bug
1 (Low Risk)
sponsor acknowledged

Awards

183.8773 USDC - $183.88

External Links

Handle

0xRajeev

Vulnerability details

Impact

The prepare() function hashes the invariantData parameter data to check the mapping entry is 0 for that digest as a measure to prevent duplicate prepare()s. However, an attacker can abuse this check to front-run a targeted victim's prepare Tx with the same parameters and with some dust amount to prevent the user’s actual prepare Tx from succeeding.

Impact: Potential griefing attack vector if user address is not msg.sender. This is with the assumption that relayers are only relevant on the receiving side where the user may not have the receivingAssetId i.e. no reason for msg.sender of prepare() to be the relayer and not the user.

Proof of Concept

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L191-L192

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L212-L235

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L247-L248

Tools Used

Manual Analysis

Add msg.sender == invariantData.user check on sending chain side similar to the check for router address on the receiving side.

#0 - LayneHaber

2021-07-15T02:09:40Z

#1 - LayneHaber

2021-07-17T00:09:11Z

Reverted the changes, without tracking the active blocks this DOS vector still exists but presents no risk to locked funds. It is also unclear what the attacker would gain from this, and would cost them gas funds.

Could add a signature, but comes with some UX drawbacks that seem more important than removing the DOS vector

Findings Information

🌟 Selected for report: pauliax

Also found by: 0xRajeev, shw

Labels

bug
duplicate
1 (Low Risk)

Awards

110.3264 USDC - $110.33

External Links

Handle

0xRajeev

Vulnerability details

Impact

The expiry check against block.timestamp in fulfill() is stricter using β€˜>’ instead of β€˜>=β€˜ for an open-interval instead of closed like elsewhere.

Impact: This will cause fulfill() to expire earlier than it should.

Proof of Concept

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L317-L318

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L188

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L495

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L533

Tools Used

Manual Analysis

Make inequality less strict (use β€˜>=β€˜) for time to be a closed-interval like other checks on L188, L495 and L533.

#0 - LayneHaber

2021-07-12T20:28:47Z

#28

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
G (Gas Optimization)
sponsor confirmed

Awards

224.978 USDC - $224.98

External Links

Handle

0xRajeev

Vulnerability details

Impact

EIP-2929 in Berlin fork increased the gas costs of SLOADs and CALL* family opcodes increasing them for not-accessed slots/addresses and decreasing them for accessed slots. EIP-2930 optionally supports specifying an access list (in the transaction) of all slots and addresses accessed by the transaction which reduces their gas cost upon access and prevents EIP-2929 gas cost increases from breaking contracts.

Impact: Considering these changes may significantly impact gas usage for transactions that call functions touching many state variables or making many external calls. Specifically, removeUserActiveBlocks() removes an active block from the array of blocks for an user, all of which are stored in storage. Transactions for fulfill() and cancel() functions that call removeUserActiveBlocks() can consider using access lists for all the storage state (of user’s active blocks) they touch (read + write) to reduce gas.

Proof of Concept

https://eips.ethereum.org/EIPS/eip-2929

https://eips.ethereum.org/EIPS/eip-2930

https://hackmd.io/@fvictorio/gas-costs-after-berlin

https://github.com/gakonst/ethers-rs/issues/265

SLOADs: https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L580

SSTOREs: https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L583

Calls: https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L346

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L490

Tools Used

Manual Analysis

Evaluate the feasibility of using access lists to save gas due to EIPs 2929 & 2930 post-Berlin hard fork. The tooling support is WIP.

#0 - LayneHaber

2021-07-15T19:21:03Z

Removed tracking of active blocks: https://github.com/connext/nxtp/pull/24

Findings Information

🌟 Selected for report: shw

Also found by: 0xRajeev

Labels

bug
duplicate
G (Gas Optimization)

Awards

101.2401 USDC - $101.24

External Links

Handle

0xRajeev

Vulnerability details

Impact

Post-Berlin, SLOADs on state variables accessed first-time in a transaction increased from 800 gas to 2100, which is a 2.5x increase. Successive SLOADs cost 100 gas. Memory stores/loads (MSTOREs/MLOADs) cost only 3 gas. Therefore, by caching repeatedly read state variables in local variables within a function, one can save >=100 gas.

Proof of Concept

In removeLiquidity(), caching routerBalances[msg.sender][assetId] in a local variable will save 1002 = 200 gas (mapping of a mapping) because it is SLOADed 2 times, once in the require() check and then again to increment it: https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L122-L125. Instead, change to: uint256 currentBalance = routerBalances[msg.sender][assetId]; require(currentBalance >= amount, "removeLiquidity: INSUFFICIENT_FUNDS"); // Update router balances routerBalances[msg.sender][assetId] = currentBalance - amount In prepare(), caching routerBalances[msg.sender][assetId] in a local variable will save 1002 = 200 gas (mapping of a mapping) because it is SLOADed 2 times, once in the require() check and then again to increment it: https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L253-L260 In function removeUserActiveBlocks(), caching activeTransactionBlocks[user][i] in a local variable will save 100*2 = 200 gas (mapping of a mapping) per active block, because it is SLOADed 2 times, once in the conditional predicate check and again in the assignment RHS: https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L576-L580. Instead change to: uint256 block = activeTransactionBlocks[user][i]; if (!removed && block == preparedBlock) { removed = true; continue; } updated[updatedIdx] = block;

Tools Used

Manual Analysis

Cache repeatedly read state variables (especially those within a loop) in local variables at an appropriate part of the function (preferably the beginning) and use them instead of state variables. Converting repetitive SLOADs to MLOADs reduces gas from 100 to 3.

#0 - LayneHaber

2021-07-13T17:28:35Z

#75

Findings Information

🌟 Selected for report: 0xRajeev

Also found by: pauliax

Labels

bug
G (Gas Optimization)
sponsor confirmed

Awards

101.2401 USDC - $101.24

External Links

Handle

0xRajeev

Vulnerability details

Impact

EIP-2929 in Berlin fork increased the gas costs of CALL* family opcodes to 2600. Making a delegatecall to a library function therefore costs 2600. LibUtils.revertIfCallFailed() reverts and passes on the revert string if the boolean argument is false. Instead, moving the checking of the boolean to the caller avoids the library call when the boolean is true, which is likely the case most of the time.

Proof of Concept

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/lib/LibUtils.sol#L10-L19

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/lib/LibAsset.sol#L35

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/lib/LibERC20.sol#L20

Tools Used

Manual Analysis

Remove the boolean parameter from revertIfCallFailed() and move the conditional check logic to the call sites.

#0 - LayneHaber

2021-07-14T15:52:34Z

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
G (Gas Optimization)
sponsor confirmed

Awards

224.978 USDC - $224.98

External Links

Handle

0xRajeev

Vulnerability details

Impact

While code modularity is generally a good practice and creating libraries of functions commonly used across different contracts can increase maintainability and reduce contract deployment size/cost, it comes at the increased cost of gas usage at runtime because of the external calls. EIP-2929 in Berlin fork increased the gas costs of CALL* family opcodes to 2600. Making a delegatecall to a library function therefore costs 2600.

Impact: A LibAsset.transferAsset() call from TransactionManager.sol makes LibERC20.transfer() call for ERC20 which in turn makes another external call to LibUtils.revertIfCallFailed() in wrapCall. So an ERC20 transfer effectively makes 3 additional (besides the ERC20 token contract function call assetId.call(..) external calls -> LibAsset -> LibERC20 -> LibUtils, which costs 2600*3 = 7800 gas.

Combining these functions into a single library or making them all internal to TransactionManager.sol can convert these delegatecalls into JMPs to save gas.

Proof of Concept

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/lib/LibAsset.sol#L58

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/lib/LibAsset.sol#L44

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/lib/LibERC20.sol#L64

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/lib/LibERC20.sol#L20

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L128

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L369

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L378

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L406

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L425

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L504

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L514

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L525

And other Lib* calls.

Tools Used

Manual Analysis

Consider moving all the library functions internal to this contract or to a single library to save gas from external calls each of which costs 2600 gas.

#0 - LayneHaber

2021-07-13T21:52:15Z

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
G (Gas Optimization)
sponsor confirmed

Awards

224.978 USDC - $224.98

External Links

Handle

0xRajeev

Vulnerability details

Impact

While it may be considered extra-safe to have a nonreentrant modifier on all functions making any external calls even though they are to trusted contracts, when functions implement Checks-Effects-Interactions (CEI) pattern, it is helpful to evaluate the perceived security benefit vs gas usage trade-off for using nonreentrant modifier.

Functions adhering to the CEI pattern may consider not having the nonreentrant modifier which does two SSTORES (getting more expensive with the London fork EIP-3529) to its _status state variable.

Example 1: In addLiquidity(), by moving the updating of router balance on L101 to before the transfers from L92, the function would adhere to CEI pattern and could be evaluated to remove the nonreentrant modifier.

Example 2: removeLiquidity() already adheres to CEI pattern and could be evaluated to remove the nonreentrant modifier.

prepare() can be slightly restructured to follow CEI pattern as well. However, fulfill() and cancel() are risky with multiple external calls and its safer to leave the nonreentrant call at the expense of additional gas costs.

Impact: Save gas by removing nonreentrant modifier if function is deemed to be reentrant safe. This can save gas costs of 2 SSTORES per function call that uses this modifier: _status SSTORE from 1 to 2 costs 5000 and _status SSTORE from 2 to 1 which costs 100 (because it was already accessed) which is significant at 5100 per call post-Berlin EIP-2929.

Proof of Concept

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L92-L101

Tools Used

Manual Analysis

Evaluate security benefit vs gas usage trade-off for using nonreentrant modifier on functions that may already be reentrant safe or do not need this protection. It may indeed be safe to leave this modifier (while accepting the gas impact) if such an evaluation is tricky or depends on assumptions.

#0 - LayneHaber

2021-07-15T19:17:59Z

Findings Information

🌟 Selected for report: shw

Also found by: 0xRajeev, cmichel, greiart, s1m0

Labels

bug
duplicate
G (Gas Optimization)

Awards

29.5216 USDC - $29.52

External Links

Handle

0xRajeev

Vulnerability details

Impact

The router balance checks are performed within a require and then Solidity 0.8.4’s built-in arithmetic overflow/underflow checks again perform this check implicitly (and revert on underflows) during the subtraction of the balance. So effectively, this router balance >= amount check is performed twice (and both times by reading state variables via SLOADs). This redundant check costs a bit of gas.

Proof of Concept

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L121-L125

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L253-L260

Tools Used

Manual Analysis

Remove the initial require() check to let Solidity perform the underflow check in the subtraction but this comes at the cost of having only a revert and missing the error string from require(). Alternatively, put the router balance decrement expression within unchecked{} block to save gas by avoiding Solidity 0.8.4’s built-in arithmetic overflow/underflow checks because this has already been explicitly performed within require().

#0 - LayneHaber

2021-07-13T17:33:00Z

#74

Findings Information

🌟 Selected for report: 0xRajeev

Labels

bug
G (Gas Optimization)
sponsor confirmed

Awards

224.978 USDC - $224.98

External Links

Handle

0xRajeev

Vulnerability details

Impact

Checking if toSend > 0 before making the external library call to LibAsset.transferAsset() can save 2600 gas by avoiding the external call in such situations.

Proof of Concept

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L375-L380

https://github.com/code-423n4/2021-07-connext/blob/8e1a7ea396d508ed2ebeba4d1898a748255a48d2/contracts/TransactionManager.sol#L364

Tools Used

Manual Analysis

Add toSend > 0 to predicate on L375 similar to check on L387.

#0 - sanchaymittal

2021-07-14T08:46:51Z

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