pragma solidity ^0.4.8;
library BobbySafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint c = a * b;
assert(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a / b;
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
contract BobbyERC20Base {
address public ceoAddress;
address public cfoAddress;
//是否暂停智能合约的运行
bool public paused = false;
constructor(address cfoAddr) public {
ceoAddress = msg.sender;
cfoAddress = cfoAddr;
}
modifier onlyCEO() {
require(msg.sender == ceoAddress);
_;
}
function setCEO(address _newCEO) public onlyCEO {
require(_newCEO != address(0));
ceoAddress = _newCEO;
}
modifier whenNotPaused() {
require(!paused);
_;
}
modifier whenPaused {
require(paused);
_;
}
function pause() external onlyCEO whenNotPaused {
paused = true;
}
function unpause() public onlyCEO whenPaused {
paused = false;
}
}
contract ERC20Interface {
//ERC20指定接口
event Approval(address indexed src, address indexed guy, uint wad);
event Transfer(address indexed src, address indexed dst, uint wad);
//extend event
event Grant(address indexed src, address indexed dst, uint wad); //发放代币,有解禁期
event Unlock(address indexed user, uint wad); //解禁代币
function name() public view returns (string n);
function symbol() public view returns (string s);
function decimals() public view returns (uint8 d);
function totalSupply() public view returns (uint256 t);
function balanceOf(address _owner) public view returns (uint256 balance);
function transfer(address _to, uint256 _value) public returns (bool success);
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
function approve(address _spender, uint256 _value) public returns (bool success);
function allowance(address _owner, address _spender) public view returns (uint256 remaining);
}
//Erc智能合约
contract ERC20 is ERC20Interface, BobbyERC20Base {
using BobbySafeMath for uint256;
uint private _Thousand = 1000;
uint private _Billion = _Thousand * _Thousand * _Thousand;
//代币基本信息
string private _name = "BOBBY"; //代币名称
string private _symbol = "BOBBY"; //代币标识
uint8 private _decimals = 9; //小数点后位数
uint256 private _totalSupply = 10 * _Billion * (10 ** uint256(_decimals));
struct LockedToken {
uint256 total; // 数量
uint256 duration; // 解锁总时长
uint256 periods; // 解锁期数
uint256 balance; // 剩余未解锁数量
uint256 unlockLast; // 上次解锁时间
}
//解封用户代币结构
struct UserToken {
uint index; //放在数组中的下标
address addr; //用户账号
uint256 tokens; //通证数量
LockedToken[] lockedTokens; //锁定的token
}
mapping(address=>UserToken) private _userMap; //用户映射
address[] private _userArray; //用户数组,from 1
uint32 private actionTransfer = 0;
uint32 private actionGrant = 1;
uint32 private actionUnlock = 2;
struct LogEntry {
uint256 time;
uint32 action; // 0 转账 1 发放 2 解锁
address from;
address to;
uint256 v1;
uint256 v2;
uint256 v3;
}
LogEntry[] private _logs;
function _addUser(address addrUser) private returns (UserToken storage) {
_userMap[addrUser].index = _userArray.length;
_userMap[addrUser].addr = addrUser;
_userMap[addrUser].tokens = 0;
_userArray.push(addrUser);
return _userMap[addrUser];
}
//构造方法,将代币的初始总供给都分配给合约的部署账户。合约的构造方法只在合约部署时执行一次
constructor(address cfoAddr) BobbyERC20Base(cfoAddr) public {
//placeholder
_userArray.push(address(0));
UserToken storage userCFO = _addUser(cfoAddr);
userCFO.tokens = _totalSupply;
}
//返回合约名称。view关键子表示函数只查询状态变量,而不写入
function name() public view returns (string n){
n = _name;
}
//返回合约标识符
function symbol() public view returns (string s){
s = _symbol;
}
//返回合约小数位
function decimals() public view returns (uint8 d){
d = _decimals;
}
//返回合约总供给额
function totalSupply() public view returns (uint256 t){
t = _totalSupply;
}
//查询账户_owner的账户余额
function balanceOf(address _owner) public view returns (uint256 balance){
UserToken storage user = _userMap[_owner];
if (0 == user.index) {
balance = 0;
return;
}
balance = user.tokens;
for (uint index = 0; index < user.lockedTokens.length; index++) {
balance = balance.add((user.lockedTokens[index]).balance);
}
}
function _checkUnlock(address addrUser) private {
UserToken storage user = _userMap[addrUser];
if (0 == user.index) {
return;
}
for (uint index = 0; index < user.lockedTokens.length; index++) {
LockedToken storage locked = user.lockedTokens[index];
if(locked.balance <= 0){
continue;
}
uint256 diff = now.sub(locked.unlockLast);
uint256 unlockUnit = locked.total.div(locked.periods);
uint256 periodDuration = locked.duration.div(locked.periods);
uint256 unlockedPeriods = locked.total.sub(locked.balance).div(unlockUnit);
uint256 periodsToUnlock = diff.div(periodDuration);
if(periodsToUnlock > 0) {
uint256 tokenToUnlock = 0;
if(unlockedPeriods + periodsToUnlock >= locked.periods) {
tokenToUnlock = locked.balance;
}else{
tokenToUnlock = unlockUnit.mul(periodsToUnlock);
}
if (tokenToUnlock >= locked.balance) {
tokenToUnlock = locked.balance;
}
locked.balance = locked.balance.sub(tokenToUnlock);
user.tokens = user.tokens.add(tokenToUnlock);
locked.unlockLast = locked.unlockLast.add(periodDuration.mul(periodsToUnlock));
emit Unlock(addrUser, tokenToUnlock);
log(actionUnlock, addrUser, 0, tokenToUnlock, 0, 0);
}
}
}
//从代币合约的调用者地址上转移_value的数量token到的地址_to,并且必须触发Transfer事件
function transfer(address _to, uint256 _value) public whenNotPaused returns (bool success){
require(msg.sender != _to);
//检查是否有可以解锁的token
_checkUnlock(msg.sender);
require(_userMap[msg.sender].tokens >= _value);
_userMap[msg.sender].tokens = _userMap[msg.sender].tokens.sub(_value);
UserToken storage userTo = _userMap[_to];
if(0 == userTo.index){
userTo = _addUser(_to);
}
userTo.tokens = userTo.tokens.add(_value);
emit Transfer(msg.sender, _to, _value);
log(actionTransfer, msg.sender, _to, _value, 0, 0);
success = true;
}
function transferFrom(address, address, uint256) public whenNotPaused returns (bool success){
success = true;
}
function approve(address, uint256) public whenNotPaused returns (bool success){
success = true;
}
function allowance(address, address) public view returns (uint256 remaining){
remaining = 0;
}
function grant(address _to, uint256 _value, uint256 _duration, uint256 _periods) public whenNotPaused returns (bool success){
require(msg.sender != _to);
//检查是否有可以解锁的token
_checkUnlock(msg.sender);
require(_userMap[msg.sender].tokens >= _value);
_userMap[msg.sender].tokens = _userMap[msg.sender].tokens.sub(_value);
UserToken storage userTo = _userMap[_to];
if(0 == userTo.index){
userTo = _addUser(_to);
}
LockedToken memory locked;
locked.total = _value;
locked.duration = _duration.mul(30 days);
// locked.duration = _duration.mul(1 minutes); //for test
locked.periods = _periods;
locked.balance = _value;
locked.unlockLast = now;
userTo.lockedTokens.push(locked);
emit Grant(msg.sender, _to, _value);
log(actionGrant, msg.sender, _to, _value, _duration, _periods);
success = true;
}
function getUserAddr(uint256 _index) public view returns(address addr){
require(_index < _userArray.length);
addr = _userArray[_index];
}
function getUserSize() public view returns(uint256 size){
size = _userArray.length;
}
function getLockSize(address addr) public view returns (uint256 len) {
UserToken storage user = _userMap[addr];
len = user.lockedTokens.length;
}
function getLock(address addr, uint256 index) public view returns (uint256 total, uint256 duration, uint256 periods, uint256 balance, uint256 unlockLast) {
UserToken storage user = _userMap[addr];
require(index < user.lockedTokens.length);
total = user.lockedTokens[index].total;
duration = user.lockedTokens[index].duration;
periods = user.lockedTokens[index].periods;
balance = user.lockedTokens[index].balance;
unlockLast = user.lockedTokens[index].unlockLast;
}
function getLockInfo(address addr) public view returns (uint256[] totals, uint256[] durations, uint256[] periodses, uint256[] balances, uint256[] unlockLasts) {
UserToken storage user = _userMap[addr];
uint256 len = user.lockedTokens.length;
totals = new uint256[](len);
durations = new uint256[](len);
periodses = new uint256[](len);
balances = new uint256[](len);
unlockLasts = new uint256[](len);
for (uint index = 0; index < user.lockedTokens.length; index++) {
totals[index] = user.lockedTokens[index].total;
durations[index] = user.lockedTokens[index].duration;
periodses[index] = user.lockedTokens[index].periods;
balances[index] = user.lockedTokens[index].balance;
unlockLasts[index] = user.lockedTokens[index].unlockLast;
}
}
function log(uint32 action, address from, address to, uint256 _v1, uint256 _v2, uint256 _v3) private {
LogEntry memory entry;
entry.action = action;
entry.time = now;
entry.from = from;
entry.to = to;
entry.v1 = _v1;
entry.v2 = _v2;
entry.v3 = _v3;
_logs.push(entry);
}
function getLogSize() public view returns(uint256 size){
size = _logs.length;
}
function getLog(uint256 _index) public view returns(uint time, uint32 action, address from, address to, uint256 _v1, uint256 _v2, uint256 _v3){
require(_index < _logs.length);
require(_index >= 0);
LogEntry storage entry = _logs[_index];
action = entry.action;
time = entry.time;
from = entry.from;
to = entry.to;
_v1 = entry.v1;
_v2 = entry.v2;
_v3 = entry.v3;
}
}
{
"compilationTarget": {
"ERC20.sol": "ERC20"
},
"evmVersion": "byzantium",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[],"name":"cfoAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"n","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"","type":"address"},{"name":"","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"ceoAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getLogSize","outputs":[{"name":"size","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"},{"name":"index","type":"uint256"}],"name":"getLock","outputs":[{"name":"total","type":"uint256"},{"name":"duration","type":"uint256"},{"name":"periods","type":"uint256"},{"name":"balance","type":"uint256"},{"name":"unlockLast","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"t","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"},{"name":"","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_index","type":"uint256"}],"name":"getUserAddr","outputs":[{"name":"addr","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_newCEO","type":"address"}],"name":"setCEO","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"d","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_index","type":"uint256"}],"name":"getLog","outputs":[{"name":"time","type":"uint256"},{"name":"action","type":"uint32"},{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"_v1","type":"uint256"},{"name":"_v2","type":"uint256"},{"name":"_v3","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"getLockSize","outputs":[{"name":"len","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"paused","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"getLockInfo","outputs":[{"name":"totals","type":"uint256[]"},{"name":"durations","type":"uint256[]"},{"name":"periodses","type":"uint256[]"},{"name":"balances","type":"uint256[]"},{"name":"unlockLasts","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"s","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getUserSize","outputs":[{"name":"size","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"},{"name":"_duration","type":"uint256"},{"name":"_periods","type":"uint256"}],"name":"grant","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"cfoAddr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Grant","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Unlock","type":"event"}]