ERC-5380: ERC-721 Entitlement Extension
ERC-5380 lets the holder of an NFT grant per-action delegation to other addresses without transferring ownership. Where ERC-4907 gives a single "USER" delegation and ERC-7432 defines named-role grants, ERC-5380 sits between them: per-tokenId-per-action delegation flagged with a bytes4 action selector. Useful for:
- Voting delegation — owner delegates the "vote" action without delegating "transfer".
- Resource-claiming delegation — owner delegates the "claim airdrop" action.
- Game-action delegation — owner delegates the "play" action to a player while retaining transfer rights.
Each entitlement has an expiry, so delegation auto-revokes without an explicit unlock.
Required Interface
interface IERC5380 {
event UpdatedEntitlement(uint256 indexed tokenId, address indexed user,
bytes4 indexed function_, uint64 expires);
function setUser(uint256 tokenId, address user, bytes4 function_, uint64 expires) external;
function userOf(uint256 tokenId, bytes4 function_) external view returns (address);
function userExpires(uint256 tokenId, bytes4 function_) external view returns (uint256);
}The function_ parameter is the 4-byte selector of the delegated method (e.g. keccak256("vote(uint256)")[:4]); the contract checking the entitlement looks up userOf(tokenId, this.action.selector) to see who's delegated.
Neo Equivalent: NEP-11 + (tokenId, actionId, delegate) Entitlement Storage
public static void SetUser(ByteString tokenId, UInt160 user, ByteString actionId, BigInteger expires)
{
if (!Runtime.CheckWitness(OwnerOf(tokenId))) throw new Exception("NEP11:NotOwner");
if (expires <= Runtime.Time) throw new Exception("NEP11:ExpiryInPast");
var entry = new Map<string, object>
{
["user"] = user,
["expires"] = expires,
};
Storage.Put(Storage.CurrentContext, EntKey(tokenId, actionId), StdLib.Serialize(entry));
OnUpdatedEntitlement(tokenId, user, actionId, expires);
}
public static UInt160 UserOf(ByteString tokenId, ByteString actionId)
{
var raw = Storage.Get(Storage.CurrentContext, EntKey(tokenId, actionId));
if (raw is null) return null;
var entry = (Map<string, object>)StdLib.Deserialize((ByteString)raw);
if ((BigInteger)entry["expires"] < Runtime.Time) return null;
return (UInt160)entry["user"];
}| ERC-5380 (Ethereum) | Neo Equivalent | Notes |
|---|---|---|
setUser(tokenId, user, function_, expires) | SetUser(tokenId, user, actionId, expires) | Owner-witness-checked |
userOf(tokenId, function_) | UserOf(tokenId, actionId) honouring expiry | Direct port |
userExpires(tokenId, function_) | UserExpires(tokenId, actionId) | Direct port |
| 4-byte function selector | ByteString actionId (typically Sha256("methodName")[:4]) | Same convention |
Comparison: ERC-4907 vs ERC-5380 vs ERC-7432
| Feature | ERC-4907 | ERC-5380 | ERC-7432 |
|---|---|---|---|
| Delegation granularity | Single "USER" role | Per-action selector | Per-named-role grant |
| Cross-contract awareness | None (NFT contract only) | Action selector | Role identifier (typically hashed name) |
| Expiry | Yes | Yes | Yes |
| Revocability flag | No | No | Yes (configurable) |
| Use case | Generic rentals | Specific delegated actions | Multi-role characters / DAOs |
ERC-5380 is the right fit when the consuming contract identifies itself by selector (a vote contract knows its method's selector); for broader role names, prefer ERC-7432.
Composition
- ERC-4907 — single user role. ERC-5380 generalises to per-action.
- ERC-7432 — named roles. ERC-5380 is selector-keyed while ERC-7432 is name-keyed.
- ERC-7066 — lockable. Locking a token disables all active entitlements (per spec, action delegation cannot exceed ownership scope).
- ERC-2981 — royalties. Action delegations don't affect royalty splits.
Migration Notes
For Solidity contracts using ERC-5380:
- Replace the 4-byte selector storage with a Neo equivalent — typically
Sha256("methodName")[0..4]or a free-form ByteString if your dApp doesn't need cross-chain consistency. - Override the consuming contract's authorization check to call
UserOf(tokenId, MY_ACTION_ID)against the NFT contract. - Expiry handling —
Runtime.Timefor unix-ms comparison.
For dApps that already use ERC-4907 single-user rentals, layering ERC-5380 on top lets you grant fine-grained per-action delegation without changing the existing user role.
