ERC-5725 Transferable vesting NFT

Final · Vaults & DeFi · created 2022-09-08

A standard that turns a vesting contract, in which locked tokens are released gradually over time, into an NFT so that it can be bought, sold, or moved.

Why it is needed

Vesting contracts and timelock contracts, which lock tokens for a period and release them gradually, differ in interface and implementation from project to project. As a result, wallets and analytics tools cannot handle these contracts in one way, and users must check their vesting schedules on a different screen for each project. Existing vesting is also tied to a specific address, which makes it hard to hand the right to receive tokens to someone else, and distributing shares to multiple people, as in a Simple Agreement for Future Tokens (SAFT), is complicated. This standard solves these problems by representing the vesting right as an ERC-721 NFT so that it can be transferred, and by defining common functions to query the locked and vested amounts.

What to implement

In depth

ERC-721 extension structure

IERC5725 is an interface that inherits IERC721, and its interface ID is 0xbd3a202b. It is designed to be fully compatible with existing ERC-721 integrations and marketplaces, and an ERC-165 supportsInterface check can detect both ERC-721 compatibility and vesting NFT compatibility. The vesting curve can freely implement linear, cliff, exponential, and other deterministic curves.

Accounting relationships among the four state values

The source defines the terms vesting (the amount to be released in the future), vested (the total released so far), claimable (the amount released but not yet claimed), and claimed (the total already received). The sum of vestedPayout and vestingPayout SHOULD always equal the total payout, which is the same as the vestedPayoutAtTime value when type(uint256).max is passed as the timestamp. The amount that is vested but still locked can also be calculated with the formula vestedPayout - claimedPayout - claimablePayout = lockedPayout. claimablePayout is RECOMMENDED to be calculated as vestedPayout minus claimedPayout, and a release cliff can be expressed by returning 0 until a specific time.

Required rules for claim

claim(tokenId) MUST pay the claimablePayout value at the time of the call to msg.sender, MUST revert if the caller is neither the token owner nor an approved address, and MUST emit a PayoutClaimed event. Reverting when there is nothing to claim is RECOMMENDED (SHOULD). Past claims are not kept as chain state, so a historical claim graph must be reconstructed by querying PayoutClaimed events.

The deterministic curve and vestedPayoutAtTime

The timestamp in vestedPayoutAtTime(tokenId, timestamp) MAY be a past or future value, and it MUST return 0 for a time before the NFT was minted. vestedPayout SHOULD return the same value as a vestedPayoutAtTime call made with block.timestamp. The design intent is that, if the curve is deterministic, the release curve can be visualized on-chain or off-chain by sweeping across the vestingPeriod. The standard does not constrain the internal implementation of custom curves.

Why timestamps are used instead of block numbers

In Solidity development the common advice is not to use block.timestamp as a state-dependent variable because miners can manipulate it, but this standard chooses timestamps over block numbers. Block times differ across EVM-compatible networks, so a timestamp basis is needed for the same interface to work on multiple chains. The standard explains that nodes reject blocks with grossly manipulated timestamps, so the room for abuse is negligible. In addition, the reference implementation tracks the claimed amount per NFT internally to prevent claiming more than the allocation.

Claim approval separate from transfer approval

This standard provides a claim-specific approval scheme separate from the ERC-721 transfer approval. setClaimApprovalForAll designates an operator for all of the caller's tokens, and setClaimApproval designates an operator for a single tokenId; they emit the ClaimApprovalForAll and ClaimApproval events respectively. The approval state can be queried with isClaimApprovedForAll and getClaimApproved. An operator approved under ERC-5725 can claim released tokens but cannot move the NFT away from its owner.

Things to watch out for

Original ERC · ← ERCs