Azuki and ERC721A
Azuki's team developed ERC721A for its January 12, 2022 NFT sale. The implementation reduces repeated ownership storage writes when sequential NFTs go to one recipient. Ordinary minting still emits an event per token, so larger batches continue to add execution costs.
Section links
- Token Interface
- ERC-721, including the metadata extension.
- Contract Version
- ERC721A Contracts v4.3.0.
Shared Ownership Storage
ERC721A v4.3.0's ordinary mint function initializes ownership at a sequential batch's first token ID. That entry packs the recipient address, timestamp and ownership flags into one storage value. The function also updates the recipient's balance and minted count together in a packed account record.
The remaining token IDs can share that initial ownership information without separate entries at mint time. For an existing sequential token without its own initialized entry,
ownerOf
searches backward to find the applicable record. Each NFT still has its own token ID.
A batch spreads those shared storage updates across its NFTs. Separate mint calls each initialize their own starting ownership entry. The ordinary
_mint
path still loops through every token to emit a
Transfer
event. Fewer ownership writes therefore reduce one part of the cost without making total gas use constant.
Ownership Lookups and Transfers
An
ownerOf
lookup may scan several entries when a token sits far from its batch's initialized record. The contract comments describe the initial lookup cost as proportional to the maximum mint batch size. Later transfers initialize more ownership entries, which can shorten subsequent searches.
A
transferFrom
call writes an explicit ownership entry for the token that moves. When the next token already exists and lacks an initialized entry, the function copies the previous ownership record there. That neighboring NFT keeps its existing owner.
Minting Paths and Receiver Checks
The internal functions
_mint
,
_safeMint
and
_mintERC2309
require a nonzero recipient and a quantity greater than zero.
-
_mintemits aTransferevent for each token. It does not callonERC721Receivedto check whether a contract recipient accepts the NFTs. -
_safeMintuses the ordinary mint logic and adds receiver checks when the recipient has contract code. It callsonERC721Receivedfor every token. Each callback must return the expected selector, or the mint reverts. -
_mintERC2309emits oneConsecutiveTransferevent for the batch. Its ERC-721-compliant use applies only during contract creation. Version 4.3.0 limits this helper to 5,000 tokens per call.
Receiver callbacks add work beyond the shared ownership updates, so the chosen minting path affects gas use. The creation-only ERC-2309 helper has different event handling from an ordinary public mint.
The
_mintSpot
function handles individual non-sequential token IDs above the
_sequentialUpTo()
boundary in v4.3.0. Its default boundary leaves no available spot range; a derived contract must override it to enable that path. Each spot mint initializes its own ownership entry, separately from the sequential batch mechanism.
Collection Integration
ERC721A supplies internal helpers, while the collection contract defines its public mint entry point. The second argument in
_mint(to, quantity)
specifies how many tokens to create for that recipient. The README's minimal example forwards that count without enforcing payment, allowlist eligibility or a collection supply cap.
Minting Checklist
-
If the recipient has contract code, include the per-token receiver callbacks when assessing
_safeMint's cost. -
If minting occurs after contract creation, exclude
_mintERC2309to preserve ERC-721 compliance. -
When migrating to version 4.x with OpenZeppelin extensions, review
supportsInterfacecompatibility, especially for ERC2981 integrations. - If a sale requires payment or restricts eligibility or supply, implement those checks around the internal mint call.