--- eip: 8204 title: Token Payments description: Payment signaling for token transfers in commerce applications. author: Jacopo Ranalli (@jacopo-eth) , Domenico Macellaro (@zerohex-eth) discussions-to: https://ethereum-magicians.org/t/erc-8204-token-payments/28046 status: Draft type: Standards Track category: ERC created: 2026-03-19 --- ## Abstract This ERC defines a standard way to signal token payments. It introduces a `Payment` event for individual payments, an optional `Order` event for transaction-level metadata, conventions for typed metadata payloads, interfaces and implementation modes for token standards and settler contracts. These events let applications and indexers distinguish payments from generic transfers, and associate context such as the buyer, and payment or order metadata. ## Motivation Transfer events in existing token standards do not distinguish a payment for goods, services or other expenses from a simple transfer. Payment-specific data matters for commerce applications such as wallets, checkout systems, merchant tooling, and accounting systems. Indexers could use the additional context to automatically categorize onchain payments, and applications to provide a better user experience to buyers and merchants. Typically, offchain payment systems offer this distinction as a first-class primitive. On Ethereum, token transfers have generally lacked a standard way to express it. This ERC introduces a standard payment signal without changing the underlying token transfer semantics. ## Specification The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. ### Definitions - **native transfer event:** the canonical token movement event defined by the underlying token standard. - **native transfer arguments:** the ordered argument list of the native transfer event, including argument names, types, and order. - **indexed-argument limit:** the maximum number of indexed event arguments permitted for an event. For standard EVM events, this limit is `3`. - **buyer:** the intended beneficiary of the goods or services, as asserted by the caller or settler contract. - **payer:** the address funding the payment. When the corresponding transfer semantics identify a source address, `payer` refers to that source address. - **recipient:** the logical payee represented by a payment integration. It is not necessarily the same as the native transfer `to` address. - **currency:** the token contract address for the asset represented by the payment entry. - **payment amount:** the asset quantity represented by a `Payment` entry under the underlying token standard's native transfer semantics. It MAY be scalar or structured; for a transfer of a single non-fungible asset, it is `1`. - **payment data:** ABI-encoded payment metadata payloads. - **order data:** ABI-encoded order metadata payloads. `payer` and `recipient` are semantic roles in this ERC. They do not rename native transfer arguments such as `from` and `to`, which MUST be preserved where this ERC derives events or functions from an underlying token standard. ### Events For a token standard whose native transfer event is: ```solidity event Transfer(T1 a1, T2 a2, ..., Tn an); ``` the corresponding `Payment` event MUST be: ```solidity event Payment(T1 a1, T2 a2, ..., Tn an, address buyer, address currency, bytes[] data); ``` `Payment` represents an individual payment entry. The leading arguments of `Payment` MUST match the native transfer event exactly in name, type, and order. `buyer`, `currency`, and `data` MUST be appended in that order. `currency` and `data` MUST NOT be indexed. If the native transfer event uses fewer than the indexed-argument limit, `buyer` MUST be indexed. Otherwise, if the native transfer event indexes an argument representing the transfer source (`payer`), that argument MUST NOT be indexed and `buyer` MUST be indexed. In all other cases, the leading `Payment` arguments MUST preserve the native transfer event's indexedness. ```solidity event Order(address indexed buyer, address indexed payer, bytes[] data); ``` `Order` MAY be emitted for metadata that applies to multiple payments in a transaction. `Payment.data` and `Order.data` MUST use the Metadata Payload Encoding defined below. ### Metadata Payload Encoding `Payment.data` and `Order.data` are ordered collections of metadata payloads. The set of valid metadata types is extensible. It is RECOMMENDED to standardize them and their characteristics by publishing them as ERCs. Applications MAY support any set of standard or custom metadata types. A metadata type is a 4-byte value (`bytes4`). It MUST be defined as the Solidity function selector of a descriptive function signature, so as to give the type a human-readable name and a standard payload encoding corresponding to the ABI encoding of the function arguments. `Payment.data` and `Order.data` MUST be encoded as `bytes[]`, where each element is exactly one metadata payload. Each payload MUST be the ABI encoding of exactly one metadata function call: the first 4 bytes are the metadata type, and the remaining bytes are the ABI-encoded arguments for that type. Producers MUST NOT concatenate multiple metadata payloads into the same array element. `Payment.data` and `Order.data` with no metadata (an empty metadata list) MUST be considered valid. Applications and indexers that do not recognize a metadata type MAY ignore that payload entry. `Memo`, `MerchantReference`, `PurchaseReference`, and `URI` SHOULD appear at most once in a given metadata collection. `MerchantReference` and `PurchaseReference` are opaque values. Applications and indexers MAY store them without interpretation and MAY apply application-specific conventions or registry lookups when interpretation is needed. The following metadata types are standardized: ```solidity interface CommerceMetadata { function Memo(string memory text) external; function MerchantReference(bytes32 value) external; function PurchaseReference(bytes32 value) external; function URI(string memory value) external; } ``` - `Memo(string)` MAY be used in `Payment.data` or `Order.data` for a human-readable description, whether brief or long-form. When it begins with `