Aconomy

QuillAuditsFiles named in the report

Audit · firm’s term: audit report

QuillAudits reviewed Aconomy Marketplace (Aconomy) on 24 Jun 2024: 37 findings, 0 critical; 20 marked fixed, 17 open.

No open critical findings
Open the reportOpen Aconomy Marketplace’s security record

Report hosted on quillaudits.com.

Trustblock does not review code.

Findings37

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

SeverityOpenAcknowledgedFixed
Critical000
High000
Medium403
Low13017
MediumBad debt can accrue over time giving users no incentive to pay back.Fixed

A nuanced action from the borrower may be seen when NFT deposits happen, they take loans and then do not pay back the ERC20 token loaned. There is no guarantee that users will pay back the loans they take by returning the ERC20 token along with the interest accrued over time. When enough time passes, retrieving the NFT by repaying the debt would be more expensive than the user could want to bear.

MediumThe user can choose low to no fees.Fixed

While calling BuyNFT()/executeBidOrder() the user has the ability to set _fromCollection as true/false to choose less (or even more for other users to whom the remaining amount would be sent) fees/royalties amount.This happens because depending on the if{} or else{} execution , the different royalties are used. Instead of allowing the user to toggle _fromCollection bool value in BuyNFT() and in executeBidOrder(), another variable can be added to the TokenMeta struct , where the bool value can be set while creating the sale itself. The contract will make a call on the _contractAddress address entered by the user while creating the sale and will check if it is a collection or not. The ERC-165 standard interface detection can be used for the same. In both BuyNFT() and in executeBidOrder() this boolean value can be used from the stored TokenMeta and depending on this bool value the royalties would be fetched in if or else. The implemented functionality should be checked practically as it may break the functionality for contracts that are not supporting the ERC-165 standard interface detection. Additionally the assumption is made that the piMarket is only supporting valid collections/contract implementations supported by the marketplace as discussed internally.

MediumUser’s NFT can be claimed by another actor by paying the user’s debt at any timeFixed

There is no restriction on who can pay back the loan bidded for by the lender. Any user can repay the loan to receive the NFT that the original lender added as collateral. As it is, there is no time delta between when the user is 'safe' to redeem their tokens and when an arbitrageur/liquidator could swoop in to pick up the user's collateral. This means that users who put up their NFT for sale run the risk of losing their NFT immediately another user repays the amount + interest accrued.

LowA maximum limit for fees can be set.Fixed

setAconomyPoolFee(), setAconomyPiMarketFee(), setAconomyNFTLendBorrowFee() functions are setting fees. Currently, there’s no maximum limit for fees, the maximum limit for fees can be added to increase the trust among users.

LowAdd amount 0 restriction in withdraw()Fixed

Add amount 0 restriction check in withdraw() as allowing 0 amount can create a problem where for the first time the NFT will get transferred and then on L504 the 0 amount will get added to the withdrawnAmount mapping. While calling withdraw() again, on L476 it will check that the msg.sender is owner and it will revert as the NFT is already transferred to the contract while withdrawing the ERC20 first time, even though 0 tokens were withdrawn.If this type of condition happens then the user again needs to repay 0 amount so the user will get his NFT back. There are certain situations where this issue can be triggered e.g while making a call on the front end the amount for withdraw ends up to be 0 because of some issues or some unhandled inputs, While this issue can be rare, it’s still good practice to add the require check to avoid panic at a critical moment.

LowAdd revert messageFixed

On L330 it checks that require(erc20Balance >= _value); The revert message can be added.

LowCode-test functionality mismatchFixed

In listNFTforBorrowing() and setExpectedAmount(),  _expectedAmount entered is getting checked to be greater than or equal to 10000000 (1e7) on L174, L238 respectively. Additionally on L264 in Bid(), it checks for require(_bidAmount >= 10000000, "bid amount too low"); which is also 1e7.

LowFor different _ERC20Address stake the same mapped value is usedFixed

When the user stakes the amount, on L63 the amount is getting added in validatorStakes[_validator].stakedAmount. Any ERC20 token can be used by a user while staking.When a user uses different ERC20 tokens the same mapping is used to add/store the staked amount. This mapping is then used while subtracting an amount while refunding the stake. This can create misrepresentation for the staked amount, as it’s not possible to understand how much amount of different ERC20 tokens was added by that validator.

LowFully initialize used librariesFixed

The __Pausable_init() calls __Pausable_init_unchained() which sets the _paused to false. Although the default uninitialized value is false as well, it is good practice to call the __Pausable_init within the initialize() call.The same happens with __ReentrancyGuard_init() which is empty but remains uncalled.

LowMultiple functions yield the same resultsFixed

The interfaces within CollectionMethods and PiNFT are similar for validator royalties. In PiMarket.sol, getCollectionValidatorRoyalty() and retrieveValidatorRoyalty() call the getValidatorRoyalties() method on the contract address passed in to obtain the LibShare.Share[] array details.

Show 10 more
LowOwner can refund validator’s stake with any arbitrary ERC20 token, not the staked tokenFixed

The contract owner is allowed to perform a refund of the validator’s stake at any time deemed necessary (except when the contract is paused). The validator’s staked tokens could be an arbitrary token, say DAI for example, but the owner is not constrained to refund DAI tokens. They can refund any amount of any ERC20 tokens, which could be worth nothing at all. This way, a malicious/compromised owner account could deny validators access to their staked amounts and keep the staked value stuck in the contract still as there is no central withdraw / sweep function in validatorStake.sol

