VestingWallet.sol 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. // SPDX-License-Identifier: MIT
  2. // OpenZeppelin Contracts (last updated v4.9.0) (finance/VestingWallet.sol)
  3. pragma solidity ^0.8.19;
  4. import "../token/ERC20/utils/SafeERC20.sol";
  5. import "../utils/Address.sol";
  6. import "../utils/Context.sol";
  7. /**
  8. * @title VestingWallet
  9. * @dev This contract handles the vesting of Eth and ERC20 tokens for a given beneficiary. Custody of multiple tokens
  10. * can be given to this contract, which will release the token to the beneficiary following a given vesting schedule.
  11. * The vesting schedule is customizable through the {vestedAmount} function.
  12. *
  13. * Any token transferred to this contract will follow the vesting schedule as if they were locked from the beginning.
  14. * Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly)
  15. * be immediately releasable.
  16. *
  17. * By setting the duration to 0, one can configure this contract to behave like an asset timelock that hold tokens for
  18. * a beneficiary until a specified time.
  19. */
  20. contract VestingWallet is Context {
  21. event EtherReleased(uint256 amount);
  22. event ERC20Released(address indexed token, uint256 amount);
  23. /**
  24. * @dev The `beneficiary` is not a valid account.
  25. */
  26. error VestingWalletInvalidBeneficiary(address beneficiary);
  27. uint256 private _released;
  28. mapping(address => uint256) private _erc20Released;
  29. address private immutable _beneficiary;
  30. uint64 private immutable _start;
  31. uint64 private immutable _duration;
  32. /**
  33. * @dev Set the beneficiary, start timestamp and vesting duration of the vesting wallet.
  34. */
  35. constructor(address beneficiaryAddress, uint64 startTimestamp, uint64 durationSeconds) payable {
  36. if (beneficiaryAddress == address(0)) {
  37. revert VestingWalletInvalidBeneficiary(address(0));
  38. }
  39. _beneficiary = beneficiaryAddress;
  40. _start = startTimestamp;
  41. _duration = durationSeconds;
  42. }
  43. /**
  44. * @dev The contract should be able to receive Eth.
  45. */
  46. receive() external payable virtual {}
  47. /**
  48. * @dev Getter for the beneficiary address.
  49. */
  50. function beneficiary() public view virtual returns (address) {
  51. return _beneficiary;
  52. }
  53. /**
  54. * @dev Getter for the start timestamp.
  55. */
  56. function start() public view virtual returns (uint256) {
  57. return _start;
  58. }
  59. /**
  60. * @dev Getter for the vesting duration.
  61. */
  62. function duration() public view virtual returns (uint256) {
  63. return _duration;
  64. }
  65. /**
  66. * @dev Getter for the end timestamp.
  67. */
  68. function end() public view virtual returns (uint256) {
  69. return start() + duration();
  70. }
  71. /**
  72. * @dev Amount of eth already released
  73. */
  74. function released() public view virtual returns (uint256) {
  75. return _released;
  76. }
  77. /**
  78. * @dev Amount of token already released
  79. */
  80. function released(address token) public view virtual returns (uint256) {
  81. return _erc20Released[token];
  82. }
  83. /**
  84. * @dev Getter for the amount of releasable eth.
  85. */
  86. function releasable() public view virtual returns (uint256) {
  87. return vestedAmount(uint64(block.timestamp)) - released();
  88. }
  89. /**
  90. * @dev Getter for the amount of releasable `token` tokens. `token` should be the address of an
  91. * IERC20 contract.
  92. */
  93. function releasable(address token) public view virtual returns (uint256) {
  94. return vestedAmount(token, uint64(block.timestamp)) - released(token);
  95. }
  96. /**
  97. * @dev Release the native token (ether) that have already vested.
  98. *
  99. * Emits a {EtherReleased} event.
  100. */
  101. function release() public virtual {
  102. uint256 amount = releasable();
  103. _released += amount;
  104. emit EtherReleased(amount);
  105. Address.sendValue(payable(beneficiary()), amount);
  106. }
  107. /**
  108. * @dev Release the tokens that have already vested.
  109. *
  110. * Emits a {ERC20Released} event.
  111. */
  112. function release(address token) public virtual {
  113. uint256 amount = releasable(token);
  114. _erc20Released[token] += amount;
  115. emit ERC20Released(token, amount);
  116. SafeERC20.safeTransfer(IERC20(token), beneficiary(), amount);
  117. }
  118. /**
  119. * @dev Calculates the amount of ether that has already vested. Default implementation is a linear vesting curve.
  120. */
  121. function vestedAmount(uint64 timestamp) public view virtual returns (uint256) {
  122. return _vestingSchedule(address(this).balance + released(), timestamp);
  123. }
  124. /**
  125. * @dev Calculates the amount of tokens that has already vested. Default implementation is a linear vesting curve.
  126. */
  127. function vestedAmount(address token, uint64 timestamp) public view virtual returns (uint256) {
  128. return _vestingSchedule(IERC20(token).balanceOf(address(this)) + released(token), timestamp);
  129. }
  130. /**
  131. * @dev Virtual implementation of the vesting formula. This returns the amount vested, as a function of time, for
  132. * an asset given its total historical allocation.
  133. */
  134. function _vestingSchedule(uint256 totalAllocation, uint64 timestamp) internal view virtual returns (uint256) {
  135. if (timestamp < start()) {
  136. return 0;
  137. } else if (timestamp > end()) {
  138. return totalAllocation;
  139. } else {
  140. return (totalAllocation * (timestamp - start())) / duration();
  141. }
  142. }
  143. }