pragma solidity 0.4.19;
pragma solidity ^0.4.18;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
/**
* @dev Multiplies two numbers, throws on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Substracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
/**
* @dev Adds two numbers, throws on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
pragma solidity ^0.4.18;
pragma solidity ^0.4.18;
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/179
*/
contract ERC20Basic {
function totalSupply() public view returns (uint256);
function balanceOf(address who) public view returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public view returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
/**
* CashBetCoin ERC20 token
* Based on the OpenZeppelin Standard Token
* https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC20/StandardToken.sol
*/
contract MigrationSource {
function vacate(address _addr) public returns (uint256 o_balance,
uint256 o_lock_value,
uint256 o_lock_endTime,
bytes32 o_operatorId,
bytes32 o_playerId);
}
contract CashBetCoin is MigrationSource, ERC20 {
using SafeMath for uint256;
string public constant name = "CashBetCoin";
string public constant symbol = "CBC";
uint8 public constant decimals = 8;
uint internal totalSupply_;
address public owner;
mapping(bytes32 => bool) public operators;
mapping(address => User) public users;
mapping(address => mapping(bytes32 => bool)) public employees;
MigrationSource public migrateFrom;
address public migrateTo;
struct User {
uint256 balance;
uint256 lock_value;
uint256 lock_endTime;
bytes32 operatorId;
bytes32 playerId;
mapping(address => uint256) authorized;
}
modifier only_owner(){
require(msg.sender == owner);
_;
}
modifier only_employees(address _user){
require(employees[msg.sender][users[_user].operatorId]);
_;
}
// PlayerId may only be set if operatorId is set too.
modifier playerid_iff_operatorid(bytes32 _opId, bytes32 _playerId){
require(_opId != bytes32(0) || _playerId == bytes32(0));
_;
}
// Value argument must be less than unlocked balance.
modifier value_less_than_unlocked_balance(address _user, uint256 _value){
User storage user = users[_user];
require(user.lock_endTime < block.timestamp ||
_value <= user.balance - user.lock_value);
require(_value <= user.balance);
_;
}
event Approval(address indexed owner, address indexed spender, uint256 value);
event Transfer(address indexed from, address indexed to, uint256 value);
event LockIncrease(address indexed user, uint256 amount, uint256 time);
event LockDecrease(address indexed user, address employee, uint256 amount, uint256 time);
event Associate(address indexed user, address agent, bytes32 indexed operatorId, bytes32 playerId);
event Burn(address indexed owner, uint256 value);
event OptIn(address indexed owner, uint256 value);
event Vacate(address indexed owner, uint256 value);
event Employee(address indexed empl, bytes32 indexed operatorId, bool allowed);
event Operator(bytes32 indexed operatorId, bool allowed);
function CashBetCoin(uint _totalSupply) public {
totalSupply_ = _totalSupply;
owner = msg.sender;
User storage user = users[owner];
user.balance = totalSupply_;
user.lock_value = 0;
user.lock_endTime = 0;
user.operatorId = bytes32(0);
user.playerId = bytes32(0);
Transfer(0, owner, _totalSupply);
}
function totalSupply() public view returns (uint256){
return totalSupply_;
}
function balanceOf(address _addr) public view returns (uint256 balance) {
return users[_addr].balance;
}
function transfer(address _to, uint256 _value) public value_less_than_unlocked_balance(msg.sender, _value) returns (bool success) {
User storage user = users[msg.sender];
user.balance = user.balance.sub(_value);
users[_to].balance = users[_to].balance.add(_value);
Transfer(msg.sender, _to, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value) public value_less_than_unlocked_balance(_from, _value) returns (bool success) {
User storage user = users[_from];
user.balance = user.balance.sub(_value);
users[_to].balance = users[_to].balance.add(_value);
user.authorized[msg.sender] = user.authorized[msg.sender].sub(_value);
Transfer(_from, _to, _value);
return true;
}
function approve(address _spender, uint256 _value) public returns (bool success){
// To change the approve amount you first have to reduce the addresses`
// allowance to zero by calling `approve(_spender, 0)` if it is not
// already 0 to mitigate the race condition described here:
// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
require((_value == 0) || (users[msg.sender].authorized[_spender] == 0));
users[msg.sender].authorized[_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _user, address _spender) public view returns (uint256){
return users[_user].authorized[_spender];
}
// Returns the number of locked tokens at the specified address.
//
function lockedValueOf(address _addr) public view returns (uint256 value) {
User storage user = users[_addr];
// Is the lock expired?
if (user.lock_endTime < block.timestamp) {
// Lock is expired, no locked value.
return 0;
} else {
return user.lock_value;
}
}
// Returns the unix time that the current token lock will expire.
//
function lockedEndTimeOf(address _addr) public view returns (uint256 time) {
return users[_addr].lock_endTime;
}
// Lock the specified number of tokens until the specified unix
// time. The locked value and expiration time are both absolute (if
// the account already had some locked tokens the count will be
// increased to this value.) If the user already has locked tokens
// the locked token count and expiration time may not be smaller
// than the previous values.
//
function increaseLock(uint256 _value, uint256 _time) public returns (bool success) {
User storage user = users[msg.sender];
// Is there a lock in effect?
if (block.timestamp < user.lock_endTime) {
// Lock in effect, ensure nothing gets smaller.
require(_value >= user.lock_value);
require(_time >= user.lock_endTime);
// Ensure something has increased.
require(_value > user.lock_value || _time > user.lock_endTime);
}
// Things we always require.
require(_value <= user.balance);
require(_time > block.timestamp);
user.lock_value = _value;
user.lock_endTime = _time;
LockIncrease(msg.sender, _value, _time);
return true;
}
// Employees of CashBet may decrease the locked token value and/or
// decrease the locked token expiration date. These values may not
// ever be increased by an employee.
//
function decreaseLock(uint256 _value, uint256 _time, address _user) public only_employees(_user) returns (bool success) {
User storage user = users[_user];
// We don't modify expired locks (they are already 0)
require(user.lock_endTime > block.timestamp);
// Ensure nothing gets bigger.
require(_value <= user.lock_value);
require(_time <= user.lock_endTime);
// Ensure something has decreased.
require(_value < user.lock_value || _time < user.lock_endTime);
user.lock_value = _value;
user.lock_endTime = _time;
LockDecrease(_user, msg.sender, _value, _time);
return true;
}
function associate(bytes32 _opId, bytes32 _playerId) public playerid_iff_operatorid(_opId, _playerId) returns (bool success) {
User storage user = users[msg.sender];
// Players can associate their playerId once while the token is
// locked. They can't change this association until the lock
// expires ...
require(user.lock_value == 0 ||
user.lock_endTime < block.timestamp ||
user.playerId == 0);
// OperatorId argument must be empty or in the approved operators set.
require(_opId == bytes32(0) || operators[_opId]);
user.operatorId = _opId;
user.playerId = _playerId;
Associate(msg.sender, msg.sender, _opId, _playerId);
return true;
}
function associationOf(address _addr) public view returns (bytes32 opId, bytes32 playerId) {
return (users[_addr].operatorId, users[_addr].playerId);
}
function setAssociation(address _user, bytes32 _opId, bytes32 _playerId) public only_employees(_user) playerid_iff_operatorid(_opId, _playerId) returns (bool success) {
User storage user = users[_user];
// Employees may only set opId to empty or something they are an
// employee of.
require(_opId == bytes32(0) || employees[msg.sender][_opId]);
user.operatorId = _opId;
user.playerId = _playerId;
Associate(_user, msg.sender, _opId, _playerId);
return true;
}
function setEmployee(address _addr, bytes32 _opId, bool _allowed) public only_owner {
employees[_addr][_opId] = _allowed;
Employee(_addr, _opId, _allowed);
}
function setOperator(bytes32 _opId, bool _allowed) public only_owner {
operators[_opId] = _allowed;
Operator(_opId, _allowed);
}
function setOwner(address _addr) public only_owner {
owner = _addr;
}
function burnTokens(uint256 _value) public value_less_than_unlocked_balance(msg.sender, _value) returns (bool success) {
User storage user = users[msg.sender];
user.balance = user.balance.sub(_value);
totalSupply_ = totalSupply_.sub(_value);
Burn(msg.sender, _value);
return true;
}
// Sets the contract address that this contract will migrate
// from when the optIn() interface is used.
//
function setMigrateFrom(address _addr) public only_owner {
require(migrateFrom == MigrationSource(0));
migrateFrom = MigrationSource(_addr);
}
// Sets the contract address that is allowed to call vacate on this
// contract.
//
function setMigrateTo(address _addr) public only_owner {
migrateTo = _addr;
}
// Called by a token holding address, this method migrates the
// tokens from an older version of the contract to this version.
// The migrated tokens are merged with any existing tokens in this
// version of the contract, resulting in the locked token count
// being set to the sum of locked tokens in the old and new
// contracts and the lock expiration being set the longest lock
// duration for this address in either contract. The playerId is
// transferred unless it was already set in the new contract.
//
// NOTE - allowances (approve) are *not* transferred. If you gave
// another address an allowance in the old contract you need to
// re-approve it in the new contract.
//
function optIn() public returns (bool success) {
require(migrateFrom != MigrationSource(0));
User storage user = users[msg.sender];
uint256 balance;
uint256 lock_value;
uint256 lock_endTime;
bytes32 opId;
bytes32 playerId;
(balance, lock_value, lock_endTime, opId, playerId) =
migrateFrom.vacate(msg.sender);
OptIn(msg.sender, balance);
user.balance = user.balance.add(balance);
bool lockTimeIncreased = false;
user.lock_value = user.lock_value.add(lock_value);
if (user.lock_endTime < lock_endTime) {
user.lock_endTime = lock_endTime;
lockTimeIncreased = true;
}
if (lock_value > 0 || lockTimeIncreased) {
LockIncrease(msg.sender, user.lock_value, user.lock_endTime);
}
if (user.operatorId == bytes32(0) && opId != bytes32(0)) {
user.operatorId = opId;
user.playerId = playerId;
Associate(msg.sender, msg.sender, opId, playerId);
}
totalSupply_ = totalSupply_.add(balance);
return true;
}
// The vacate method is called by a newer version of the CashBetCoin
// contract to extract the token state for an address and migrate it
// to the new contract.
//
function vacate(address _addr) public returns (uint256 o_balance,
uint256 o_lock_value,
uint256 o_lock_endTime,
bytes32 o_opId,
bytes32 o_playerId) {
require(msg.sender == migrateTo);
User storage user = users[_addr];
require(user.balance > 0);
o_balance = user.balance;
o_lock_value = user.lock_value;
o_lock_endTime = user.lock_endTime;
o_opId = user.operatorId;
o_playerId = user.playerId;
totalSupply_ = totalSupply_.sub(user.balance);
user.balance = 0;
user.lock_value = 0;
user.lock_endTime = 0;
user.operatorId = bytes32(0);
user.playerId = bytes32(0);
Vacate(_addr, o_balance);
}
// Don't accept ETH.
function () public payable {
revert();
}
}
{
"compilationTarget": {
"CashBetCoin.sol": "CashBetCoin"
},
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"migrateFrom","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_addr","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_addr","type":"address"}],"name":"setMigrateFrom","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_addr","type":"address"},{"name":"_opId","type":"bytes32"},{"name":"_allowed","type":"bool"}],"name":"setEmployee","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"lockedEndTimeOf","outputs":[{"name":"time","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"bytes32"}],"name":"employees","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"optIn","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"uint256"},{"name":"_time","type":"uint256"}],"name":"increaseLock","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"lockedValueOf","outputs":[{"name":"value","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"uint256"}],"name":"burnTokens","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_addr","type":"address"}],"name":"setMigrateTo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_opId","type":"bytes32"},{"name":"_playerId","type":"bytes32"}],"name":"associate","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_opId","type":"bytes32"},{"name":"_allowed","type":"bool"}],"name":"setOperator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"migrateTo","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"users","outputs":[{"name":"balance","type":"uint256"},{"name":"lock_value","type":"uint256"},{"name":"lock_endTime","type":"uint256"},{"name":"operatorId","type":"bytes32"},{"name":"playerId","type":"bytes32"}],"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":false,"inputs":[{"name":"_user","type":"address"},{"name":"_opId","type":"bytes32"},{"name":"_playerId","type":"bytes32"}],"name":"setAssociation","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"operators","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_user","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_addr","type":"address"}],"name":"vacate","outputs":[{"name":"o_balance","type":"uint256"},{"name":"o_lock_value","type":"uint256"},{"name":"o_lock_endTime","type":"uint256"},{"name":"o_opId","type":"bytes32"},{"name":"o_playerId","type":"bytes32"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"uint256"},{"name":"_time","type":"uint256"},{"name":"_user","type":"address"}],"name":"decreaseLock","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_addr","type":"address"}],"name":"associationOf","outputs":[{"name":"opId","type":"bytes32"},{"name":"playerId","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_totalSupply","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"time","type":"uint256"}],"name":"LockIncrease","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":false,"name":"employee","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"time","type":"uint256"}],"name":"LockDecrease","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":false,"name":"agent","type":"address"},{"indexed":true,"name":"operatorId","type":"bytes32"},{"indexed":false,"name":"playerId","type":"bytes32"}],"name":"Associate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"OptIn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Vacate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"empl","type":"address"},{"indexed":true,"name":"operatorId","type":"bytes32"},{"indexed":false,"name":"allowed","type":"bool"}],"name":"Employee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"operatorId","type":"bytes32"},{"indexed":false,"name":"allowed","type":"bool"}],"name":"Operator","type":"event"}]