Platform: Code4rena
Start Date: 08/06/2022
Pot Size: $115,000 USDC
Total HM: 26
Participants: 72
Period: 11 days
Judge: leastwood
Total Solo HM: 14
Id: 132
League: ETH
Rank: 10/72
Findings: 2
Award: $2,739.99
🌟 Selected for report: 1
🚀 Solo Findings: 1
🌟 Selected for report: cloudjunky
2598.127 USDC - $2,598.13
When repaying the AAVE Portal in repayAavePortal()
the _local
asset is used to repay the loan in _backLoan()
rather than the adopted
asset. This is likely to cause issues in production when actually repaying loans if the asset/token being repayed to AAVE is not the same as the asset/token that was borrowed.
The comment on L93
of PortalFacet.sol
states;
// Need to swap into adopted asset or asset that was backing the loan // The router will always be holding collateral in the local asset while the loaned asset // is the adopted asset
The swap is executed on L98
in the call to AssetLogic.swapFromLocalAssetIfNeededForExactOut()
however the return value adopted
is never used (it's an unused local variable). The full function is shown below;
// Swap for exact `totalRepayAmount` of adopted asset to repay aave (bool success, uint256 amountIn, address adopted) = AssetLogic.swapFromLocalAssetIfNeededForExactOut( _local, totalAmount, _maxIn ); if (!success) revert PortalFacet__repayAavePortal_swapFailed(); // decrement router balances unchecked { s.routerBalances[msg.sender][_local] -= amountIn; } // back loan _backLoan(_local, _backingAmount, _feeAmount, _transferId);
The balance of the _local
token is reduced but instead of the adopted
token being passed to _backLoan()
in L112 the _local
token is used.
Vim
To be consistent with the comments in the repayAavePortal()
function adopted
should be passed to _backLoan
so that the loan is repayed in the appropriate token.
Remove the reference to _local
in the _backLoan()
function and replace it with adopted
so it reads;
_backLoan(adopted, _backingAmount, _feeAmount, _transferId);
#0 - 0xleastwood
2022-08-15T09:07:38Z
If routers are able to repay Aave debt using a token of higher denomination (i.e. pay USDC
debt using ETH
or DAI
), then the liquidity portal functionality will be broken until Connext applies the necessary protocol upgrade to become solvent again.
Considering the fact that only whitelisted routers have access to this feature and the bridge transfers are not broken but instead limited, I think medium
severity makes sense.
🌟 Selected for report: BowTiedWardens
Also found by: 0x1f8b, 0x29A, 0x52, 0xNazgul, 0xNineDec, 0xf15ers, 0xkatana, 0xmint, Chom, ElKu, Funen, IllIllI, JMukesh, Jujic, Kaiziron, Lambda, MiloTruck, Ruhum, SmartSek, SooYa, TerrierLover, TomJ, WatchPug, Waze, _Adam, asutorufos, auditor0517, bardamu, c3phas, catchup, cccz, ch13fd357r0y3r, cloudjunky, cmichel, cryptphi, csanuragjain, defsec, fatherOfBlocks, hansfriese, hyh, jayjonah8, joestakey, k, kenta, obtarian, oyc_109, robee, sach1r0, shenwilly, simon135, slywaters, sorrynotsorry, tintin, unforgiven, xiaoming90, zzzitron
141.8552 USDC - $141.86
Multicall.sol
L18 has a require statement with an empty message - providing no reason as to why it might revert;
function aggregate(Call[] memory calls) public returns (uint256 blockNumber, bytes[] memory returnData) { blockNumber = block.number; returnData = new bytes[](calls.length); for (uint256 i = 0; i < calls.length; i++) { (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData); require(success); returnData[i] = ret; } }
Ensure that each require statement has a clear message for why it failed.
The following natspec comments have missing params or incomplete documentation;
XCalled()
event in BridgeFacet.sol
has no parameter documentation despite the very next event Reconciled()
having complete param documentation.RouterLiquidityAdded()
event in RoutersFacet.sol
is missing param documentation for canonicalId
in the natspec.swapExact()
function in StableSwapFacet.sol
is missing param documentation for minAmountOut
and deadline
in natspec.swapExactOut()
function in StableSwapFacet.sol
is missing param documentation for maxAmountIn
and deadline
in natspec.setupAsset()
function in AssetFacet.sol
is missing param documentation for _stableSwapPool
in natspec.repayAavePortal()
function in PortalFacet.sol
is missing param documentation for _transferId
in natspec.IExecutor{}
interface of IExecutor.sol
is confusing as to whether it is intended for the ExecutorArgs
struct or the Executed
event. Furthermore the params names don't match. The natspec uses an underscore where the struct and the event do not. In addition transferId
and recovery
are missing from the natspec documentation.swapFromLocalAssetIfNeededForExactOut()
function in AssetLogic.sol
is missing param documentation for _maxIn
in natspec._swapAsset()
function in AssetLogic.sol
is missing param documentation for _slippageTol
in natspec._swapAssetOut()
function in AssetLogic.sol
is missing param documentation for _maxIn
in natspec.ExecuteArgs
struct in LibConnextStorage.sol
is missing param documentation for routerSignatures
in natspec.RouterPermissionsManagerInfo
struct in LibConnextStorage.sol
is missing param documentation for approvedForPortalRouters
in natspec.Complete natspec documentation for functions and events identified above. I tried not to be pedantic here and focused only on functions that had some form of natspec param documentation assuming they were important and more accurate documentation would help.
nonce
value passed to the handle()
function in BridgeFacet.sol
is never used._router
value passed to the _reconcileProcessPortal()
function in BridgeFacet.sol
is never used.adopted
value returned from AssetLogic.swapFromLocalAssetIfNeededForExactOut()
in PortalFacet.sol
is never used._router
value passed to repayAavePortalFor()
in PortalFacet.sol
is never used.nonce
or _nonce
value is is passed to numerous handle()
functions throughout the code base sufficing the IMessageRecipient{}
interface. However there seems to be no requirement for it in BridgeFacet.sol
and it should be documented as unused
similar to the Nomad GovernanceRouter.sol
handle()
function i.e. uint32, // nonce (unused)
._reconcileProcessPortal()
is called within the _reconcile()
function in BridgeFacet.sol
. The first router in the path is passed to the function _reconcileProcessPortal(amount, token, routers[0], transferId);
. If reconciling portal payments is not related to the router that took on the credit risk then remove routers[0]
from the _reconcile()
function and from the _reconcileProcessPortal()
function definition.(bool success, uint256 amountIn, _) = AssetLogic.swapFromLocalAssetIfNeededForExactOut(..
or the medium bug is true and adopted
should be used instead of _local
for the rest of the function._router
should either be used in the function body or removed.#0 - jakekidd
2022-07-01T22:39:31Z
1 is invalid, multicall not in scope