文件 1 的 5:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
this;
return msg.data;
}
}
文件 2 的 5:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
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);
}
文件 3 的 5:IVesting.sol
pragma solidity ^0.8.2;
interface IVesting {
function co() external view returns(address);
function vehicules(address _user, uint256 _index) external view returns (
bool updateable,
uint256 start,
uint256 end,
uint256 upfront,
uint256 amount,
uint256 claimed,
uint256 claimedUpfront);
function vehiculeCount(address _user) external view returns (uint256);
function claim(uint256 _index) external;
function pendingReward(address _user, uint256 _index) external view returns(uint256);
function claimed(address _user, uint256 _index) external view returns(uint256);
}
文件 4 的 5:Ownable.sol
pragma solidity ^0.8.0;
import "Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
文件 5 的 5:Vesting.sol
pragma solidity ^0.8.2;
import "Ownable.sol";
import "IERC20.sol";
import "IVesting.sol";
contract Vesting is Ownable, IVesting {
struct Vehicule {
bool updateable;
uint256 start;
uint256 end;
uint256 upfront;
uint256 amount;
uint256 claimed;
uint256 claimedUpfront;
}
address public override co;
mapping(address => mapping(uint256 => Vehicule)) public override vehicules;
mapping(address => uint256) public override vehiculeCount;
event TokensClaimed(address indexed user, uint256 amount);
event VehiculeCreated(address indexed user, uint256 id, uint256 amount, uint256 start, uint256 end);
constructor(address _co) {
require(_co != address(0));
co = _co;
}
function min(uint256 a, uint256 b) pure private returns(uint256) {
return a < b ? a : b;
}
function max(uint256 a, uint256 b) pure private returns(uint256) {
return a > b ? a : b;
}
function createVehicule(address _user, uint256 _amount, uint256 _upfront, uint256 _start, uint256 _end, bool _updateable) external onlyOwner returns(uint256){
require(_end > _start, "Vesting: wrong vehicule parametres");
require(_start > 0, "Vesting: start cannot be 0");
uint256 counter = vehiculeCount[_user];
vehicules[_user][counter] = Vehicule(_updateable, _start, _end, _upfront, _amount, 0, 0);
vehiculeCount[_user]++;
emit VehiculeCreated(_user, counter, _amount + _upfront, _start, _end);
}
function killVehicule(address _user, uint256 _index) external onlyOwner {
require(vehicules[_user][_index].updateable, "Vesting: Can't kill");
delete vehicules[_user][_index];
}
function endVehicule(address _user, uint256 _index) external onlyOwner {
Vehicule storage vehicule = vehicules[_user][_index];
require(vehicule.updateable, "Vesting: Cannot end");
uint256 _now = block.timestamp;
uint256 start = vehicule.start;
if (start == 0)
revert("Vesting: vehicule does not exist");
uint256 end = vehicule.end;
uint256 elapsed = min(end, max(_now, vehicule.start)) - start;
uint256 maxDelta = end - start;
uint256 unlocked = vehicule.amount * elapsed / maxDelta;
if (_now > start) {
vehicule.amount = unlocked;
vehicule.end = min(vehicule.end, _now);
}
else {
vehicule.upfront = 0;
vehicule.amount = 0;
}
}
function fetchTokens(uint256 _amount) external onlyOwner {
IERC20(co).transfer(msg.sender, _amount);
}
function claim(uint256 _index) external override {
uint256 _now = block.timestamp;
Vehicule storage vehicule = vehicules[msg.sender][_index];
uint256 upfront = _claimUpfront(vehicule);
uint256 start = vehicule.start;
if (start == 0)
revert("Vesting: vehicule does not exist");
require(_now > start, "Vesting: cliff !started");
uint256 end = vehicule.end;
uint256 elapsed = min(end, _now) - start;
uint256 maxDelta = end - start;
uint256 yield = (vehicule.amount * elapsed / maxDelta) - vehicule.claimed;
vehicule.claimed += yield;
IERC20(co).transfer(msg.sender, yield + upfront);
emit TokensClaimed(msg.sender, yield);
}
function _claimUpfront(Vehicule storage vehicule) private returns(uint256) {
uint256 upfront = vehicule.upfront;
if (upfront > 0) {
vehicule.upfront = 0;
vehicule.claimedUpfront = upfront;
return upfront;
}
return 0;
}
function balanceOf(address _user) external view returns(uint256 totalVested) {
uint256 vehiculeCount = vehiculeCount[_user];
for (uint256 i = 0; i < vehiculeCount; i++) {
Vehicule memory vehicule = vehicules[_user][i];
totalVested += totalVested + pendingReward(_user, i);
}
}
function pendingReward(address _user, uint256 _index) public override view returns(uint256) {
Vehicule memory vehicule = vehicules[_user][_index];
uint256 elapsed = min(vehicule.end, block.timestamp) - vehicule.start;
uint256 maxDelta = vehicule.end - vehicule.start;
return vehicule.amount * elapsed / maxDelta - vehicule.claimed + vehicule.upfront;
}
function claimed(address _user, uint256 _index) external view override returns(uint256) {
Vehicule memory vehicule = vehicules[_user][_index];
return vehicule.claimed + vehicule.claimedUpfront;
}
}
{
"compilationTarget": {
"Vesting.sol": "Vesting"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_co","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"start","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"end","type":"uint256"}],"name":"VehiculeCreated","type":"event"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"totalVested","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"claimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"co","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_upfront","type":"uint256"},{"internalType":"uint256","name":"_start","type":"uint256"},{"internalType":"uint256","name":"_end","type":"uint256"},{"internalType":"bool","name":"_updateable","type":"bool"}],"name":"createVehicule","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"endVehicule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"fetchTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"killVehicule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"pendingReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"vehiculeCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"vehicules","outputs":[{"internalType":"bool","name":"updateable","type":"bool"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"},{"internalType":"uint256","name":"upfront","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"claimed","type":"uint256"},{"internalType":"uint256","name":"claimedUpfront","type":"uint256"}],"stateMutability":"view","type":"function"}]