Finance

This document is better viewed at https://docs.openzeppelin.com/contracts/api/finance

This directory includes primitives for financial systems:

  • VestingWallet handles the vesting of Ether and ERC-20 tokens for a given beneficiary. Custody of multiple tokens can be given to this contract, which will release the token to the beneficiary following a given, customizable, vesting schedule.

  • VestingWalletRevocable extends VestingWallet with per-asset revocation, freezing vesting for a revoked asset and returning its unvested portion to a designated revoker.

Contracts

VestingWallet

import "@openzeppelin/contracts/finance/VestingWallet.sol";

A vesting wallet is an ownable contract that can receive native currency and ERC-20 tokens, and release these assets to the wallet owner, also referred to as "beneficiary", according to a vesting schedule.

Any assets transferred to this contract will follow the vesting schedule as if they were locked from the beginning. Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly) be immediately releasable.

By setting the duration to 0, one can configure this contract to behave like an asset timelock that holds tokens for a beneficiary until a specified time.

Since the wallet is Ownable, and ownership can be transferred, it is possible to sell unvested tokens. Preventing this in a smart contract is difficult, considering that: 1) a beneficiary address could be a counterfactually deployed contract, 2) there is likely to be a migration path for EOAs to become contracts in the near future.
When using this contract with any token whose balance is adjusted automatically (i.e. a rebase token), make sure to account the supply/balance adjustment in the vesting schedule to ensure the vested amount is as intended.
Chains with support for native ERC20s may allow the vesting wallet to withdraw the underlying asset as both an ERC20 and as native currency. For example, if chain C supports token A and the wallet gets deposited 100 A, then at 50% of the vesting period, the beneficiary can withdraw 50 A as ERC20 and 25 A as native currency (totaling 75 A). Consider disabling one of the withdrawal methods.

constructor(address beneficiary, uint64 startTimestamp, uint64 durationSeconds) public

Sets the beneficiary (owner), the start timestamp and the vesting duration (in seconds) of the vesting wallet.

receive() external

The contract should be able to receive Eth.

start() → uint256 public

Getter for the start timestamp.

duration() → uint256 public

Getter for the vesting duration.

end() → uint256 public

Getter for the end timestamp.

released() → uint256 public

Amount of eth already released

released(address token) → uint256 public

Amount of token already released

releasable() → uint256 public

Getter for the amount of releasable eth.

releasable(address token) → uint256 public

Getter for the amount of releasable token tokens. token should be the address of an IERC20 contract.

release() public

Release the native tokens (ether) that have already vested.

Emits a EtherReleased event.

release(address token) public

Release the tokens that have already vested.

Emits a ERC20Released event.

vestedAmount(uint64 timestamp) → uint256 public

Calculates the amount of ether that has already vested. Default implementation is a linear vesting curve.

vestedAmount(address token, uint64 timestamp) → uint256 public

Calculates the amount of tokens that has already vested. Default implementation is a linear vesting curve.

_vestingSchedule(uint256 totalAllocation, uint64 timestamp) → uint256 internal

Virtual implementation of the vesting formula. This returns the amount vested, as a function of time, for an asset given its total historical allocation.

EtherReleased(uint256 amount) event

ERC20Released(address indexed token, uint256 amount) event

VestingWalletRevocable

import "@openzeppelin/contracts/finance/VestingWalletRevocable.sol";

Extension of VestingWallet that adds revocation per asset.

The beneficiary remains the contract owner inherited from VestingWallet. A separate revoker account can cancel the vesting of the native asset or of specific ERC-20 tokens. When an asset is revoked, the vesting schedule for that asset is frozen at the revocation timestamp, the unvested portion is returned to the revoker, and the vested portion remains claimable by the beneficiary.

Assets transferred to the contract after they have been revoked for a given asset type are not included in the frozen historical allocation used for vesting calculations.
Modifiers

onlyRevoker() modifier

constructor(address beneficiary, address revoker_, uint64 startTimestamp, uint64 durationSeconds) public

revoker() → address public

Getter for the address allowed to revoke vesting.

revoked() → bool public

Returns true if the native asset vesting has been revoked.

revoked(address token) → bool public

Returns true if the vesting of token has been revoked.

revoke() public

Revokes the native asset vesting, returning the unvested amount to the revoker.

Emits an EtherRevoked event.

revoke(address token) public

Revokes the vesting of token, returning the unvested amount to the revoker.

Emits an ERC20Revoked event.

vestedAmount(uint64 timestamp) → uint256 public

Calculates the amount of ether that has already vested. Once revoked, the native asset vesting is frozen at the revocation timestamp and keeps using the historical allocation that existed at revocation time.

vestedAmount(address token, uint64 timestamp) → uint256 public

Calculates the amount of tokens that have already vested. Once revoked, the token vesting is frozen at the revocation timestamp and keeps using the historical allocation that existed at revocation time.

EtherRevoked(uint256 amount) event

ERC20Revoked(address indexed token, uint256 amount) event

VestingWalletInvalidRevoker(address revoker) error

VestingWalletUnauthorizedRevoker(address account) error

VestingWalletEtherAlreadyRevoked() error

VestingWalletERC20AlreadyRevoked(address token) error