ERC-7656: Generalized Contract-Linked Services
ERC-7656 generalises ERC-6551 (Token Bound Accounts) to any contract — not just NFTs. The registry computes a deterministic service contract address for any (implementation, chainId, linkedContract, linkedTokenId, salt, modeOrServiceId) tuple and deploys the service on first use. Services typically expose linkedContract(), linkedTokenId(), and linkedOwner() to identify what they're bound to, plus arbitrary service-specific methods.
This unlocks "every contract gets its own collection of plug-in services" — a multisig that has a vault, a DAO that has its own messaging endpoint, a token that has its own oracle, all looked up deterministically.
Required Interface
interface IERC7656Registry {
event Created(
address service,
address indexed implementation,
bytes32 salt,
uint256 chainId,
bytes12 modeOrServiceId, // mode bit + 11-byte service-id
address indexed linkedContract,
uint256 indexed linkedTokenId
);
function create(
address implementation,
bytes32 salt,
uint256 chainId,
bytes12 modeOrServiceId,
address linkedContract,
uint256 linkedTokenId
) external returns (address service);
function compute(
address implementation,
bytes32 salt,
uint256 chainId,
bytes12 modeOrServiceId,
address linkedContract,
uint256 linkedTokenId
) external view returns (address service);
}
interface IERC7656Service {
function linkedData() external view returns (
uint256 chainId,
bytes12 modeOrServiceId,
address linkedContract,
uint256 linkedTokenId
);
function owner() external view returns (address);
}Mode bit
The high bit of modeOrServiceId distinguishes:
- Mode 0 —
linkedTokenIdrefers to an NFT id (the service is bound to a specific NFT, ERC-6551 style). - Mode 1 —
linkedTokenIdis unused; the service is bound to the contract as a whole (the generalisation over ERC-6551).
The remaining 11 bytes are a free-form serviceId — multiple service instances per (linkedContract, linkedTokenId) are addressable by varying the serviceId.
Neo Equivalent: Registry With (Linked, ServiceId) Mapping
The same pattern works on Neo as for ERC-6551 — a registry contract that deploys per-tuple service contracts on first use, with the (linkedContract, linkedTokenId, serviceId) triple in storage.
| ERC-7656 (Ethereum) | Neo Equivalent | Notes |
|---|---|---|
IERC7656Registry.create(...) | Registry.CreateService(linkedContract, tokenId, serviceId, impl, salt) | Same shape; deterministic post-deploy |
IERC7656Registry.compute(...) | Off-chain compute via Helper.GetContractHash from sender + nonce + salt | Predict-before-deploy |
mode 0 (NFT-bound) | tokenId != null — service stores NFT contract + tokenId | Same as ERC-6551 mirror |
mode 1 (contract-bound) | tokenId == null — service stores only the linked contract hash | Generalisation lets multisigs / DAOs / tokens have services |
serviceId (11-byte tag) | serviceId (any ByteString) — namespaced storage prefix in the service | Extra dimension for multiple service instances per linked contract |
IERC7656Service.linkedData() | Service.LinkedData() — returns the tuple stored at deploy time | Direct equivalent |
IERC7656Service.owner() | Resolves to linkedContract.OwnerOf(tokenId) (mode 0) or linkedContract.GetOwner() (mode 1) | Owner resolution depends on the linked contract's interface |
Relationship to ERC-6551
ERC-7656 is strictly more general than ERC-6551:
- ERC-6551 → mode 0 only, with
linkedContractconstrained to an ERC-721. - ERC-7656 → mode 0 (NFT-bound) or mode 1 (contract-bound),
serviceIdselector for multiple services per binding.
If you've already adopted the ERC-6551 mirror's Pattern A, the ERC-7656 implementation is the same registry with two additional dimensions: mode and serviceId.
Why Now
ERC-7656 reached Final in late 2025 after a year of revision. The underlying need — "let any contract host a discoverable, deterministic collection of services" — has been recognised by every smart-account project (ZeroDev, Safe modules, Argent guardians) but each shipped its own ad-hoc registry. The standard converges those.