Lowpublic variables can be marked as privateFixed

AconomyPoolFee(), AconomyPiMarketFee(), AconomyNFTLendBorrowFee() are public view methods used to return the values of _AconomyPoolFee,_AconomyPiMarketFee,_AconomyNFTLendBorrowFee variables respectively.Solidity creates getter methods for every public variable. the contract currently has these above-mentioned functions defined for returning the values, which makes it redundant to have other public methods created by solidity for _AconomyPoolFee,_AconomyPiMarketFee,_AconomyNFTLendBorrowFee.

LowRedundant variable:Fixed

ERC20Address address type variable from StakeDetail struct is not getting used. The AttestationRegistryAddress variable is not used in any of the contracts.

LowRemove redundant import and it's usage.Fixed

• In validatorStake.sol, Counters.sol is getting imported on L8 and L10, and using directive is getting used for it on L19. No functionality from Counters.sol is getting used hence it's redundant. Consider removing the import on L8,L10 and its usage on L19. • Same in PoolRegistry.sol, Counters.sol is getting imported on L4 and it’s using directive is on L21. It is also redundant.In piNFT.sol, IERC20 and IERC721Receiver are redundant imports. Consider removing the unused imports. • IERC20Upgradeable, IERC721ReceiverUpgradeable, Ownable are unused imports in ValidatedNFT.sol and can be removed. • IERC20, ERC721, LibCollection are unused imports in CollectionMethods.sol and can be removed. • Solidity pragma versions greater than 0.8.0 have internal underflow and overflow protection. Arithmetic operations that would result in underflow and overflow would result in an internal panic. The SafeMath library helps to protect from such issues in earlier Solidity versions but now becomes redundant as used here. • In NFTLendingBorrowing.sol, Counters.sol is getting imported on L10 and using directive is getting used for it on L23. No functionality from Counters.sol is getting used hence it's redundant. Consider removing the import on L10 and its usage on L23. • ERC721, Ownable are unused imports in PiMarket.sol and can be removed. ERC721 is imported on L5 and Ownable is imported on L6 but they are unused throughout the codebase and can be removed. • In piNFTMethods the import "./CollectionMethods.sol"; on L12 is unused hence can be removed. In poolAddress.sol , the Counters.sol is imported but never used hence can be removed.

LowRemove redundant modifierFixed

In CollectionMethods.sol, the modifier that checks if a caller is the NFT owner is unused. Although CollectionMethods is imported in the piMarket contract, piMarket.sol defines onlyOwnerOfToken separately, not using the existing function modifier in CollectionMethods.sol.

LowRequire check can be combinedFixed

On L203, L204 addERC20() function has require checks, The first statement checks that approvedValidator[_collectionAddress][_tokenId] should not be address(0) and the second one checks that msg.sender should be equal to approvedValidator[_collectionAddress][_tokenId].Instead of checking for both the conditions only one condition can be checked that is require(msg.sender == approvedValidator[_collectionAddress][_tokenId]);.  So if msg.sender == approvedValidator[_collectionAddress][_tokenId] is true then approvedValidator[_collectionAddress][_tokenId] can't be a zero address.

LowThe validator’s eligibility is not getting checked.Fixed

While adding the validator with addValidator() and lazyAddValidator(), No check is added in the piNFTMethods for checking if the validator has staked or not. There's the possibility that the validators that haven’t been staked can be also added by users in piNFTMethods.

LowUnused `bidStartTime` in TokenMeta structFixed

The variable bidStartTime in the TokenMeta struct is declared but never used. It currently takes up an extra portion of storage in every instance of the TokenMeta struct created and will never be filled.

LowUnused eventsFixed

In Fundingpool.sol, InstallmentRepaid() is unused hence can be removed.In poolAddress.sol, repaidAmounts() event is unused hence can be removed.

LowValidatedNFT mints tokens without a name and symbolFixed

The initialize() function in the ValidatedNFT contract does not initialize ERC721Upgradeable by calling __ERC721_init(). The ERC721Upgradeable is inherited in ERC721URIStorageUpgradeable. The __ERC721_init() sets the contract name and symbol in __ERC721_init(). Not calling the initializer function leaves the NFT without a name and symbol which could affect metadata parsing for each ValidatedNFT minted, even though they will have all other information intact.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Repository and files the report names (9)

  • contracts/Libraries/WadRayMath.sol
  • contracts/Libraries/DateTimeLib.sol
  • contracts/Libraries/LibPool.sol
  • contracts/Libraries/LibCollection.sol
  • contracts/utils/LibShare.sol
  • contracts/Libraries/LibCalculations.sol
  • contracts/Libraries/LibNFTLendingBorrowing.sol
  • contracts/Libraries/LibMarket.sol
  • contracts/Libraries/LibPoolAddress.sol

Part of Aconomy Marketplace’s security record

Aconomy Marketplace
Audits1 audit by 1 firmLast audit Jun 2024, by QuillAudits.
Critical findingsNo open critical findingsIn 1 report.
Open Aconomy Marketplace’s security record

Security firm

QuillAudits

350 projects reviewed · 459 reports · latest Apr 2025

See QuillAudits’s audits

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