ParaSpace Lending V1 security record

BNB Chain, Ethereumpara.spaceXGitHub

As of 3 Nov 2026, ParaSpace Lending V1 has 2 audit reports by 1 security firm on Trustblock; the latest is by CertiK, dated 23 Dec 2022. Those reports list no open critical findings.

Critical findings
No open critical findings
Across the 2 reports, 2 critical findings were reported. Each is marked fixed by the firm that reported it. See the findings
Open the latest report
Your label, live
Get the label for your site

Record last updated . It holds audits and findings, contracts and incidents up to Nov 2026. Trustblock does not review code.

Audits2

Newest first

AuditSecurity firmDateScopeCritical findingsReport
ParaSpace (Audit #3)Security firm: CertiKDate: Scope: Files named in the reportNo open critical findingsOpen · skynet.certik.com
ParaSpace - NFT Money MarketSecurity firm: CertiKDate: Scope: Files named in the report2 critical findings, fixedOpen · skynet.certik.com

Findings38

Statuses are the security firm’s own, from each report.

SeverityOpenAcknowledgedFixed
Critical002
High022
Medium104
Low17010
Critical`NToken.permit()` doesn't check the token ownerParaSpace - NFT Money Market · CertiK · 25 Oct 2022Fixed

```solidity=286 require(owner == ecrecover(digest, v, r, s), Errors.INVALID_SIGNATURE); _nonces[owner] = currentValidNonce + 1; _approve(spender, value); ``` `NToken.permit()` allows the `spender` to transfer in the future the token with id `value` if correctly signed by `owner` message provided. However, it is not checked that `_isApprovedOrOwner(owner, value)`. As a result, anyone can get approval for any token. The `value` argument name is misleading.

Open the report
CriticalPotential Flashloan AttackParaSpace - NFT Money Market · CertiK · 25 Oct 2022Fixed

The following check is performed when borrowing if the interest rate mode is stable: ```solidity require( !params.userConfig.isUsingAsCollateral( reservesData[params.asset].id ) || params.reserveCache.reserveConfiguration.getLtv() == 0 || params.amount > IToken(params.reserveCache.xTokenAddress).balanceOf( params.userAddress ), Errors.COLLATERAL_SAME_AS_BORROWING_CURRENCY ); ``` So the current attack only applies if the interest rate mode is variable. The following slightly tweaked attack works in either interest rate mode. Attack Flow: 1. An exploiter creates two contracts A and B. 2. Contract A flash loans 100 ETH from other protocols, it then supplies 100 ETH and is minted 100 ETH of PToken (assuming the asset is ETH), they then borrow 80% of the locked ETH in some other Token. (assuming up to 80% can be borrowed). 3. Contract A transfers the borrowed Token's to Contract B, which then swaps them for around 80 ETH. 4. Contract B then supplies 80 ETH and is minted 80 ETH worth of PToken. Subsequently, they borrow 64 ETH worth of some other Token, which they then swap for around 64 ETH. 5. Contract B uses its own 80 ETH worth of PToken to pay off Contract A's debt. 6. Contract A withdraws all 100 ETH supplied by burning its 100 ETH of PToken, as its debt is now cleared. 7. Contract A repays the 100 ETH flash loan. 9. The exploiter gains around 64 ETH in profit as they still have the 64 ETH in contract B. (Not accounting for the swap and flash loan fees.)

Open the report
HighNo Access Restriction On Function `removeFeeder()ParaSpace (Audit #3) · CertiK · 23 Dec 2022Fixed

The function `removeFeeder()` in the aforementioned line can be called by anyone as it has no access restriction. This enables anyone to call this function to remove the price feeder and revoke the `UPDATE_ROLE` of the feeder. **Attack Scenario** Anyone is free to remove any Oracle price feeder. **Proof of concept** PoC exploit code is not necessary in this case. The finding is straightforward and about access restriction on function.

Open the report
HighPotential Reentrancy AttackParaSpace - NFT Money Market · CertiK · 25 Oct 2022Fixed

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. For example, the hacker can call the `withdrawERC721()` method and use the hook `_checkOnERC721Received` method of the ERC721 receiver to reenter the method `liquidationERC721()`.

Open the report
MediumNot Withdraw Rewards While Transferring AssetParaSpace (Audit #3) · CertiK · 23 Dec 2022Fixed

The functions `transfer()` and `transferFrom()` are not overridden to withdraw all staked and pending rewards before transferring the asset. So the original owner of the asset will potentially lose some staking and reward tokens. **Proof of concept** 1. User A supplies the BAYC/MAYC and gets the BAYC/MAYC NToken, then stakes the APE with their BAYC/MAYC NToken. 2. User A transfers the BAYC/MAYC NToken to User B. User A will lose the staked APE coins and rewards.

Open the report
MediumFunctions Not RestrictedParaSpace - NFT Money Market · CertiK · 25 Oct 2022Fixed

```solidity=1 function batchBuyWithCredit( bytes32[] calldata marketplaceIds, bytes[] calldata payloads, DataTypes.Credit[] calldata credits, address onBehalfOf, uint16 referralCode ) external payable virtual override nonReentrant { ``` ```solidity function buyWithCredit( bytes32 marketplaceId, bytes calldata payload, DataTypes.Credit calldata credit, address onBehalfOf, uint16 referralCode ) external payable virtual override nonReentrant { ``` Both of the `buyWithCredit()` and `batchBuyWithCredit()` are external functions without any validation. Any user can call them with any parameter. At the same time, these functions do not validate the caller's allowance from the `onBehalfOf` account. Not verifying that the caller has allowance from the `onBehalfOf` account could be a vulnerability.

Open the report
MediumIncompatibility with Deflationary TokensParaSpace - NFT Money Market · CertiK · 25 Oct 2022Fixed

When transferring deflationary ERC20 tokens, the input amount may not be equal to the received amount due to the charged transaction fee. For example, if a user sends 100 deflationary tokens (with a 10% transaction fee), only 90 tokens actually arrived at the contract. However, a failure to discount such fees may allow the same user to withdraw 100 tokens from the contract, which causes the contract to lose 10 tokens in such a transaction. Reference: https://thoreum-finance.medium.com/what-exploit-happened-today-for-gocerberus-and-garuda-also-for-lokum-ybear-piggy-caramelswap-3943ee23a39f ```solidity=200 IERC20(token0).safeTransferFrom(sender, address(this), amountAdd0); ``` - Transferring tokens by `amountAdd0`. ```solidity=228 uint256 refund0 = amountAdd0 - amount0; ``` - The `amountAdd0` appears to be used for bookkeeping purposes without compensating the potential transfer fees. --- ```solidity=203 IERC20(token1).safeTransferFrom(sender, address(this), amountAdd1); ``` - Transferring tokens by `amountAdd1`. ```solidity=233 uint256 refund1 = amountAdd1 - amount1; ``` - The `amountAdd1` appears to be used for bookkeeping purposes without compensating the potential transfer fees. --- ```solidity=264 _increaseLiquidityCurrentRange( tokenId, amountAdd0, amountAdd1, amount0Min, amount1Min ); ``` - Transferring tokens by `amountAdd0`. - This function call executes the following operation. - In `NTokenUniswapV3._increaseLiquidityCurrentRange`, - `IERC20(token0).safeTransferFrom(sender, address(this), amountAdd0);` ```solidity=264 _increaseLiquidityCurrentRange( tokenId, amountAdd0, amountAdd1, amount0Min, amount1Min ); ``` - This function call executes the following operation. - In `NTokenUniswapV3._increaseLiquidityCurrentRange`, - `uint256 refund0 = amountAdd0 - amount0;` - The `amountAdd0` appears to be used for bookkeeping purposes without compensating the potential transfer fees.

Open the report
MediumPotential Financial LossParaSpace - NFT Money Market · CertiK · 25 Oct 2022Fixed

```solidity=279 if (reserve.xTokenAddress == address(0)) { address underlyingAsset = INToken(token) .UNDERLYING_ASSET_ADDRESS(); reserve = reservesData[underlyingAsset]; bool isNToken = reserve.xTokenAddress == token; require(isNToken, Errors.ASSET_NOT_LISTED); if (!userConfig.isUsingAsCollateral(reserve.id)) { userConfig.setUsingAsCollateral(reserve.id, true); } // No need to supply anymore because it's already NToken continue; } ``` According to the `if` condition `reserve.xTokenAddress == address(0)` of the above statement from method `_repay()`, this means that the purchased token is an `NToken`. However, this token will be locked in the `pool` of the contract forever. This may be incorrect.

Open the report
LowRedundant Modifier `onlyWhenAssetExisted` On FuncParaSpace (Audit #3) · CertiK · 23 Dec 2022Fixed

The modifier `onlyWhenAssetExisted` is redundant on the function `removeAsset()`, because it is declared on the internal function `_removeAsset()`.

Open the report
LowWithdraw Balance Is Not Properly CalculatedParaSpace (Audit #3) · CertiK · 23 Dec 2022Fixed

The withdrawal amount of Ape coins from the staking contract is not properly calculated, this will transfer all Ape coins left in the current contract to the recipient. The token balance should be checked before and after the withdrawal. ```solidity=38 function withdrawBAKC( ApeCoinStaking _apeCoinStaking, uint256 poolId, ApeCoinStaking.PairNftWithAmount[] memory _nftPairs, address _apeRecipient ) external { ApeCoinStaking.PairNftWithAmount[] memory _otherPairs = new ApeCoinStaking.PairNftWithAmount[](0); if (poolId == BAYC_POOL_ID) { _apeCoinStaking.withdrawBAKC(_nftPairs, _otherPairs); } else { _apeCoinStaking.withdrawBAKC(_otherPairs, _nftPairs); } uint256 balance = _apeCoinStaking.apeCoin().balanceOf(address(this)); _apeCoinStaking.apeCoin().safeTransfer(_apeRecipient, balance); } ```

Open the report
Show 8 more
LowDiscussion On Borrow With CreditParaSpace - NFT Money Market · CertiK · 25 Oct 2022Fixed

```solidity=424 // Pool.sol function acceptBidWithCredit( bytes32 marketplaceId, bytes calldata payload, DataTypes.Credit calldata credit, address onBehalfOf, uint16 referralCode ) external virtual override nonReentrant { ``` Currently, the functions `buyWithCredit()`, `batchBuyWithCredit()`, `acceptBidWithCredit()`, and `batchAcceptBidWithCredit()` can be called by anyone and there is no authorization between caller and `onBehalfOf`. In our opinion, these functions would be invoked in relevant gateway contracts. - The functions `buyWithCredit()` and `batchBuyWithCredit()` could be invoked in `WETHGateway.sol`. - The functions `acceptBidWithCredit()` and `batchAcceptBidWithCredit()` could be invoked in `WPunkGateway.sol`. ```solidity // WPunkGateway.sol function acceptBidWithCredit( bytes32 marketplaceId, bytes calldata payload, DataTypes.Credit calldata credit, uint256[] calldata punkIndexes, uint16 referralCode ) external nonReentrant { ...... Pool.acceptBidWithCredit( marketplaceId, payload, credit, msg.sender, referralCode ); } ```

Open the report
LowIncorrect Conditional StatementParaSpace - NFT Money Market · CertiK · 25 Oct 2022Fixed

Referring to the comments, the logic may occur in a normal supplyERC721 pool transaction. However, when the operator is `POOL` the transaction will be reverted. That means the `UniswapV3` tokens cannot be supplied in this protocol. ```solidity=47 // if the operator is the pool, this means that the pool is transferring the token to this contract // which can happen during a normal supplyERC721 pool tx if (operator == address(POOL)) { revert(Errors.OPERATION_NOT_SUPPORTED); } ```

Open the report
LowLack of Account ValidationParaSpace - NFT Money Market · CertiK · 25 Oct 2022Fixed

```solidity=149 function supplyERC721FromNToken( address asset, DataTypes.ERC721SupplyParams[] calldata tokenData, address onBehalfOf ) external virtual override nonReentrant { SupplyLogic.executeSupplyERC721FromNToken( _reserves, _usersConfig[onBehalfOf], DataTypes.ExecuteSupplyERC721Params({ asset: asset, tokenData: tokenData, onBehalfOf: onBehalfOf, actualSpender: address(0), referralCode: 0 }) ); } ``` In the function `Pool.supplyERC721FromNToken()`, any user can call it to supply ERC721 from other accounts without restriction.

Open the report
LowMissing Zero Address ValidationParaSpace - NFT Money Market · CertiK · 25 Oct 2022Fixed

Addresses should be checked before assignment or external call to make sure they are not zero addresses. --- ```solidity=75 (bool success, ) = newImplementation.delegatecall(data); ``` - `newImplementation` is not zero-checked before being used. --- ```solidity=75 _treasury = treasury; ``` - `treasury` is not zero-checked before being used. --- ```solidity=76 _underlyingAsset = underlyingAsset; ``` - `underlyingAsset` is not zero-checked before being used. --- ```solidity=75 _treasury = treasury; ``` - `treasury` is not zero-checked before being used.

Open the report
LowPotential Incorrect Price RiskParaSpace - NFT Money Market · CertiK · 25 Oct 2022Fixed

- GenericLogic.sol contract `_getUserBalanceInBaseCurrency()` method ```solidity uint256 assetPrice; ...... if (INToken(xTokenAddress).getAtomicPricingConfig()) { uint256 totalBalance = INToken(xTokenAddress).balanceOf(user); for (uint256 index = 0; index < totalBalance; index++) { uint256 tokenId = IERC721Enumerable(xTokenAddress) .tokenOfOwnerByIndex(user, index); if ( ICollaterizableERC721(xTokenAddress).isUsedAsCollateral( tokenId ) ) { // TODO use getTokensPrices instead if it saves gas assetPrice = IPriceOracleGetter(oracle).getTokenPrice( currentReserveAddress, tokenId ); balance += assetPrice; } } } ...... unchecked { return (balance / assetUnit, assetPrice); } ``` According to the above statement, the method `_getUserBalanceInBaseCurrency()` will return the `assetPrice` of the last item. And referring to the client's technical documentation, `atomic pricing` is defined as: the first notable difference between typical floor-based and UniV3 is that each token has a different price based on the ERC20 token composition inside the LP token. This means that each UniV3 token has a different price, and in the statement logic, the local variable `assetPrice` will be overwritten by the value of the last item in the loop. We understand that for NFT assets, the user cannot borrow them now. However, the result returned by this method is incorrect and might not make sense.

Open the report
LowRedundant `else` ClauseParaSpace - NFT Money Market · CertiK · 25 Oct 2022Fixed

Refer to the `SupplyLogic.executeUseReserveAsCollateral()` method is used to set whether the ERC20 asset can be collateralized, and there is no similar statement for the ERC721 asset, we think the ERC721 asset the "else" statement is redundant.

Open the report
LowRedundant CodeParaSpace - NFT Money Market · CertiK · 25 Oct 2022Fixed

The require statement is duplicated in `validateSupplyBase()` with the related statements in `validateSupplyERC20()` and `validateSupplyERC721()`.

Open the report
LowUnchecked ERC-20 `transfer()`/`transferFrom()` CaParaSpace - NFT Money Market · CertiK · 25 Oct 2022Fixed

The return value of the transfer()/transferFrom() call is not checked. ```solidity=155 IERC20(token).transfer(to, amount); ```

Open the report

Contracts

No deployed address is recorded for these audits.

The reports name a repository or files. Open a report to see its scope.

The repositories and files the reports name

Show this on your site

Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.

Content

Facts

Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.

Fixed as
Language

Style

Layout
Theme
Accent
Corners

Format

Size
Date
Embed as
HTML
<head>
<script src="https://widget.trustblock.run/project-widget.min.js" defer></script>
</head>
<body>
<div
data-tb-project-widget
data-tb-project-slug="paraspace"
></div>
</body>

Paste the code where the label should appear. It uses no API credit and updates by itself.

Something 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