ERC-7439 Anti-scalping tickets (Prevent ticket touting)
Final · NFTs & multi-token · created 2023-07-28
A standard that issues concert tickets as NFTs but blocks even the owner from transferring them freely, so tickets can be resold only through authorized sales agents or approved resale platforms.
Why it is needed
Industrial-scale scalping is a long-standing problem. It brings fraud and crime, wastes social resources, and does great harm to artists and the wider industry. Governments in many countries have begun to pass laws regulating scalpers, but the effect is limited, and tickets to shows where resale is banned, or tickets the seller does not yet even own, are sold speculatively, producing enormous illegal profits. Paper tickets and vouchers in emails are easy to forge or pass along for resale. Ordinary NFT tickets can also be transferred freely by their owners, so they do not stop scalpers from hoarding tickets and reselling them. This standard prohibits transfers by all parties, including the ticket owner. Only specific accounts, such as sales agents, managers, promoters, and approved resale platforms, can move tickets, which keeps scalpers from handing tickets off at will. Buyers can resell through an authorized channel at or below the price they originally paid.
What to implement
function safeMint(address to, bytes memory signature) external;: Issues a ticket with the recipient address and a signature. When the status changes, it must emit a TokenStatusChanged event.
In depth
ERC-721 extension and transfer restrictions
This standard is an extension of ERC-721. It defines a range of functions that let ticketing agencies or show organizers take preventive measures against audiences being exploited in the scalper market. It prohibits ticket transfers by all parties, including the owner, and allows only specific accounts to transfer. In the reference implementation, _beforeTokenTransfer lets a call through only when the caller is PARTNER_ROLE or when from or to is the zero address (mint or burn), and it reverts every other transfer with "ERC7439: You cannot transfer this NFT!". PARTNER_ROLE can be held by a ticketing agency, a state supervisor, or a legitimate secondary-sale platform designated by the agency. Because a buyer cannot tell whether a seller is a scalper, direct transfers by the owner are not allowed.
TokenInfo struct
Each ticket has a TokenInfo record attached. The signature is data signed with the user's private key or the agency's private key. Implementers decide for themselves what to sign, and are encouraged to use it as proof of ticket validity. tokenStatus is the current state of the ticket, and expireTime is recommended to be set to the end time of the show. The feature is strengthened by letting only authorized accounts, excluding the owner, modify this record.
The four TokenStatus states
enum TokenStatus is Sold(0), Resell(1), Void(2), Redeemed(3). When a ticket is sold, it must be changed to Sold, and in this state the ticket is valid. When it is listed on the secondary market, it must be changed to Resell, and it is also valid. If the owner is involved in illegal trading, the state must be set to Void, and a ticket in this state is invalid. When a ticket is used, changing it to Redeemed is recommended. The organizer can compare the token issuance amount against TokenStatus to keep track of actual sales at all times.
safeMint: public minting and transparent primary sales
safeMint(address to, bytes memory signature) is a public function, intended to let anyone mint a ticket directly at face value in a transparent way. On minting, a signature that only the buyer's account or the agency can interpret is stored in TokenInfo to prove ticket validity, and the state becomes Sold. When the state changes, the TokenStatusChanged event must be emitted. The design comes from the view that an open and transparent primary sales channel is needed to stop the practice of ticketing agencies colluding with scalpers to build hype, or siphoning off free and VIP tickets.
changeState: resale verification and liquidity
changeState(uint256 tokenId, bytes memory signature, TokenStatus tokenStatus, uint256 newExpireTime) can be called only by a specific role, and the TokenStatusChanged event must be emitted when the state changes. The reference implementation restricts it with onlyRole(PARTNER_ROLE). To put a sold ticket on the secondary market, the owner must ask the secondary market to change its state to Resell. This state change process is itself a form of official verification for secondary-sale tickets and a safeguard for second-hand buyers. Tickets can be minted directly on the primary market and, when needed, transferred on a transparent secondary market, which supports ticket liquidity.
Entry verification and refund and cancellation handling
Before the show, the ticketing agency verifies the ticket by its signature to confirm that the attendee is genuine. TokenStatus must be Sold, and expireTime lets the agency tell whether the attendee has come to the right performance. If verification passes, the agency changes the state to Redeemed. The design also lets refunds be handled simply even in the extreme case where a show is cancelled. The reference implementation deletes the token's tokenInfo when it is burned.
Interface identification and compatibility
When supportsInterface is called with 0x15fbb306, it must return true. This standard requires ERC-165 and ERC-721 and is compatible with ERC-721. The reference implementation inherits OpenZeppelin ERC721 and AccessControl, and supports both the IERC7439 interface ID and the interface IDs of the parent contracts. The status of the original is Final, a finalized standard.