All findings resolved

Security & Audit

MightyWallEscrow was reviewed line-by-line for every known Solidity exploit pattern before mainnet deployment. All findings have been resolved and verified with new regression tests.

Audit summary

48

Tests passing

1

Medium finding

3

Low findings

0

Critical / High

Date: 26 June 2026Contract: MightyWallEscrow.solSolidity: 0.8.24OZ: v5

Security properties verified

🔄

Reentrancy — not possible

Every state-changing function is guarded by OpenZeppelin's ReentrancyGuard. State is updated before any external call (Checks-Effects-Interactions). A malicious ERC1155 callback or ETH-receive hook attempting to re-enter cancelOffer, acceptOffer, or any other function is blocked by the mutex.

🔐

Contract funds cannot be drained

ETH, NFTs, and ERC-20s can only leave the escrow via acceptOffer (to the counterparties) or cancelOffer (back to the maker). There is no owner withdrawal function. The deployer cannot touch user assets.

Atomic settlement — no partial fills

acceptOffer executes every transfer in a single transaction. If any transfer fails for any reason — wrong approval, wrong balance, malicious token — the entire transaction reverts. Neither party is left holding only their side.

📐

ETH accounting is exact

msg.value must equal makerBundle.ethAmount + tip exactly when creating an offer, and takerBundle.ethAmount exactly when accepting. The contract cannot accumulate unaccounted ETH from protocol operations.

🛡️

SafeERC20 throughout

All ERC-20 transfers use OpenZeppelin's SafeERC20 wrapper, which handles tokens like USDT and USDC that do not conform to the standard return-value spec. Silent failures are impossible.

🎯

Private offers are enforced on-chain

When a taker address is specified, the smart contract rejects any other caller. A third party cannot intercept a private offer even if they know the offer ID.

Findings

No critical or high severity issues were found. All medium and low findings have been fixed and covered by regression tests.

MEDIUM

acceptOffer succeeded on non-existent offer IDs

FIXED

Calling acceptOffer with an ID that had never been created returned success and emitted a false OfferAccepted event. No assets moved, but the event would confuse off-chain indexers.

Resolution

Added a check that the offer's maker address is non-zero before proceeding. Non-existent offers now revert with OfferNotOpen.

LOW

supportsInterface violated ERC165 specification

FIXED

The ERC165 standard requires a contract to return true when its own interface ID (0x01ffc9a7) is queried. The contract returned false, making it technically non-compliant and potentially breaking some ERC1155 integrations.

Resolution

Added type(IERC165).interfaceId to the supportsInterface return value.

LOW

setFeeRecipient emitted no event

FIXED

Admin fee-recipient changes were invisible to on-chain monitors, Etherscan event feeds, and front-end listeners.

Resolution

Added a FeeRecipientUpdated(oldRecipient, newRecipient) event, emitted on every change.

LOW

ERC721 amount field not validated

FIXED

ERC721's transferFrom takes only a token ID — there is no quantity. The NFTItem.amount field was stored but silently ignored for ERC721 transfers, meaning a maker could specify amount = 99 and only 1 token would actually move.

Resolution

Added a validation that amount == 1 for every ERC721 item in both bundles. Invalid amounts now revert with InvalidAmount.

INFO

Directly-sent NFTs cannot be recovered

ACCEPTED

If a user calls nft.safeTransferFrom directly to the escrow address outside of any offer, the contract accepts the transfer but has no mechanism to return it. This is a user-error scenario, not an exploit.

Resolution

No code change. Adding an owner rescue function would introduce a new trust surface (the owner could potentially misuse it). The current behavior is the safer design choice for a trustless escrow.

Drain vector analysis

VectorStatus
ETH drain via reentrancyBlocked — nonReentrant + CEI
NFT drain via reentrancyBlocked — nonReentrant + CEI
ERC-20 drain via reentrancyBlocked — nonReentrant + CEI
Owner withdrawal backdoorNone — no such function exists
Double-accept exploitBlocked — status set before transfers
Double-cancel exploitBlocked — status set before transfers
ETH stuck in contractImpossible under normal operation
Unauthorized acceptanceBlocked for private offers by on-chain enforcement
Flash loan attackN/A — no oracle or lending integration
Signature replayN/A — pure on-chain transaction model

Contract addresses

Sepolia testnet

0x1bf1aE8D2E9D5c22b758B32C6d26cECb7544bAC3
Etherscan ↗

Mainnet

Deploying soon