Loading
PRICE DISCUSSION DOCS
APP
Updated 2026-10-11·View as Markdown

Contracts — PaymentSplitter

One PaymentSplitter exists per Project, deployed by the factory beside the Project's ERC-721. It has one job: receive the Project's 5% EIP-2981 secondary royalty and divide it 60/40 between artist and platform — 3% of each sale to the artist, 2% to the platform. No admin, no upgrade, permanent.

Design

  • receive() is deliberately empty. Marketplaces forward royalties however they like — including with the 2,300-gas stipend of a plain transfer. Any storage write in receive() would make those payments (and potentially the sales carrying them) revert, so shares are computed at withdrawal time from cumulative accounting instead of banked per receipt.
  • Cumulative accounting. Lifetime received = current balance + everything withdrawn. The artist's lifetime entitlement is 60% of that; the platform's is the exact complement, so no wei is ever stranded by rounding. Pending = entitlement − already withdrawn; both entitlements are monotonic, so pending can never underflow.
  • Withdrawals are permissionless. Funds can only flow to the immutable artist address or the factory's live platform wallet — there is nothing for a third-party caller to gain, so anyone may trigger a payout.
  • The platform side reads the factory live. A platform-wallet rotation on the factory applies identically to accumulated and future royalties, mirroring the primary path's symmetry.

Interface

// ETH
function totalReceived() external view returns (uint256);
function pendingArtist() external view returns (uint256);
function pendingPlatform() external view returns (uint256);
function withdrawArtist() external;
function withdrawPlatform() external;

// ERC-20 (canonically WETH from accepted offers) — same accounting, per token
function totalReceivedERC20(address token) external view returns (uint256);
function pendingArtistERC20(address token) external view returns (uint256);
function pendingPlatformERC20(address token) external view returns (uint256);
function withdrawERC20Artist(address token) external;
function withdrawERC20Platform(address token) external;

// Blur Pool — Blur pays bid-fill fees as pool balance, which has no transfer()
function redeemBlurPool() external;

ERC-20 accounting is kept independently per token with independent artist and platform paths, so one recipient being unable to receive a given token (a transfer blocklist, for example) never strands the other's share.

Blur pays fees on bid fills in Blur Pool balance, and Blur Pool has no transfer(), so the token path can't move it. redeemBlurPool() — callable by anyone — withdraws the splitter's whole pool balance to itself as ETH, and the ETH path splits it 60/40 like any other royalty. Supported royalty tokens are standard single-address ERC-20s (WETH). Rebasing, fee-on-transfer and double-entry-point tokens are unsupported: their balance can be stranded or split wrong between the recipients, not only stuck.

Further reading