ERC-5216: ERC-1155 Per-tokenId Allowance
ERC-1155's standard setApprovalForAll is all-or-nothing — either the operator can move every token of every id the owner holds, or none. ERC-5216 adds per-tokenId per-spender amount allowances, matching ERC-20's granular approval model. Used for:
- DEX trading — limit how much of any one tokenId a router can move on your behalf.
- Permissioned transfers — grant a contract spending rights for a specific item without granting blanket access.
- Recurring payment NFTs — allow a payment service to draw fixed amounts of a tokenId at intervals.
Required Interface (delta from ERC-1155)
solidity
interface IERC5216 {
event ApprovalById(address indexed account, address indexed operator,
uint256 id, uint256 amount);
function approveById(address operator, uint256 id, uint256 amount) external;
function allowanceById(address account, address operator, uint256 id)
external view returns (uint256);
}safeTransferFrom checks both the global isApprovedForAll and the per-(owner, operator, tokenId) allowance, decrementing the allowance on use.
Neo Equivalent: NEP-11 Divisible + Per-(owner, spender, id) Allowance
NEP-11 divisible is the multi-token NEP-11 variant; the allowance extension adds three storage keys:
csharp
public static void ApproveById(UInt160 spender, ByteString tokenId, BigInteger amount)
{
var owner = Runtime.CallingScriptHash;
if (!Runtime.CheckWitness(owner)) throw new Exception("NEP11:NoAuth");
Storage.Put(Storage.CurrentContext, AllowanceKey(owner, spender, tokenId), amount);
OnApprovalById(owner, spender, tokenId, amount);
}
public static BigInteger AllowanceById(UInt160 owner, UInt160 spender, ByteString tokenId)
=> (BigInteger)(Storage.Get(Storage.CurrentContext, AllowanceKey(owner, spender, tokenId)) ?? ByteString.Empty);
public static bool TransferById(UInt160 from, UInt160 to, ByteString tokenId, BigInteger amount)
{
if (!Runtime.CheckWitness(Runtime.CallingScriptHash)) throw new Exception("NEP11:NoAuth");
var spender = Runtime.CallingScriptHash;
if (!from.Equals(spender))
{
var allowance = AllowanceById(from, spender, tokenId);
if (allowance < amount) throw new Exception("NEP11:InsufficientAllowance");
Storage.Put(Storage.CurrentContext, AllowanceKey(from, spender, tokenId), allowance - amount);
}
return base.Transfer(from, to, amount, tokenId, null);
}| ERC-5216 (Ethereum) | Neo Equivalent | Notes |
|---|---|---|
approveById(operator, id, amount) | ApproveById(spender, tokenId, amount) owner-witness-checked | |
allowanceById(account, operator, id) | AllowanceById(owner, spender, tokenId) view | |
safeTransferFrom decrements allowance | TransferById decrements allowance pre-transfer | |
Combined with isApprovedForAll | Combined with NEP-11 base operator approval | Either path authorises |
Composition
- ERC-1155 — base. ERC-5216 is the granular-allowance extension.
- ERC-5615 — supply extension. Pair with ERC-5216 for full ERC-1155 functionality parity.
Migration Notes
For DEX integrations:
- Routers prefer per-tokenId approvals for safety; ERC-5216 lets collections support this.
- The
approveByIdcall is per-tokenId — for users approving many tokenIds, this becomes expensive. Off-chain helpers can batch theApprovecalls into a single transaction script.
