ERC-5496: Multi-privilege NFT
ERC-5496 lets an NFT carry multiple discrete privileges that the owner can grant individually. Each privilege has a unique id (integer); holders can hold a subset of an NFT's privileges. The privileges may auto-reset on transfer or persist depending on the implementation choice.
Differs from ERC-7432 (named role grants with TTL) and ERC-5380 (per-action delegation) in that privileges are discrete bits rather than time-bounded grants: either the holder has the privilege or doesn't.
Common in:
- Game characters with multi-permission roles (read, write, command, evolve).
- DAO members with privilege bitmaps for proposing / voting / executing.
- Subscription NFTs with feature-flag privileges.
Required Interface (delta from ERC-721)
solidity
interface IERC5496 {
event PrivilegeAssigned(uint256 indexed tokenId, uint256 indexed privId,
address indexed user, uint256 expires);
function setPrivilege(uint256 tokenId, uint256 privId, address user, uint256 expires) external;
function privilegeOf(uint256 tokenId, uint256 privId) external view returns (address);
function privilegeExpires(uint256 tokenId, uint256 privId) external view returns (uint256);
function hasPrivilege(uint256 tokenId, uint256 privId, address user) external view returns (bool);
}Neo Equivalent: NEP-11 + Per-(tokenId, privId, holder) Storage
csharp
public static void SetPrivilege(ByteString tokenId, BigInteger privId, UInt160 user, BigInteger expires)
{
if (!Runtime.CheckWitness(OwnerOf(tokenId))) throw new Exception("NEP11:NotOwner");
var entry = new Map<string, object> { ["user"] = user, ["expires"] = expires };
Storage.Put(Storage.CurrentContext, PrivKey(tokenId, privId), StdLib.Serialize(entry));
}
public static bool HasPrivilege(ByteString tokenId, BigInteger privId, UInt160 user)
{
var raw = Storage.Get(Storage.CurrentContext, PrivKey(tokenId, privId));
if (raw is null) return false;
var entry = (Map<string, object>)StdLib.Deserialize((ByteString)raw);
if ((BigInteger)entry["expires"] < Runtime.Time) return false;
return ((UInt160)entry["user"]).Equals(user);
}Direct port of the mapping. Unlike ERC-7432 which uses named bytes32 role IDs, ERC-5496 uses simple integer IDs (suitable for small fixed enumerations).
Comparison
| Standard | Granularity | Identifier | Use case |
|---|---|---|---|
| ERC-4907 | Single role | None (USER) | Simple rentals |
| ERC-5380 | Per-action | bytes4 selector | Action delegation |
| ERC-5496 (this) | Per-privilege | uint256 ID | Bitmap-style permissions |
| ERC-7432 | Per-role | bytes32 hash | Named-role grants |
