Free Token Vesting Wallet Smart Contract Template (Solidity)
DevTools StoreFree Token Vesting Wallet Template (Solidity)
Linear/cliff vesting for team tokens, investor allocations.
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract VestingWallet is Ownable {
IERC20 public token;
struct VestingSchedule {
address beneficiary;
uint256 totalAmount;
uint256 released;
uint256 start;
uint256 cliffDuration;
uint256 vestingDuration;
}
mapping(bytes32 => VestingSchedule) public schedules;
constructor(address _token) Ownable(msg.sender) {
token = IERC20(_token);
}
function createSchedule(
address beneficiary, uint256 amount,
uint256 start, uint256 cliff, uint256 duration
) external onlyOwner returns (bytes32) {
bytes32 id = keccak256(abi.encodePacked(beneficiary, block.timestamp));
schedules[id] = VestingSchedule(beneficiary, amount, 0, start, cliff, duration);
token.transferFrom(msg.sender, address(this), amount);
return id;
}
function release(bytes32 id) external {
VestingSchedule storage s = schedules[id];
uint256 vested = _vestedAmount(id);
uint256 releasable = vested - s.released;
require(releasable > 0);
s.released += releasable;
token.transfer(s.beneficiary, releasable);
}
function _vestedAmount(bytes32 id) internal view returns (uint256) {
VestingSchedule storage s = schedules[id];
if (block.timestamp < s.start + s.cliffDuration) return 0;
if (block.timestamp >= s.start + s.vestingDuration) return s.totalAmount;
return (s.totalAmount * (block.timestamp - s.start)) / s.vestingDuration;
}
}Full vesting with revocation, multiple schedules, deploy scripts, tests: https://rentry.co/w9gngnqt ($1+)