{"id":"ERC-2309","name":"ERC-721 Consecutive Transfer Extension","status":"Final","chain":"both","category":{"id":"nft","name":"NFT Capabilities","description":"What NFTs can do beyond basic ownership"},"journeyStages":[{"id":"executing","name":"Executing Transactions","description":"Performing on-chain actions"},{"id":"status","name":"Status & Confirmation","description":"Waiting for and confirming outcomes"}],"uxImpact":"Batch minting/transfer events — efficiently create or move thousands of consecutive tokens in one transaction.","officialUrl":"https://ercs.ethereum.org/ERCS/erc-2309","lastReviewed":"2026-09-18","lastUpdated":"2026-09-18","kind":"proposal","hasDetailedContent":true,"content":{"id":"ERC-2309","specConstraints":[{"id":"erc-2309-range-transfer-authority","constraint":"The transaction executor must own every token in the consecutive range or be an approved operator for the owners.","appliesTo":"account-implementation","normativity":"normative","specQuote":"The address executing the transaction **MUST** own all the tokens within the range of `fromTokenId` and `toTokenId`, or **MUST** be an approved operator to act on the owners behalf.","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-2309#specification","designImplication":"Treat a range transfer as one authority-sensitive operation. Show the collection, token range, and acting account before offering a batch action; do not imply that consecutive range encoding bypasses ERC-721 ownership checks."},{"id":"erc-2309-consecutive-range","constraint":"The fromTokenId and toTokenId values must describe a consecutive token-id range.","appliesTo":"application","normativity":"normative","specQuote":"The `fromTokenId` and `toTokenId` **MUST** be a consecutive range of tokens IDs.","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-2309#specification","designImplication":"Use a range summary only when the ids are consecutive, and expose the endpoints so users can understand exactly which tokens the batch covers."},{"id":"erc-2309-mint-zero-address","constraint":"A consecutive mint sets fromAddress to the zero address.","appliesTo":"infrastructure","normativity":"normative","specQuote":"When minting/creating tokens, the `fromAddress` argument **MUST** be set to `0x0` (i.e. zero address).","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-2309#specification","designImplication":"Let indexers and activity surfaces label a zero-address range as a mint, while keeping the token range visible for audit and expansion."},{"id":"erc-2309-burn-zero-address","constraint":"A consecutive burn sets toAddress to the zero address.","appliesTo":"infrastructure","normativity":"normative","specQuote":"When burning/destroying tokens, the `toAddress` argument **MUST** be set to `0x0` (i.e. zero address).","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-2309#specification","designImplication":"Let history and ownership indexing label a zero-address destination as a burn, while retaining the full consecutive range for recovery and audit."},{"id":"erc-2309-consecutive-event-exclusivity","constraint":"A consecutive transfer must not be accompanied by the ordinary Transfer event in the same emission path.","appliesTo":"infrastructure","normativity":"normative","specQuote":"when emitting the `ConsecutiveTransfer` event the `Transfer` event **MUST NOT** be emitted.","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-2309#specification","designImplication":"Index `ConsecutiveTransfer` as its own ownership update and avoid double-counting a range as both a consecutive event and individual Transfer events."},{"id":"erc-2309-event-indexing-support","constraint":"Indexers that only listen for the original Transfer event can miss ownership changes emitted through ConsecutiveTransfer.","appliesTo":"infrastructure","normativity":"backwards-compatibility","specQuote":"They would not be properly recording token ownership information that could be known by listening for the `ConsecutiveTransfer` event.","specSection":"Backwards Compatibility","specUrl":"https://ercs.ethereum.org/ERCS/erc-2309#backwards-compatibility","designImplication":"Make ownership and inventory surfaces depend on an indexer that handles both event types. If the client has not processed the range event, show the ownership state as incomplete rather than silently trusting stale holdings."},{"id":"erc-2309-extension-availability","constraint":"ERC-721 contracts MAY implement the Consecutive Transfer Extension for creation, burn, or transfer of consecutive tokens.","appliesTo":"account-implementation","normativity":"normative","specSection":"Specification","specUrl":"https://ercs.ethereum.org/ERCS/erc-2309#specification","designImplication":"Detect the extension and range-event support before presenting bulk ownership history or consecutive mint/burn tooling.","specQuote":"**ERC-721 compliant contracts MAY implement this Consecutive Transfer Extension to provide a standard event to be emitted at the time of creation, burn, or transfer of one or many consecutive tokens**"}],"sources":[{"label":"Official specification","url":"https://ercs.ethereum.org/ERCS/erc-2309","type":"official-spec"}],"lastReviewed":"2026-09-18","lastUpdated":"2026-09-18","summary":"Batch minting/transfer events — efficiently create or move thousands of consecutive tokens in one transaction.","designerTakeaways":["You can collapse consecutive mints into one Received #1–1000 activity row with expand option.","Your import progress can show Indexing 847 of 1000 tokens during large batch processing.","You can offer summary-first gallery with expand to id list for power users."],"applicability":{"whenToUse":["Mass airdrops or batch mints use consecutive ids.","Activity feed would choke on individual events.","Indexer supports range expansion."],"whenToAvoid":["Single NFT mints with no batch events.","Non-consecutive token ids in batch.","Users need immediate per-id detail without indexing wait."]},"prototypeFirst":[{"screen":"Batch receipt notification","why":"First touch after airdrop sets expectations.","covers":["Range summary","Expand ids"],"include":["You received #1–1000","Collection name","View collection CTA"]},{"screen":"Indexing progress","why":"Large batches take time to appear in gallery.","covers":["Indexing","Complete","Partial failure"],"include":["Progress bar","Count label","Retry indexing"]},{"screen":"Activity feed aggregation","why":"Feed readability for whale airdrops.","covers":["Collapsed batch","Expanded id list"],"include":["Summary row","Expand chevron","Max expand limit"]},{"screen":"Collection grid after batch","why":"Gallery must load performantly.","covers":["Virtualized grid","Representative thumbnails"],"include":["Lazy load","Sample preview strip","View all"]}],"mentalModel":[{"label":"Consecutive range","description":"Tokens #start–#end moved in one on-chain event."},{"label":"Batch event","description":"One log line represents many tokens — UI summarizes."},{"label":"Indexer expansion","description":"Backend expands range into individual holdings asynchronously."},{"label":"Summary vs detail","description":"Default summary; expand for id list when needed."},{"label":"Airdrop receipt","description":"User cares they got the drop, not 1000 duplicate lines."}],"statesToDesign":[{"state":"Batch received — indexing","trigger":"Event seen, gallery loading.","userNeed":"Know tokens are coming.","designResponse":"Indexing N tokens progress indicator."},{"state":"Batch complete in gallery","trigger":"Indexer finished.","userNeed":"Browse collection.","designResponse":"Full grid with count badge on collection."},{"state":"Activity feed collapsed","trigger":"Default view.","userNeed":"Scan history quickly.","designResponse":"Single batch row with range."},{"state":"Expanded id list","trigger":"User expands batch row.","userNeed":"Find specific id.","designResponse":"Paginated id list or search within range."},{"state":"Partial index failure","trigger":"Indexer timeout.","userNeed":"Recover without panic.","designResponse":"Retry indexing with support link."}],"designDecisions":[{"question":"Summary or list by default?","recommendation":"Summary in feed; full grid in collection view.","rationale":"Feed chokes on thousands of rows."},{"question":"How many ids in expand?","recommendation":"Paginate at 50; offer search by id.","rationale":"Rendering 1000 rows crashes mobile."},{"question":"Notification for batch?","recommendation":"One push: You received 1000 [Collection] tokens.","rationale":"1000 pushes would spam."}],"problemsSolved":[{"problem":"Activity feed unusable after airdrop","oldWay":"1000 identical transfer lines","newWay":"Single batch summary row","impact":"high"},{"problem":"Slow gallery after mass mint","oldWay":"UI freezes loading all items","newWay":"Progressive indexing with progress UI","impact":"high"},{"problem":"Users miss airdrop happened","oldWay":"Buried in noise or invisible","newWay":"Clear batch receipt notification","impact":"medium"}],"uxPatterns":[{"name":"Batch Activity Summary","description":"Collapsed range row in transaction history.","components":["BatchRow","RangeLabel","ExpandToggle"],"userFlow":["Airdrop lands","Feed shows one row","User expands optional","Ids paginate"]},{"name":"Collection Index Progress","description":"Progress while indexer expands token range.","components":["ProgressBar","CountLabel","RetryButton"],"userFlow":["Batch detected","Progress shown","Grid populates","Complete state"]}],"antiPatterns":[{"pattern":"1000 separate activity notifications","why":"UI freeze and notification spam","instead":"Single batch summary with optional expand","severity":"critical"},{"pattern":"Empty gallery during indexing with no progress","why":"Users think airdrop failed","instead":"Indexing N tokens progress bar","severity":"high"},{"pattern":"Rendering full id list on mobile expand","why":"Browser crash","instead":"Paginated expand with search","severity":"high"}],"vocabulary":[{"use":"Received 500 tokens","avoid":"ConsecutiveTransfer event id 1-500","why":"Outcome not event name."},{"use":"Loading your collection","avoid":"Indexer expanding range","why":"User-facing wait copy."},{"use":"Show token IDs","avoid":"Expand batch mint log","why":"Power user optional action."}],"technicalNotes":"ERC-2309 consecutive events require indexer range expansion; never render unbounded id lists.","relatedStandards":[{"id":"ERC-721","relationship":"Consecutive transfer extension for ERC-721"},{"id":"ERC-1155","relationship":"Batch transfers in multi-token standard"}]},"sources":[{"label":"Official specification","url":"https://ercs.ethereum.org/ERCS/erc-2309","type":"official-spec"},{"label":"Discussion search","url":"https://ethereum-magicians.org/search?q=ERC-2309","type":"discussion"}],"urls":{"canonical":"https://www.eipsfordesigners.com/standards/ERC-2309","markdown":"https://www.eipsfordesigners.com/standards/ERC-2309/content.md","agent":"https://www.eipsfordesigners.com/standards/ERC-2309/agent.md","api":"https://www.eipsfordesigners.com/api/standards/ERC-2309","official":"https://ercs.ethereum.org/ERCS/erc-2309","discussion":"https://ethereum-magicians.org/search?q=ERC-2309"},"freshness":{"lastReviewed":"2026-09-18","lastUpdated":"2026-09-18"}}