EIP-5725 Transferable Vesting NFT

Thanks a lot you really went a long way to make your point!

For this particular thread and the scope of the Ethereum improvement proposal, we refer to vesting using the general definition and understanding within the Web3 and crypto ecosystem.

Let me help you out with some context on how vesting is used in the industry that pertains us:

These are some industry specific articles that refer to vesting as the process of locking and distributing purchased tokens within a given timeframe which is the same definition we are using here and pretty much the one whole industry uses.

Lucky for us this standard doesn’t intend to address Restricted Stock Units, neither it pertains the ‘traditional world’ of finance. I still appreciate the lesson, cheers!

1 Like

Sometimes traditional finance doesn’t map one-to-one with decentralized finance because the trust-less nature of smart contracts and the lack of intermediary party holding funds. I believe this could be where there is confusion and possibly we can explain it better in the EIP for future readers.

Based on your definitions of vesting and unlocking in posts above I’m curious how we can better describe these in the EIP. Based on the LinearVestingNFT

we can clearly see how vested RSUs don’t immediately become available all at once to the employee, it just means the employer can no longer take them away

I believe these comments are based on these definitions below:

- _vesting_: Tokens which are locked until a future date.
- _vested_: Tokens which have reached their unlock date. (The usage in this specification relates to the **total** vested tokens for a given Vesting NFT.)
- _claimable_: Amount of tokens which can be claimed at the current `timestamp`.
- _timestamp_: The unix `timestamp` (seconds) representation of dates used for vesting.

In the case of the LinearVestingNFT.sol reference implementation, vestedPayoutAtTime will return 0 before the cliff (i.e. unlock time), but the other thing to consider is that with this implementation, a third party cannot decide to take tokens back before the end of the vesting period.

For example, the ApeSwap Treasury Bills feature uses a linear vesting schedule where tokens are immediately unlocked as they are vested. Is there a better way to describe vesting schedules which operate like this?

1 Like

Re: vesting curves to be implement easily
In the LinearVestingNFT.sol reference implementation, the function vestedPayoutAtTime(uint256 tokenId, uint256 timestamp) is a central location where vesting curves can be easily updated for new contracts. I am in agreement with @Apeguru that we wanted to keep the standard very flexible to allow for different contract architectures which is why it doesn’t include vesting curve standards.

easily changeable vesting curves
To expand on the idea of easily changeable vesting curves, I was personally experimenting with an IVestingCurve standard in another project to allow for drop in vesting curves. It was using an externally deployed IVestingCurve contract, but it’s probably best as a Library or also any contract could extend IVestingCurve.

This approach does add some complexity, but it would be useful if you wanted to build a crowd sourced repository of vesting curves.

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.17;

/// @notice VestingCurve interface to allow for simple updates of vesting release schedules.
interface IVestingCurve {
     * @notice Returns the vested token amount given the inputs below.
     * @param totalPayout Total payout vested once the vestingTerm is up
     * @param vestingTerm Length of time in seconds that tokens are vesting for
     * @param startTimestamp The timestamp of when vesting starts
     * @param checkTimestamp The timestamp to calculate vested tokens
     * @return vestedPayout Total payoutTokens vested at checkTimestamp
     * Requirements
     * - MUST return 0 if checkTimestamp is less than startTimestamp
     * - MUST return totalPayout if checkTimestamp is greater than startTimestamp + vestingTerm,
     * - MUST return a value including or between 0 and totalPayout
    function getVestedPayoutAtTime(
        uint256 totalPayout,
        uint256 vestingTerm,
        uint256 startTimestamp,
        uint256 checkTimestamp
    ) external view returns (uint256 vestedPayout);
1 Like

Hey, we did this several years ago with Revest. Suggest you include backwards compatibility, though to be honest, this sort of thing works better as a protocol over an EIP.

Can you provide a link to the interface you are using?

I believe that the fact that you are using a different implementation in your protocol calls for this to be standardized better. It means that multiple types of vesting vehicles would not be easily integrated in a single marketplace, for example.

As DeFiFoFum says, please share with us the interface and implementation specifics, so backward compatibility can be assessed. Also please feel free to provide a pull request and contribute into making this a better standard.

I will also double down on saying that the fact that there are countless of vesting factories, token-lock protocols and things like Revest. Is more than enough to justify creating a standard.


Firstly, I’ve been battle testing 5725 and I think it’s a great standard to formalise how everyones been doing vesting. In my testing I’ve reached out to ApeGuru with some improvements around allowances.
The EIP is on Last Call, so I hope my addition can be included because not having allowances within 5725 would be pretty lame.

ERC-5725 provides an allowance mechanism for the owner of a token to set a spender with an amount which may call claim(tokenId) on behalf of the owner. The amount can be claimed across multiple tokenIds. The allowance amount is not reset if the owner transfers all of their NFT’s away, the allowance maybe spent in the future if owner reaquires a token.

I’ve based this on the discussions I’ve had with ApeGuru. The allowance pattern is similar to IERC20, whereby the allowance is not reset, it uses the logic from the newer Increase/DecreaseAllowance logic, over the older approve style - this is in contract to IERC721 which will remove the allowances once your NFT balance is 0 due to it using the tokenId as the key, rather than address in IERC20. Because we are using address allowances like IERC20, I chose not to reset the balance when the users NFT count is zero. This has the quirk when the user aquires a new token in the future, the allowance is still valid.

This allowance logic is required for 5725 to have any meaningful infrastructure build around it. Currently the specification will only allow the owner to call claim(tokenId) which has the following effects which are all attributed to not having any approvals for a third party to claim.

  1. Supporting infrastructure could not claim on behalf of the user
  2. Contracts cannot batch multiple claim(tokenId)'s together since the proxy contract is not the owner.

Viva la 5725.

My allowance implementation can be found here : [feat] implement and prove allowance mechanism for ERC5725 by Elliott-Green · Pull Request #8 · ApeSwapFinance/eip-5725-vesting-nft-implementation · GitHub

Thanks so much @nines ! I appreciate your suggestion and pull request. I spent some time going through and reviewing the code base and while I like the idea of being able to batch claim from approvals, I am suggesting a refactor to the interface.

There is much more information in this comment:

Looking forward to hearing your feedback!

We worked with @nines to include the suggested allowance updates. We will be moving EIP-5725 to Final shortly.

Thanks to all contributors and commenters who have provided support to make this EIP a reality. :pray: :partying_face:

1 Like