CertiK reviewed ParaSpace Lending V1 (ParaSpace - NFT Money Market) on 25 Oct 2022: 23 findings, 2 critical; 14 marked fixed, 1 acknowledged, 8 open.
Report hosted on skynet.certik.com.
Trustblock does not review code.
Statuses are the security firm’s own, from its report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 2 |
| High | 0 | 1 | 1 |
| Medium | 1 | 0 | 3 |
| Low | 7 | 0 | 8 |
A reentrancy attack can occur when the contract creates a function that makes an external call to another untrusted contract before resolving any effects. If the attacker can control the untrusted contract, they can make a recursive call back to the original function, repeating interactions that would have otherwise not run after the external call resolved the effects. *** ## PoolAddressesProvider.sol ### External call(s) ```solidity=85 address oldImplementationAddress = _getProxyImplementation(id); ``` * This function call executes the following external call(s). * In `PoolAddressesProvider._getProxyImplementation`, * `InitializableImmutableAdminUpgradeabilityProxy(payableProxyAddress).implementation()` ```solidity=86 _updateImpl(id, newImplementationAddress); ``` * This function call executes the following external call(s). * In `PoolAddressesProvider._updateImpl`, * `proxy.initialize(newAddress,params)` * In `PoolAddressesProvider._updateImpl`, * `proxy.upgradeToAndCall(newAddress,params)` ### State variables written after the call(s) ```solidity=86 _updateImpl(id, newImplementationAddress); ``` * This function call executes the following assignment(s). * In `PoolAddressesProvider._updateImpl`, * `_addresses[id] = proxyAddress = address(proxy)` --- ## PoolAddressesProvider.sol ### External call(s) ```solidity=102 address oldPoolImpl = _getProxyImplementation(POOL); ``` * This function call executes the following external call(s). * In `PoolAddressesProvider._getProxyImplementation`, * `InitializableImmutableAdminUpgradeabilityProxy(payableProxyAddress).implementation()` ```solidity=103 _updateImpl(POOL, newPoolImpl); ``` * This function call executes the following external call(s). * In `PoolAddressesProvider._updateImpl`, * `proxy.initialize(newAddress,params)` * In `PoolAddressesProvider._updateImpl`, * `proxy.upgradeToAndCall(newAddress,params)` ### State variables written after the call(s) ```solidity=103 _updateImpl(POOL, newPoolImpl); ``` * This function call executes the following assignment(s). * In `PoolAddressesProvider._updateImpl`, * `_addresses[id] = proxyAddress = address(proxy)` --- ## PoolAddressesProvider.sol ### External call(s) ```solidity=118 address oldPoolConfiguratorImpl = _getProxyImplementation( POOL_CONFIGURATOR ); ``` * This function call executes the following external call(s). * In `PoolAddressesProvider._getProxyImplementation`, * `InitializableImmutableAdminUpgradeabilityProxy(payableProxyAddress).implementation()` ```solidity=121 _updateImpl(POOL_CONFIGURATOR, newPoolConfiguratorImpl); ``` * This function call executes the following external call(s). * In `PoolAddressesProvider._updateImpl`, * `proxy.initialize(newAddress,params)` * In `PoolAddressesProvider._updateImpl`, * `proxy.upgradeToAndCall(newAddress,params)` ### State variables written after the call(s) ```solidity=121 _updateImpl(POOL_CONFIGURATOR, newPoolConfiguratorImpl); ``` * This function call executes the following assignment(s). * In `PoolAddressesProvider._updateImpl`, * `_addresses[id] = proxyAddress = address(proxy)` --- ## BorrowLogic.sol ### External call(s) ```solidity=99 ( isFirstBorrowing, reserveCache.nextTotalStableDebt, reserveCache.nextAvgStableBorrowRate ) = IStableDebtToken(reserveCache.stableDebtTokenAddress).mint( params.user, params.onBehalfOf, params.amount, currentStableRate ); ``` ```solidity=110 ( isFirstBorrowing, reserveCache.nextScaledVariableDebt ) = IVariableDebtToken(reserveCache.variableDebtTokenAddress).mint( params.user, params.onBehalfOf, params.amount, reserveCache.nextVariableBorrowIndex ); ``` ### State variables written after the call(s) ```solidity=125 reserve.updateInterestRates( reserveCache, params.asset, 0, params.releaseUnderlying ? params.amount : 0 ); ``` * This function call executes the following assignment(s). * In `ReserveLogic.updateInterestRates`, * `reserve.currentLiquidityRate = vars.nextLiquidityRate.toUint128()` * In `ReserveLogic.updateInterestRates`, * `reserve.currentStableBorrowRate = vars.nextStableRate.toUint128()` * In `ReserveLogic.updateInterestRates`, * `reserve.currentVariableBorrowRate = vars.nextVariableRate.toUint128()` ```solidity=125 reserve.updateInterestRates( reserveCache, params.asset, 0, params.releaseUnderlying ? params.amount : 0 ); ``` * This function call executes the following assignment(s). * In `ReserveLogic.updateInterestRates`, * `reserve.currentLiquidityRate = vars.nextLiquidityRate.toUint128()` * In `ReserveLogic.updateInterestRates`, * `reserve.currentStableBorrowRate = vars.nextStableRate.toUint128()` * In `ReserveLogic.updateInterestRates`, * `reserve.currentVariableBorrowRate = va
The function `initialize()` is `public` and can be called by anyone as long as the contract is deployed.
The title comment for `NToken.sol` is for `PToken`, not `NToken`. --- The comments for `executeBorrow()` and `executeRepay()` isolated positions are mentioned, however, there are no isolated positions in the code.
The variables `auctionRecoveryHealthFactor`, `newReserveFactor`, `newBorrowCap`, and `newFee` do not have reasonable boundaries, so they can be given arbitrary values after deploying.
If multiple pools are deployed, there may be a risk of code reentrancy. The hacker can call the `withdrawERC721()` method of one of the Pools and use the hook `_checkOnERC721Received` method of the ERC721 receiver to reenter the methods(such as the method `liquidationERC721()`) of other Pools. Please check if multiple Pools are allowed to be deployed at the same time.
The contract is serving as the underlying entity to interact with third-party `UniswapV3`, `OpenSea`, `X2Y2`, `MoonBird`,and NFT Oracle protocols. The scope of the audit treats 3rd party entities as black boxes and assumes their functional correctness. However, in the real world, 3rd parties can be compromised and this may lead to lost or stolen assets. In addition, upgrades of 3rd parties can possibly create severe impacts, such as increasing fees of 3rd parties, migrating to new LP pools, etc. The `ParaSpace` protocol allows users to borrow assets using NFT as collateral. If NFT prices fluctuate significantly in the third-party markets, the Supplier's health factory may fluctuate as well. This is a potential risk to this protocol and to the Supplier.
The return value of an external call is not stored in a local or state variable. ```solidity=290 stableDebtToken.burn(user, stableDebt); ``` --- ```solidity=651 INToken(vars.collateralXToken).burn(params.user, msg.sender, tokenIds); ``` --- ```solidity=80 Address.functionDelegateCall( params.marketplace.adapter, abi.encodeWithSelector( IMarketplace.matchAskWithTakerBid.selector, params.marketplace.marketplace, params.payload, priceEth ) ); ``` --- ```solidity=134 Address.functionDelegateCall( params.marketplace.adapter, abi.encodeWithSelector( IMarketplace.matchBidWithTakerAsk.selector, params.marketplace.marketplace, params.payload ) ); ``` --- ```solidity=200 IERC20(token).approve(params.marketplace.operator, 0); ``` --- ```solidity=201 IERC20(token).approve(params.marketplace.operator, price); ``` --- ```solidity=353 MarketplaceLogic.executeBuyWithCredit( _reserves, _reservesList, _usersConfig[onBehalfOf], DataTypes.ExecuteMarketplaceParams({ marketplaceId: marketplaceId, payload: payload, credit: credit, ethLeft: msg.value, marketplace: marketplace, orderInfo: orderInfo, WETH: WETH, referralCode: referralCode, maxStableRateBorrowSizePercent: _maxStableRateBorrowSizePercent, reservesCount: _reservesCount, oracle: ADDRESSES_PROVIDER.getPriceOracle(), priceOracleSentinel: ADDRESSES_PROVIDER.getPriceOracleSentinel() }) ); ``` --- ```solidity=205 Address.functionCall( airdropContract, airdropParams, Errors.CALL_AIRDROP_METHOD_FAILED ); ``` --- ```solidity=107 INonfungiblePositionManager(_underlyingAsset).decreaseLiquidity( params ); ``` --- ```solidity=614 try IERC721Receiver(to).onERC721Received( _msgSender(), from, tokenId, _data ) returns (bytes4 retval) { return retval == IERC721Receiver.onERC721Received.selector; } catch (bytes memory reason) { if (reason.length == 0) { revert( "ERC721: transfer to non ERC721Receiver implementer" ); } else { assembly { revert(add(32, reason), mload(reason)) } } } ```
One or more state variables are never used in the codebase. Variable `BORROWABLE_IN_ISOLATION_MASK` in `ReserveConfiguration` is never used in `ReserveConfiguration`. ```solidity=22 uint256 internal constant BORROWABLE_IN_ISOLATION_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFFF; // prettier-ignore ``` ```solidity=12 library ReserveConfiguration { ``` --- Variable `UNBACKED_MINT_CAP_MASK` in `ReserveConfiguration` is never used in `ReserveConfiguration`. ```solidity=28 uint256 internal constant UNBACKED_MINT_CAP_MASK = 0xFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore ``` ```solidity=12 library ReserveConfiguration { ``` --- Variable `DEBT_CEILING_MASK` in `ReserveConfiguration` is never used in `ReserveConfiguration`. ```solidity=29 uint256 internal constant DEBT_CEILING_MASK = 0xF0000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore ``` ```solidity=12 library ReserveConfiguration { ``` --- Variable `BORROWABLE_IN_ISOLATION_START_BIT_POSITION` in `ReserveConfiguration` is never used in `ReserveConfiguration`. ```solidity=40 uint256 internal constant BORROWABLE_IN_ISOLATION_START_BIT_POSITION = 61; ``` ```solidity=12 library ReserveConfiguration { ``` --- Variable `IS_DYNAMIC_CONFIGS_START_BIT_POSITION` in `ReserveConfiguration` is never used in `ReserveConfiguration`. ```solidity=48 uint256 internal constant IS_DYNAMIC_CONFIGS_START_BIT_POSITION = 168; ``` ```solidity=12 library ReserveConfiguration { ``` --- Variable `UNBACKED_MINT_CAP_START_BIT_POSITION` in `ReserveConfiguration` is never used in `ReserveConfiguration`. ```solidity=49 uint256 internal constant UNBACKED_MINT_CAP_START_BIT_POSITION = 176; ``` ```solidity=12 library ReserveConfiguration { ``` --- Variable `DEBT_CEILING_START_BIT_POSITION` in `ReserveConfiguration` is never used in `ReserveConfiguration`. ```solidity=50 uint256 internal constant DEBT_CEILING_START_BIT_POSITION = 212; ``` ```solidity=12 library ReserveConfiguration { ``` --- Variable `MAX_VALID_EMODE_CATEGORY` in `ReserveConfiguration` is never used in `ReserveConfiguration`. ```solidity=60 uint256 internal constant MAX_VALID_EMODE_CATEGORY = 255; ``` ```solidity=12 library ReserveConfiguration { ``` --- Variable `MAX_VALID_UNBACKED_MINT_CAP` in `ReserveConfiguration` is never used in `ReserveConfiguration`. ```solidity=61 uint256 internal constant MAX_VALID_UNBACKED_MINT_CAP = 68719476735; ``` ```solidity=12 library ReserveConfiguration { ``` --- Variable `MAX_VALID_DEBT_CEILING` in `ReserveConfiguration` is never used in `ReserveConfiguration`. ```solidity=62 uint256 internal constant MAX_VALID_DEBT_CEILING = 1099511627775; ``` ```solidity=12 library ReserveConfiguration { ``` --- Variable `BASE_CURRENCY_DECIMALS` in `LiquidationLogic` is never used in `LiquidationLogic`. ```solidity=82 uint256 private constant BASE_CURRENCY_DECIMALS = 18; ``` ```solidity=34 library LiquidationLogic { ``` --- Variable `______gap` in `VersionedInitializable` is never used in `ATokenDebtToken`. ```solidity=78 uint256[50] private ______gap; ``` ```solidity=14 contract ATokenDebtToken is RebasingDebtToken { ``` --- Variable `_allowances` in `MintableIncentivizedERC721` is never used in `NTokenMoonBirds`. ```solidity=95 mapping(address => mapping(address => uint256)) private _allowances; ``` ```solidity=27 contract NTokenMoonBirds is NToken, IMoonBirdBase { ```
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (30)
1,044 projects reviewed · 1,375 reports · latest May 2025
See CertiK’s auditsSomething wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.
Tell us