fix: Security audit fixes for Autolisting.sol#32
Open
rroland10 wants to merge 1 commit into
Open
Conversation
Address 13 vulnerabilities across Dex223AutoListing, Dex223CoreAutoListing,
Dex223OwnableListing, and AutoListingsRegistry contracts:
Critical:
- Add onlyOwner access control to updateMe() (was publicly callable)
- Add nonReentrant guard to list() and listSingle() against reentrancy
via malicious ERC-20 tokens or registry calls
- Fix address(0) token mapping pollution in checkListing() that corrupted
isListed() results for all subsequent tokens
High:
- Fix double-charge bug: replace || with _isFullyListed() helper so users
are only charged when a token is genuinely not fully listed
- Add excess ETH refund in list()/listSingle() to prevent permanent loss
of overpaid native currency
- Reject ETH sent alongside ERC-20 token payments to prevent stuck funds
- Remove arbitrary registry replacement from OwnableListing.updateMe()
- Fix listTokenByOwner() to actually update listed_tokens/tokens/num_listed_tokens
state via checkListing()
Medium:
- Add transferOwnership() with zero-address check to all listing contracts
- Replace payable().transfer() with .call{value:}() in extractTokens()
to support multisig/contract owners
Low:
- Fix tokenUnanned typo -> tokenUnbanned in AutoListingsRegistry
- Standardize onlyOwner modifier across all contracts
- Add descriptive error messages to all require statements
Also adds hardhat compiler overrides for Revenue_old.sol and RevenueV1.sol
to fix pre-existing compilation config issues.
Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Comprehensive security audit and fixes for
contracts/dex-core/Autolisting.sol, addressing 13 vulnerabilities across all four contracts in the file:AutoListingsRegistry,Dex223AutoListing,Dex223CoreAutoListing, andDex223OwnableListing.Vulnerabilities Found & Fixed
CRITICAL Severity
1. Missing Access Control on
updateMe()— Dex223AutoListing & Dex223CoreAutoListingupdateMe()waspublicwith no access control, allowing any address to overwrite the contract URL and push arbitrary URL data to the registry. An attacker could redirect users to a phishing URL via the registry events.onlyOwnermodifier to both functions.2. Reentrancy in
list()andlistSingle()via External Callslist()performs state changes (checkListingupdateslisted_tokens,tokens,num_listed_tokens) before callingsafeTransferFrom()which makes an external call to an arbitrary ERC-20 token. A malicious token could reenterlist()during thetransferFromcall, listing tokens without paying.registry.recordListing()insidecheckListingis also an external call happening mid-execution.nonReentrantmutex modifier tolist()andlistSingle()in all contracts.3.
address(0)Token Bypass viaisListed()/listed_tokensCollisionisListed()returnslisted_tokens[_token] != 0. Since token IDs start at 1,listed_tokens[address(0)]defaults to 0 (not listed). However, the old code blindly wrotelisted_tokens[address(0)] = num_listed_tokenswhen a token hadaddress(0)for one standard, polluting the mapping slot foraddress(0). Once set to any nonzero value, every future token withaddress(0)as one of its standards would appear already listed, causingcheckListingto take the wrong branch and corrupt state.address(0)guards incheckListing()so we never write tolisted_tokens[address(0)]. Introduced_isFullyListed()helper that correctly handlesaddress(0)standards.HIGH Severity
4. Double-Charge Bug:
||Operator Causes Overcharging!isListed(_token0_erc20) || !isListed(_token0_erc223)meant users were charged the listing fee even when the token was partially listed (one standard listed, the other not). The add-second-standard operation was not meant to cost an additional fee._isFullyListed()helper that returnstrueonly when all non-zero standard addresses are already listed.5. Excess ETH Not Refunded — Permanent User Fund Loss
msg.value > toTransfer, the excess ETH was permanently trapped. The code explicitly stated// NOTE no return of excess payment. Especially problematic when price changes between TX submission and execution.msg.value - toTransfervia low-level call.6. ETH Stuck When Paying with ERC-20 Token
paymentToken != address(0)), the ETH was silently accepted and permanently stuck. Only the owner could retrieve it.require(msg.value == 0)in the ERC-20 branch and when no transfer is needed.7.
Dex223OwnableListing.updateMe()Allows Arbitrary Registry ReplacementupdateMe(string, string, address _registry)allowed the owner to pointregistryto any arbitrary address. A compromised owner could point it to a contract that silently fails, breaking event logging._registryparameter. Registry is now immutable after construction.8.
listTokenByOwner()Does Not Update Internal Stateregistry.recordListing()but never updatedlisted_tokens,tokens, ornum_listed_tokens. Tokens listed via this function were invisible toisListed(),getToken(), andnum_listed_tokens. Subsequentlist()calls would re-list the same token, corrupting state.checkListing(_token20, _token223)which properly updates all internal state.MEDIUM Severity
9. No Ownership Transfer Mechanism
transferOwnership(address _newOwner)withonlyOwnermodifier, zero-address check, andOwnershipTransferredevent to all three listing contracts.10.
extractTokens()Uses Vulnerable.transfer()for ETHpayable(msg.sender).transfer(_amount)forwards only 2300 gas, which fails if the owner is a contract (e.g., Gnosis Safe multisig). This would permanently trap ETH.LOW Severity
11. Typo:
tokenUnannedin AutoListingsRegistrytokenUnannedwas a typo fortokenUnbanned. Affects ABI consistency and off-chain tooling.tokenUnbanned().12. Inconsistent
onlyOwnerPatternrequire(msg.sender == owner)while others used the modifier. Inconsistent patterns increase the risk of missing access control on new functions.onlyOwnermodifier to all three listing contracts and used it consistently.13. Missing Error Messages in
requireStatementsAdditional Changes
Revenue_old.solandRevenueV1.sol(pragma^0.8.13mapped to compiler0.8.19) to fix pre-existing compilation config errors unrelated to this audit.Compilation
Autolisting.solcompiles with zero errors and zero warnings.MockTimeDex223Pool.sol,Dex223PoolLib.sol) remain unchanged.Test Plan
updateMe()reverts when called by non-ownerlist()correctly refunds excess ETH paymentlist()reverts when sending ETH with ERC-20 payment tokenlist()is protected against reentrancy via malicious tokencheckListing()does not write tolisted_tokens[address(0)]_isFullyListed()returns correct results for partial listingslistTokenByOwner()updateslisted_tokens,tokens,num_listed_tokensextractTokens()works with multisig/contract ownerstransferOwnership()correctly transfers and emits eventsetPaymentPrice()usesonlyOwnermodifier