Royalty Bypass Remediation
Overview
ERC-2981 royalties are advisory. The standard exposes a royaltyInfo function but never enforces payment, so a marketplace that completes a sale without querying and paying the royalty silently strips creators of revenue. The fix is to make royalty enforcement part of every settlement path that moves an NFT in exchange for value.
Related Detector: Royalty Bypass
Recommended Fix
Before (Vulnerable)
function executeSale(uint256 tokenId, uint256 price) external payable {
require(msg.value >= price);
nft.transferFrom(seller, msg.sender, tokenId);
payable(seller).transfer(price); // No royalty paid
}
After (Fixed)
function executeSale(uint256 tokenId, uint256 price) external payable {
require(msg.value >= price);
(address receiver, uint256 royalty) =
IERC2981(address(nft)).royaltyInfo(tokenId, price);
require(royalty <= price, "Royalty exceeds price");
nft.transferFrom(seller, msg.sender, tokenId);
if (royalty > 0 && receiver != address(0)) {
payable(receiver).transfer(royalty);
}
payable(seller).transfer(price - royalty);
}
The fixed version queries royaltyInfo before settling, caps the royalty at the sale price, and pays the receiver before remitting the remainder to the seller. Because enforcement lives inside the only function that transfers the token, no settlement path can skip it.
Alternative Mitigations
- Use transfer-hook or allowlist token standards (such as ERC-721C-style transfer validators) that block transfers routed through marketplaces which do not honor royalties. This shifts enforcement to the token itself rather than relying on every marketplace.
- Detect ERC-2981 support with
supportsInterface(0x2a55205a)before settling, and fall back to a default policy for tokens that do not implement it. - Route all settlement through a single audited settlement contract so royalty logic is defined once instead of duplicated per listing type.
Common Mistakes
- Querying
royaltyInfofor logging or display but never sending the funds — the detector flags exactly this gap. - Subtracting the royalty from the seller payout without actually transferring it to the receiver, which under-pays the seller and pays no one.
- Assuming peer-to-peer
transferFromcalls outside the marketplace can be policed by marketplace code. They cannot; only token-level transfer restrictions cover that path. - Hardcoding a fixed royalty percentage instead of reading the per-token value the standard returns.