Skip to content

Instantly share code, notes, and snippets.

@beauwilliams
Created March 11, 2022 23:55
Show Gist options
  • Save beauwilliams/69d5e3a275463077a310cbbe930b2b9f to your computer and use it in GitHub Desktop.
Save beauwilliams/69d5e3a275463077a310cbbe930b2b9f to your computer and use it in GitHub Desktop.
A demonstraction of a linear vesting contract for MACS
// SPDX-License-Identifier: MIT
pragma solidity 0.8.7;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
interface IERC20 {
function totalSupply() external view returns (uint256);
function decimals() external view returns (uint8);
function symbol() external view returns (string memory);
function name() external view returns (string memory);
function getOwner() external view returns (address);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address _owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
abstract contract Auth {
address owner;
mapping (address => bool) private authorisations;
constructor(address _owner) {
owner = _owner;
authorisations[_owner] = true;
}
modifier onlyOwner() {
require(isOwner(msg.sender)); _;
}
modifier authorised() {
require(isAuthorised(msg.sender)); _;
}
function authorise(address adr) public onlyOwner {
authorisations[adr] = true;
emit Authorised(adr);
}
function unauthorise(address adr) public onlyOwner {
authorisations[adr] = false;
emit Unauthorised(adr);
}
function isOwner(address account) public view returns (bool) {
return account == owner;
}
function isAuthorised(address adr) public view returns (bool) {
return authorisations[adr];
}
function transferOwnership(address newOwner) public onlyOwner {
address oldOwner = owner;
owner = newOwner;
authorisations[oldOwner] = false;
authorisations[newOwner] = true;
emit Unauthorised(oldOwner);
emit OwnershipTransferred(oldOwner, newOwner);
}
event OwnershipTransferred(address oldOwner, address newOwner);
event Authorised(address adr);
event Unauthorised(address adr);
}
/**
* @title MACS Vesting Contract
* @author Beau Williams (@beauwilliams)
* @dev Smart contract for purposes of demonstrating linear vesting logic
*/
contract MACSVesting is Auth {
using SafeMath for uint256;
address MACSTokenAddress;
mapping (address => uint256) public shares;
mapping (address => uint256) public claimed;
uint256 public totalShares;
uint256 public totalClaimed;
uint256 public vestStart;
uint256 public vestLength = 8 weeks;
constructor(address _MACSTokenAddress) Auth(msg.sender) {
MACSTokenAddress = _MACSTokenAddress;
}
bool seeded = false;
modifier seeding(){
require(!seeded);
_;
seeded = true;
}
modifier canClaim(){
require(vestStart>0); _;
}
function seed(address[] memory holders, uint256[] memory amounts) external authorised seeding {
for(uint256 i; i<holders.length; i++){
shares[holders[i]] = amounts[i];
totalShares += amounts[i];
}
}
function startVesting() external authorised {
require(vestStart == 0);
vestStart = block.timestamp;
}
function getClaimableShares(address holder) public view returns (uint256) {
if(vestStart == 0) return 0;
uint256 timeFromStart = block.timestamp - vestStart;
uint256 unlocked = timeFromStart > vestLength ? shares[holder] : shares[holder] * timeFromStart / vestLength;
uint256 unclaimed = unlocked - claimed[holder];
return unclaimed;
}
function getPendingAmount(address holder) public view returns (uint256) {
if(vestStart == 0) return 0;
uint256 claimableShares = getClaimableShares(holder);
uint256 remainingTotalShares = totalShares - totalClaimed;
return claimableShares * getVestedTokenBalance() / remainingTotalShares;
}
function claim() external canClaim {
uint256 amount = getPendingAmount(msg.sender);
if(amount > 0){
uint256 claiming = getClaimableShares(msg.sender);
claimed[msg.sender] += claiming;
totalClaimed += claiming;
IERC20(MACSTokenAddress).transfer(msg.sender, amount);
}
}
function getVestedTokenBalance() public view returns (uint256) {
return IERC20(MACSTokenAddress).balanceOf(address(this));
}
function withdrawTokens(uint256 amount) external authorised {
IERC20(MACSTokenAddress).transfer(msg.sender, amount);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment