Created
March 11, 2022 23:55
-
-
Save beauwilliams/69d5e3a275463077a310cbbe930b2b9f to your computer and use it in GitHub Desktop.
A demonstraction of a linear vesting contract for MACS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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