文件 1 的 5:IArchToken.sol
pragma solidity ^0.7.0;
interface IArchToken {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
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);
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
function mint(address dst, uint256 amount) external returns (bool);
function burn(address src, uint256 amount) external returns (bool);
function updateTokenMetadata(string memory tokenName, string memory tokenSymbol) external returns (bool);
function supplyManager() external view returns (address);
function metadataManager() external view returns (address);
function supplyChangeAllowedAfter() external view returns (uint256);
function supplyChangeWaitingPeriod() external view returns (uint32);
function supplyChangeWaitingPeriodMinimum() external view returns (uint32);
function mintCap() external view returns (uint16);
function setSupplyManager(address newSupplyManager) external returns (bool);
function setMetadataManager(address newMetadataManager) external returns (bool);
function setSupplyChangeWaitingPeriod(uint32 period) external returns (bool);
function setMintCap(uint16 newCap) external returns (bool);
event MintCapChanged(uint16 indexed oldMintCap, uint16 indexed newMintCap);
event SupplyManagerChanged(address indexed oldManager, address indexed newManager);
event SupplyChangeWaitingPeriodChanged(uint32 indexed oldWaitingPeriod, uint32 indexed newWaitingPeriod);
event MetadataManagerChanged(address indexed oldManager, address indexed newManager);
event TokenMetaUpdated(string indexed name, string indexed symbol);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce);
}
文件 2 的 5:IVotingPower.sol
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import "../lib/PrismProxy.sol";
interface IVotingPower {
struct Stake {
uint256 amount;
uint256 votingPower;
}
function setPendingProxyImplementation(address newPendingImplementation) external returns (bool);
function acceptProxyImplementation() external returns (bool);
function setPendingProxyAdmin(address newPendingAdmin) external returns (bool);
function acceptProxyAdmin() external returns (bool);
function proxyAdmin() external view returns (address);
function pendingProxyAdmin() external view returns (address);
function proxyImplementation() external view returns (address);
function pendingProxyImplementation() external view returns (address);
function proxyImplementationVersion() external view returns (uint8);
function become(PrismProxy prism) external;
function initialize(address _archToken, address _vestingContract) external;
function archToken() external view returns (address);
function vestingContract() external view returns (address);
function stake(uint256 amount) external;
function stakeWithPermit(uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
function withdraw(uint256 amount) external;
function addVotingPowerForVestingTokens(address account, uint256 amount) external;
function removeVotingPowerForClaimedTokens(address account, uint256 amount) external;
function getARCHAmountStaked(address staker) external view returns (uint256);
function getAmountStaked(address staker, address stakedToken) external view returns (uint256);
function getARCHStake(address staker) external view returns (Stake memory);
function getStake(address staker, address stakedToken) external view returns (Stake memory);
function balanceOf(address account) external view returns (uint256);
function balanceOfAt(address account, uint256 blockNumber) external view returns (uint256);
event NewPendingImplementation(address indexed oldPendingImplementation, address indexed newPendingImplementation);
event NewImplementation(address indexed oldImplementation, address indexed newImplementation);
event NewPendingAdmin(address indexed oldPendingAdmin, address indexed newPendingAdmin);
event NewAdmin(address indexed oldAdmin, address indexed newAdmin);
event Staked(address indexed user, address indexed token, uint256 indexed amount, uint256 votingPower);
event Withdrawn(address indexed user, address indexed token, uint256 indexed amount, uint256 votingPower);
event VotingPowerChanged(address indexed voter, uint256 indexed previousBalance, uint256 indexed newBalance);
}
文件 3 的 5:PrismProxy.sol
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
contract PrismProxy {
struct ProxyStorage {
address admin;
address pendingAdmin;
address implementation;
address pendingImplementation;
uint8 version;
}
bytes32 constant PRISM_PROXY_STORAGE_POSITION = keccak256("prism.proxy.storage");
event NewPendingImplementation(address indexed oldPendingImplementation, address indexed newPendingImplementation);
event NewImplementation(address indexed oldImplementation, address indexed newImplementation);
event NewPendingAdmin(address indexed oldPendingAdmin, address indexed newPendingAdmin);
event NewAdmin(address indexed oldAdmin, address indexed newAdmin);
function proxyStorage() internal pure returns (ProxyStorage storage ps) {
bytes32 position = PRISM_PROXY_STORAGE_POSITION;
assembly {
ps.slot := position
}
}
function setPendingProxyImplementation(address newPendingImplementation) public returns (bool) {
ProxyStorage storage s = proxyStorage();
require(msg.sender == s.admin, "Prism::setPendingProxyImp: caller must be admin");
address oldPendingImplementation = s.pendingImplementation;
s.pendingImplementation = newPendingImplementation;
emit NewPendingImplementation(oldPendingImplementation, s.pendingImplementation);
return true;
}
function acceptProxyImplementation() public returns (bool) {
ProxyStorage storage s = proxyStorage();
require(msg.sender == s.pendingImplementation && s.pendingImplementation != address(0), "Prism::acceptProxyImp: caller must be pending implementation");
address oldImplementation = s.implementation;
address oldPendingImplementation = s.pendingImplementation;
s.implementation = s.pendingImplementation;
s.pendingImplementation = address(0);
s.version++;
emit NewImplementation(oldImplementation, s.implementation);
emit NewPendingImplementation(oldPendingImplementation, s.pendingImplementation);
return true;
}
function setPendingProxyAdmin(address newPendingAdmin) public returns (bool) {
ProxyStorage storage s = proxyStorage();
require(msg.sender == s.admin, "Prism::setPendingProxyAdmin: caller must be admin");
address oldPendingAdmin = s.pendingAdmin;
s.pendingAdmin = newPendingAdmin;
emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);
return true;
}
function acceptProxyAdmin() public returns (bool) {
ProxyStorage storage s = proxyStorage();
require(msg.sender == s.pendingAdmin && msg.sender != address(0), "Prism::acceptProxyAdmin: caller must be pending admin");
address oldAdmin = s.admin;
address oldPendingAdmin = s.pendingAdmin;
s.admin = s.pendingAdmin;
s.pendingAdmin = address(0);
emit NewAdmin(oldAdmin, s.admin);
emit NewPendingAdmin(oldPendingAdmin, s.pendingAdmin);
return true;
}
function proxyAdmin() public view returns (address) {
ProxyStorage storage s = proxyStorage();
return s.admin;
}
function pendingProxyAdmin() public view returns (address) {
ProxyStorage storage s = proxyStorage();
return s.pendingAdmin;
}
function proxyImplementation() public view returns (address) {
ProxyStorage storage s = proxyStorage();
return s.implementation;
}
function pendingProxyImplementation() public view returns (address) {
ProxyStorage storage s = proxyStorage();
return s.pendingImplementation;
}
function proxyImplementationVersion() public view returns (uint8) {
ProxyStorage storage s = proxyStorage();
return s.version;
}
function _forwardToImplementation() internal {
ProxyStorage storage s = proxyStorage();
(bool success, ) = s.implementation.delegatecall(msg.data);
assembly {
let free_mem_ptr := mload(0x40)
returndatacopy(free_mem_ptr, 0, returndatasize())
switch success
case 0 { revert(free_mem_ptr, returndatasize()) }
default { return(free_mem_ptr, returndatasize()) }
}
}
}
文件 4 的 5:SafeMath.sol
pragma solidity ^0.7.0;
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function add(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, errorMessage);
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction underflow");
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function mul(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, errorMessage);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
文件 5 的 5:Vesting.sol
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import "./interfaces/IArchToken.sol";
import "./interfaces/IVotingPower.sol";
import "./lib/SafeMath.sol";
contract Vesting {
using SafeMath for uint256;
struct Grant {
uint256 startTime;
uint256 amount;
uint16 vestingDuration;
uint16 vestingCliff;
uint256 totalClaimed;
}
uint256 constant internal SECONDS_PER_DAY = 86400;
IArchToken public token;
IVotingPower public votingPower;
mapping (address => Grant) public tokenGrants;
address public owner;
event GrantAdded(address indexed recipient, uint256 indexed amount, uint256 startTime, uint16 vestingDurationInDays, uint16 vestingCliffInDays);
event GrantTokensClaimed(address indexed recipient, uint256 indexed amountClaimed);
event ChangedOwner(address indexed oldOwner, address indexed newOwner);
event ChangedVotingPower(address indexed oldContract, address indexed newContract);
constructor(address _token) {
require(_token != address(0), "Vest::constructor: must be valid token address");
token = IArchToken(_token);
owner = msg.sender;
}
function addTokenGrant(
address recipient,
uint256 startTime,
uint256 amount,
uint16 vestingDurationInDays,
uint16 vestingCliffInDays
)
external
{
require(msg.sender == owner, "Vest::addTokenGrant: not owner");
require(address(votingPower) != address(0), "Vest::addTokenGrant: Set Voting Power contract first");
require(vestingCliffInDays <= 10*365, "Vest::addTokenGrant: cliff more than 10 years");
require(vestingDurationInDays > 0, "Vest::addTokenGrant: duration must be > 0");
require(vestingDurationInDays <= 25*365, "Vest::addTokenGrant: duration more than 25 years");
require(vestingDurationInDays >= vestingCliffInDays, "Vest::addTokenGrant: duration < cliff");
require(tokenGrants[recipient].amount == 0, "Vest::addTokenGrant: grant already exists for account");
uint256 amountVestedPerDay = amount.div(vestingDurationInDays);
require(amountVestedPerDay > 0, "Vest::addTokenGrant: amountVestedPerDay > 0");
require(token.transferFrom(owner, address(this), amount), "Vest::addTokenGrant: transfer failed");
uint256 grantStartTime = startTime == 0 ? block.timestamp : startTime;
Grant memory grant = Grant({
startTime: grantStartTime,
amount: amount,
vestingDuration: vestingDurationInDays,
vestingCliff: vestingCliffInDays,
totalClaimed: 0
});
tokenGrants[recipient] = grant;
emit GrantAdded(recipient, amount, grantStartTime, vestingDurationInDays, vestingCliffInDays);
votingPower.addVotingPowerForVestingTokens(recipient, amount);
}
function getTokenGrant(address recipient) public view returns(Grant memory){
return tokenGrants[recipient];
}
function calculateGrantClaim(address recipient) public view returns (uint256) {
Grant storage tokenGrant = tokenGrants[recipient];
if (block.timestamp < tokenGrant.startTime) {
return 0;
}
uint256 elapsedTime = block.timestamp.sub(tokenGrant.startTime);
uint256 elapsedDays = elapsedTime.div(SECONDS_PER_DAY);
if (elapsedDays < tokenGrant.vestingCliff) {
return 0;
}
if (elapsedDays >= tokenGrant.vestingDuration) {
uint256 remainingGrant = tokenGrant.amount.sub(tokenGrant.totalClaimed);
return remainingGrant;
} else {
uint256 vestingDurationInSecs = uint256(tokenGrant.vestingDuration).mul(SECONDS_PER_DAY);
uint256 vestingAmountPerSec = tokenGrant.amount.div(vestingDurationInSecs);
uint256 amountVested = vestingAmountPerSec.mul(elapsedTime);
uint256 claimableAmount = amountVested.sub(tokenGrant.totalClaimed);
return claimableAmount;
}
}
function vestedBalance(address recipient) external view returns (uint256) {
Grant storage tokenGrant = tokenGrants[recipient];
if (block.timestamp < tokenGrant.startTime) {
return 0;
}
uint256 elapsedTime = block.timestamp.sub(tokenGrant.startTime);
uint256 elapsedDays = elapsedTime.div(SECONDS_PER_DAY);
if (elapsedDays < tokenGrant.vestingCliff) {
return 0;
}
if (elapsedDays >= tokenGrant.vestingDuration) {
return tokenGrant.amount;
} else {
uint256 vestingDurationInSecs = uint256(tokenGrant.vestingDuration).mul(SECONDS_PER_DAY);
uint256 vestingAmountPerSec = tokenGrant.amount.div(vestingDurationInSecs);
uint256 amountVested = vestingAmountPerSec.mul(elapsedTime);
return amountVested;
}
}
function claimedBalance(address recipient) external view returns (uint256) {
Grant storage tokenGrant = tokenGrants[recipient];
return tokenGrant.totalClaimed;
}
function claimVestedTokens(address recipient) external {
uint256 amountVested = calculateGrantClaim(recipient);
require(amountVested > 0, "Vest::claimVested: amountVested is 0");
votingPower.removeVotingPowerForClaimedTokens(recipient, amountVested);
Grant storage tokenGrant = tokenGrants[recipient];
tokenGrant.totalClaimed = uint256(tokenGrant.totalClaimed.add(amountVested));
require(token.transfer(recipient, amountVested), "Vest::claimVested: transfer failed");
emit GrantTokensClaimed(recipient, amountVested);
}
function tokensVestedPerDay(address recipient) public view returns(uint256) {
Grant storage tokenGrant = tokenGrants[recipient];
return tokenGrant.amount.div(uint256(tokenGrant.vestingDuration));
}
function setVotingPowerContract(address newContract)
external
{
require(msg.sender == owner, "Vest::setVotingPowerContract: not owner");
require(newContract != address(0) && newContract != address(this) && newContract != address(token), "Vest::setVotingPowerContract: not valid contract");
require(IVotingPower(newContract).archToken() == address(token), "Vest::setVotingPowerContract: voting power not initialized");
address oldContract = address(votingPower);
votingPower = IVotingPower(newContract);
emit ChangedVotingPower(oldContract, newContract);
}
function changeOwner(address newOwner)
external
{
require(msg.sender == owner, "Vest::changeOwner: not owner");
require(newOwner != address(0) && newOwner != address(this) && newOwner != address(token), "Vest::changeOwner: not valid address");
address oldOwner = owner;
owner = newOwner;
emit ChangedOwner(oldOwner, newOwner);
}
}
{
"compilationTarget": {
"contracts/Vesting.sol": "Vesting"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 999999
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"ChangedOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldContract","type":"address"},{"indexed":true,"internalType":"address","name":"newContract","type":"address"}],"name":"ChangedVotingPower","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startTime","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"vestingDurationInDays","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"vestingCliffInDays","type":"uint16"}],"name":"GrantAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"uint256","name":"amountClaimed","type":"uint256"}],"name":"GrantTokensClaimed","type":"event"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint16","name":"vestingDurationInDays","type":"uint16"},{"internalType":"uint16","name":"vestingCliffInDays","type":"uint16"}],"name":"addTokenGrant","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"calculateGrantClaim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"changeOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"claimVestedTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"claimedBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"getTokenGrant","outputs":[{"components":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint16","name":"vestingDuration","type":"uint16"},{"internalType":"uint16","name":"vestingCliff","type":"uint16"},{"internalType":"uint256","name":"totalClaimed","type":"uint256"}],"internalType":"struct Vesting.Grant","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newContract","type":"address"}],"name":"setVotingPowerContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IArchToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenGrants","outputs":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint16","name":"vestingDuration","type":"uint16"},{"internalType":"uint16","name":"vestingCliff","type":"uint16"},{"internalType":"uint256","name":"totalClaimed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"tokensVestedPerDay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"vestedBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"votingPower","outputs":[{"internalType":"contract IVotingPower","name":"","type":"address"}],"stateMutability":"view","type":"function"}]