ERC-7634: Limited Transfer Count NFT
ERC-7634 gives each NFT a hard cap on how many times it can be transferred. Once the limit is reached, further transfers revert. Used for:
- Scarcity-bound NFTs — limited-edition collectibles whose resale count is intentionally constrained (5-transfer-max series).
- Pass-along memorials — gifts intended for direct hand-off rather than secondary trading.
- Anti-flipping tokens — first-buyer / first-claimer collectibles that block speculation.
- Time-capsule NFTs — pieces that "settle" with the holder after N moves.
Different from soulbound (which blocks all transfers): ERC-7634 allows N transfers, with N typically small (1–10).
Required Interface
solidity
interface IERC7634 {
event TransferCountUpdate(uint256 indexed tokenId, uint256 newCount);
function transferCount(uint256 tokenId) external view returns (uint256);
function transferLimit(uint256 tokenId) external view returns (uint256);
function remainingTransfers(uint256 tokenId) external view returns (uint256);
}The transfer count is internal and incremented by the contract on every successful transfer; once transferCount >= transferLimit, subsequent transfers revert.
Neo Equivalent: NEP-11 + Per-Token Transfer Counter
csharp
public static BigInteger TransferCount(ByteString tokenId)
=> (BigInteger)(Storage.Get(Storage.CurrentContext, CountKey(tokenId)) ?? ByteString.Empty);
public static BigInteger TransferLimit(ByteString tokenId)
=> (BigInteger)(Storage.Get(Storage.CurrentContext, LimitKey(tokenId)) ?? ByteString.Empty);
public static BigInteger RemainingTransfers(ByteString tokenId)
=> TransferLimit(tokenId) - TransferCount(tokenId);
// Nep11Token<T> has no virtual hooks; redefine Transfer with `public new static`
// to enforce the limit + bump the per-token counter post-transfer.
public new static bool Transfer(UInt160 to, ByteString tokenId, object data = null)
{
if (RemainingTransfers(tokenId) <= 0) throw new Exception("NEP11:TransferLimitReached");
var ok = Nep11Token<TokenState>.Transfer(to, tokenId, data);
if (ok)
{
var count = TransferCount(tokenId) + 1;
Storage.Put(Storage.CurrentContext, CountKey(tokenId), count);
OnTransferCountUpdate(tokenId, count);
}
return ok;
}| ERC-7634 (Ethereum) | Neo Equivalent | Notes |
|---|---|---|
transferCount(tokenId) | TransferCount(tokenId) view | Increments on every transfer |
transferLimit(tokenId) | TransferLimit(tokenId) view | Set at mint |
remainingTransfers(tokenId) | RemainingTransfers(tokenId) view | Limit − Count |
| Mint with limit | MintWithLimit(to, tokenId, limit) | Limit is per-token |
| Pre-transfer check | OnBeforeTransfer override | NEP-11 base hook |
| Post-transfer increment | OnTransfer override | Atomic with the transfer |
Composition
- ERC-5192 — soulbound. Limit = 0 ≈ soulbound; use ERC-5192 for the binary case.
- ERC-6982 — default lockable. Limited-transfer NFTs often pair with default-locked = false (open transfer until limit hit).
- ERC-7066 — lockable. Owner can lock between transfers to prevent unwanted ones consuming the budget.
- ERC-2981 — royalties. Each remaining transfer pays royalty as usual.
Migration Notes
For limited-edition / anti-flip NFT collections:
- Set the per-token limit at mint based on the collection's marketing rules.
- The transfer counter is automatic —
OnTransferincrements and the next attempted transfer rejects atOnBeforeTransfer. - Burning typically does not count as a transfer (the spec permits burns even when the limit is reached); review per-collection.
For collections that want to publicly advertise how many transfers remain, the RemainingTransfers view is cheap to read; UIs can render a "3 transfers remaining" badge per NFT.
