Skip to content

Instantly share code, notes, and snippets.

@cdljsj
Created June 27, 2020 01:09
Show Gist options
  • Save cdljsj/432a7d3d54a92a15362bcf5138369059 to your computer and use it in GitHub Desktop.
Save cdljsj/432a7d3d54a92a15362bcf5138369059 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.4.26+commit.4563c3fc.js&optimize=false&gist=
pragma solidity ^0.6.0;
import "./ERC20Basic.sol";
import "./SafeMath.sol";
/**
* @title Basic token
* @dev Basic version of StandardToken, with no allowances.
*/
contract BasicToken is ERC20Basic {
using SafeMath for uint256;
mapping(address => uint256) balances;
uint8[] releasePercentages = [0, 0, 0, 3, 7, 12, 18, 25, 33, 42, 52, 63, 75, 88, 100];
mapping(address => uint256) public lockAmount;
mapping(address => LockDetail[]) public lockDetails;
uint8 public constant decimals = 18;
uint256 public preSaleSupply = 10000 * 10000 * (10 ** uint256(decimals));
uint256 public privateSaleSupply = 2 * 10000 * 10000 * (10 ** uint256(decimals));
uint256 public currentPreSaleAmount = 0;
uint256 public currentPrivateSaleAmount = 0;
struct LockDetail {
uint256 amount;
uint256 startTime;
uint256 releaseAmount;
}
uint256 totalSupply_;
function _deliverTokensInternal(address _beneficiary, uint256 _tokenAmount) public {
balances[_beneficiary] = balances[_beneficiary].add(_tokenAmount);
lockAmount[_beneficiary] = lockAmount[_beneficiary].add(_tokenAmount);
lockDetails[_beneficiary].push(LockDetail(_tokenAmount, now, 0));
emit Transfer(address(0), _beneficiary, _tokenAmount);
}
function distributeLockedToken(address _beneficiary, uint256 _tokenAmount) public {
require(_beneficiary != address(0));
require(currentPreSaleAmount.add(_tokenAmount) <= preSaleSupply);
_deliverTokensInternal(_beneficiary, _tokenAmount);
currentPreSaleAmount = currentPreSaleAmount.add(_tokenAmount);
}
function getDuration() public view returns(uint256) {
LockDetail memory lockDetail = LockDetail(1000, now - 99 days, 0);
return lockDetail.amount.mul(releasePercentages[(now.sub(lockDetail.startTime)).div(30 days)]).div(100);
}
function checkTokenRelease(uint256 _value) public {
if (lockAmount[msg.sender] <= 0 || balances[msg.sender].sub(lockAmount[msg.sender]) >= _value) return;
LockDetail[] storage lockDetailElements = lockDetails[msg.sender];
for (uint256 i = 0; i < lockDetailElements.length; i++) {
LockDetail storage lockDetail = lockDetailElements[i];
if (lockDetail.releaseAmount >= lockDetail.amount) continue;
uint256 duration = (now - lockDetail.startTime)/30 days;
if (duration > 14) duration = 14;
if (duration < 0) duration = 0;
uint8 releasePercentage = releasePercentages[duration];
uint256 releaseAmount = lockDetail.amount.mul(releasePercentage).div(100);
lockDetail.releaseAmount = releaseAmount;
lockAmount[msg.sender] = lockAmount[msg.sender].sub(releaseAmount);
}
require(balances[msg.sender].sub(lockAmount[msg.sender]) >= _value);
}
/**
* @dev total number of tokens in existence
* @return totalSupply_
*/
function totalSupply() override public view returns (uint256) {
return totalSupply_;
}
/**
* @dev transfer token for a specified address
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
*/
function transfer(address _to, uint256 _value) override public returns (bool) {
require(_to != address(0));
require(_value <= balances[msg.sender]);
// SafeMath.sub will throw if there is not enough balance.
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
emit Transfer(msg.sender, _to, _value);
return true;
}
/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the the balance of.
* @return balance An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public view override returns (uint256 balance) {
return balances[_owner];
}
}
pragma solidity ^0.4.24;
contract BasicToken {
uint256 totalSupply_;
mapping(address => uint256) balances;
constructor(uint256 _initialSupply) public {
totalSupply_ = _initialSupply;
balances[msg.sender] = _initialSupply;
}
function totalSupply() public view returns (uint256) {
return totalSupply_;
}
function transfer(address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
require(_value <= balances[msg.sender]);
balances[msg.sender] = balances[msg.sender] - _value;
balances[_to] = balances[_to] + _value;
return true;
}
function balanceOf(address _owner) public view returns (uint256) {
return balances[_owner];
}
}
pragma solidity ^0.5.11;
contract ERC20 {
function decimals() public view returns (uint);
function totalSupply() public view returns (uint supply);
function balanceOf( address who ) public view returns (uint value);
function allowance( address owner, address spender ) public view returns (uint _allowance);
function transfer( address to, uint256 value) external;
function transferFrom( address from, address to, uint value) public;
function approve( address spender, uint value ) public returns (bool ok);
event Transfer( address indexed from, address indexed to, uint value);
event Approval( address indexed owner, address indexed spender, uint value);
}
contract BatchWallet {
ERC20 public erc20Token;
constructor(address tokenAddress) public {
erc20Token = ERC20(tokenAddress);
}
function batchTransfer(uint256 amount, address[] memory recipients) public {
uint256 sharePerAccount = amount/recipients.length;
erc20Token.transferFrom(msg.sender, address(this), amount);
for (uint256 i = 0; i < recipients.length; i++) {
erc20Token.transfer(recipients[i], sharePerAccount);
}
}
}
/**
*Submitted for verification at Etherscan.io on 2017-05-29
*/
pragma solidity ^0.5.10;
/* taking ideas from FirstBlood token */
contract SafeMath {
/* function assert(bool assertion) internal { */
/* if (!assertion) { */
/* throw; */
/* } */
/* } // assert no longer needed once solidity is on 0.4.10 */
function safeAdd(uint256 x, uint256 y) internal returns(uint256) {
uint256 z = x + y;
assert((z >= x) && (z >= y));
return z;
}
function safeSubtract(uint256 x, uint256 y) internal returns(uint256) {
assert(x >= y);
uint256 z = x - y;
return z;
}
function safeMult(uint256 x, uint256 y) internal returns(uint256) {
uint256 z = x * y;
assert((x == 0)||(z/x == y));
return z;
}
}
contract Token {
uint256 public totalSupply;
function balanceOf(address _owner) public 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 returns (uint256 remaining);
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
/* ERC 20 token */
contract StandardToken is Token {
function transfer(address _to, uint256 _value) public returns (bool success) {
if (balances[msg.sender] >= _value && _value > 0) {
balances[msg.sender] -= _value;
balances[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
} else {
return false;
}
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0) {
balances[_to] += _value;
balances[_from] -= _value;
allowed[_from][msg.sender] -= _value;
emit Transfer(_from, _to, _value);
return true;
} else {
return false;
}
}
function balanceOf(address _owner) public returns (uint256 balance) {
return balances[_owner];
}
function approve(address _spender, uint256 _value) public returns (bool success) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) public returns (uint256 remaining) {
return allowed[_owner][_spender];
}
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
}
contract BAToken is StandardToken, SafeMath {
// metadata
string public constant name = "Basic Attention Token";
string public constant symbol = "BAT";
uint256 public constant decimals = 18;
string public version = "1.0";
// contracts
address payable public ethFundDeposit; // deposit address for ETH for Brave International
address public batFundDeposit; // deposit address for Brave International use and BAT User Fund
// crowdsale parameters
bool public isFinalized; // switched to true in operational state
uint256 public fundingStartBlock;
uint256 public fundingEndBlock;
uint256 public constant batFund = 500 * (10**6) * 10**decimals; // 500m BAT reserved for Brave Intl use
uint256 public constant tokenExchangeRate = 6400; // 6400 BAT tokens per 1 ETH
uint256 public constant tokenCreationCap = 1500 * (10**6) * 10**decimals;
uint256 public constant tokenCreationMin = 675 * (10**6) * 10**decimals;
// events
event LogRefund(address indexed _to, uint256 _value);
event CreateBAT(address indexed _to, uint256 _value);
// constructor
constructor(
address payable _ethFundDeposit,
address _batFundDeposit,
uint256 _fundingStartBlock,
uint256 _fundingEndBlock) public
{
isFinalized = false; //controls pre through crowdsale state
ethFundDeposit = _ethFundDeposit;
batFundDeposit = _batFundDeposit;
fundingStartBlock = _fundingStartBlock;
fundingEndBlock = _fundingEndBlock;
totalSupply = batFund;
balances[batFundDeposit] = batFund; // Deposit Brave Intl share
emit CreateBAT(batFundDeposit, batFund); // logs Brave Intl fund
}
/// @dev Accepts ether and creates new BAT tokens.
function createTokens() payable external {
if (isFinalized) revert();
if (block.number < fundingStartBlock) revert();
if (block.number > fundingEndBlock) revert();
if (msg.value == 0) revert();
uint256 tokens = safeMult(msg.value, tokenExchangeRate); // check that we're not over totals
uint256 checkedSupply = safeAdd(totalSupply, tokens);
// return money if something goes wrong
if (tokenCreationCap < checkedSupply) revert(); // odd fractions won't be found
totalSupply = checkedSupply;
balances[msg.sender] += tokens; // safeAdd not needed; bad semantics to use here
emit CreateBAT(msg.sender, tokens); // logs token creation
}
/// @dev Ends the funding period and sends the ETH home
function finalize() external {
if (isFinalized) revert();
if (msg.sender != ethFundDeposit) revert(); // locks finalize to the ultimate ETH owner
if(totalSupply < tokenCreationMin) revert(); // have to sell minimum to move to operational
if(block.number <= fundingEndBlock && totalSupply != tokenCreationCap) revert();
// move to operational
isFinalized = true;
if(!ethFundDeposit.send(address(this).balance)) revert(); // send the eth to Brave International
}
/// @dev Allows contributors to recover their ether in the case of a failed funding campaign.
function refund() external {
if(isFinalized) revert(); // prevents refund if operational
if (block.number <= fundingEndBlock) revert(); // prevents refund until sale period is over
if(totalSupply >= tokenCreationMin) revert(); // no refunds if we sold enough
if(msg.sender == batFundDeposit) revert(); // Brave Intl not entitled to a refund
uint256 batVal = balances[msg.sender];
if (batVal == 0) revert();
balances[msg.sender] = 0;
totalSupply = safeSubtract(totalSupply, batVal); // extra safe
uint256 ethVal = batVal / tokenExchangeRate; // should be safe; previous throws covers edges
emit LogRefund(msg.sender, ethVal); // log it
if (!msg.sender.send(ethVal)) revert(); // if you're using a contract; make sure it works with .send gas limits
}
}
pragma solidity ^0.6.1;
contract CloneFactory {
function createClone(address target) public returns (address result) {
bytes20 targetBytes = bytes20(target);
assembly {
let clone := mload(0x40)
mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(clone, 0x14), targetBytes)
mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
result := create(0, clone, 0x37)
}
}
}
pragma solidity ^0.5.7;
contract Delegate {
address public owner;
constructor(address _owner) public {
owner = _owner;
}
function pwn() public {
owner = msg.sender;
}
function getNum() public pure returns (uint256) {
return 888;
}
}
contract Delegation {
address public owner;
Delegate delegate;
constructor(address _delegateAddress) public {
delegate = Delegate(_delegateAddress);
owner = msg.sender;
}
function getNum() public view returns (uint256) {
return delegate.getNum();
}
function () public {
//if (delegate.delegatecall(msg.data))
if (address(delegate).delegatecall(bytes4(keccak256("pwn()")))) {
this;
}
}
}
pragma solidity ^0.6.1;
abstract contract Employee {
uint private salary;
function getSalary() public virtual;
}
contract Manager is Employee {
uint public salary;
address boss;
mapping(address => function()) private funmap;
function increaseSalary() public {
payable(boss).transfer(10);
}
function getSalary() public override {
}
function getTransactionInfo() public {
}
}
interface DataFeed { function getData(address token) external returns (uint value); }
contract FeedConsumer {
DataFeed feed;
uint errorCount;
function rate(address token) public returns (uint value, bool success) {
// Permanently disable the mechanism if there are
// more than 10 errors.
require(errorCount < 10);
try feed.getData(token) returns (uint v) {
return (v, true);
} catch Error(string memory /*reason*/) {
// This is executed in case
// revert was called inside getData
// and a reason string was provided.
errorCount++;
return (0, false);
} catch (bytes memory /*lowLevelData*/) {
// This is executed in case revert() was used
// or there was a failing assertion, division
// by zero, etc. inside getData.
errorCount++;
return (0, false);
}
}
}
// contract Demo {
// string public name = "Hello World";
// byte[] byteArr;
// address[] public addArr;
// uint time;
// uint public currentBalance;
// address payable public owner = msg.sender;
// mapping(address => mapping(uint256 => uint256)) public map;
// event OverFlowDemo(uint num);
// function lengthOfStr(string calldata str) external pure returns (uint) {
// return bytes(str).length;
// }
// function another(string memory str) public view returns(uint) {
// return this.lengthOfStr(str);
// }
// function overflow(uint256 _amount) public {
// require(200 - _amount > 0);
// emit OverFlowDemo((200 - _amount));
// }
// function add(address addr) public {
// addArr.push(addr);
// }
// // function reset() public {
// // addArr.length = 0;
// // }
// function test(uint256 _amount) public {
// require(msg.sender != owner);
// owner.transfer(_amount);
// }
// fallback() external payable {
// currentBalance = 1000;
// }
// // receive() payable external {
// // currentBalance = currentBalance + msg.value;
// // }
// // function() external payable {
// // currentBalance = address(this).balance + msg.value;
// // }
// }
pragma solidity ^0.6.0;
import 'StandardToken.sol';
contract DemoToken is StandardToken {
string public constant name = "DemoToken";
string public constant symbol = "DET";
uint public INITIAL_SUPPLY = 96000000 * (10 ** uint256(decimals));
string public t;
address wallet;
address fundAddr = address(0xCD9f286BA6A3d2DF7885F4A2Be267Fc524D32bD3);
address public addr;
constructor() public {
totalSupply_ = INITIAL_SUPPLY;
balances[msg.sender] = INITIAL_SUPPLY;
}
function test(string memory str, address _test) public {
require(_test != wallet);
t = str;
}
function testTransfer() public {
transfer(fundAddr, 10000);
}
function callFunc() public {
addr = msg.sender;
}
function testFloat() public pure returns(uint) {
return 10.5 * 10;
}
}
pragma solidity ^0.5.7;
import "./StandardToken.sol";
contract EicToken is StandardToken {
string public name = "EI Coin";
string public symbol = "EIC";
uint8 public decimals = 18;
uint public INITIAL_SUPPLY = 9600000 * (10 ** uint256(decimals));
constructor() public {
totalSupply_ = INITIAL_SUPPLY;
balances[msg.sender] = INITIAL_SUPPLY;
}
}
[
{
"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"
},
{
"constant": false,
"inputs": [
{
"name": "_beneficiary",
"type": "address"
},
{
"name": "_tokenAmount",
"type": "uint256"
}
],
"name": "_deliverTokensInternal",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_value",
"type": "uint256"
}
],
"name": "checkTokenRelease",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [],
"name": "createData",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_beneficiary",
"type": "address"
},
{
"name": "_tokenAmount",
"type": "uint256"
}
],
"name": "distributeLockedToken",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [],
"name": "getDuration",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"name": "_to",
"type": "address"
},
{
"name": "_value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"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": "currentPreSaleAmount",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "currentPrivateSaleAmount",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [
{
"name": "",
"type": "uint8"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "address"
}
],
"name": "lockAmount",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "address"
},
{
"name": "",
"type": "uint256"
}
],
"name": "lockDetails",
"outputs": [
{
"name": "amount",
"type": "uint256"
},
{
"name": "startTime",
"type": "uint256"
},
{
"name": "releaseAmount",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "preSaleSupply",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "privateSaleSupply",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]
pragma solidity ^0.6.0;
import "./ERC20Basic.sol";
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
abstract contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public view virtual returns (uint256);
function transferFrom(address from, address to, uint256 value) public virtual returns (bool);
function approve(address spender, uint256 value) public virtual returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
pragma solidity ^0.6.0;
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/179
*/
abstract contract ERC20Basic {
function totalSupply() virtual public view returns (uint256);
function balanceOf(address who) virtual public view returns (uint256);
function transfer(address to, uint256 value) virtual public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
pragma solidity ^0.6.8;
interface ETHCoin {
function decimals() external view returns (uint);
function name() external view returns (bytes32);
function totalSupply() external view returns (uint supply);
function balanceOf( address who ) external view returns (uint value);
function allowance( address owner, address spender ) external view returns (uint _allowance);
function transfer( address to, uint256 value) external;
function transferFrom( address from, address to, uint value) external;
function approve( address spender, uint value ) external returns (bool ok);
event Transfer( address indexed from, address indexed to, uint value);
event Approval( address indexed owner, address indexed spender, uint value);
}
pragma solidity ^0.5.0;
import 'openzeppelin-solidity/contracts/math/SafeMath.sol';
contract Fallout {
using SafeMath for uint256;
mapping (address => uint) allocations;
address payable public owner;
/* constructor */
function Fal1out() public payable {
owner = msg.sender;
allocations[owner] = msg.value;
}
modifier onlyOwner {
require(
msg.sender == owner,
"caller is not the owner"
);
_;
}
function allocate() public payable {
allocations[msg.sender] = allocations[msg.sender].add(msg.value);
}
function sendAllocation(address payable allocator) public {
require(allocations[allocator] > 0);
allocator.transfer(allocations[allocator]);
}
function collectAllocations() public onlyOwner {
msg.sender.transfer(address(this).balance);
}
function allocatorBalance(address allocator) public view returns (uint) {
return allocations[allocator];
}
}
// File: contracts/TetherToken.sol
pragma solidity >=0.4.21 <0.6.0;
/**
contract TetherToken {
uint public _totalSupply;
function totalSupply() public view returns (uint);
function balanceOf(address who) public view returns (uint);
function transfer(address to, uint value) public;
function allowance(address owner, address spender) public view returns (uint);
function transferFrom(address from, address to, uint value) public;
function approve(address spender, uint value) public;
}
*/
contract TetherToken {
function totalSupply() public view returns (uint supply);
function balanceOf( address who ) public view returns (uint value);
function allowance( address owner, address spender ) public view returns (uint _allowance);
function transfer( address to, uint256 value) external;
function transferFrom( address from, address to, uint value) public;
function approve( address spender, uint value ) public returns (bool ok);
event Transfer( address indexed from, address indexed to, uint value);
event Approval( address indexed owner, address indexed spender, uint value);
}
// File: contracts/Ownable.sol
pragma solidity >=0.4.21 <0.6.0;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be aplied to your functions to restrict their use to
* the owner.
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* > Note: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// File: contracts/SafeMath.sol
pragma solidity ^0.5.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
// File: contracts/FomoInvest.sol
pragma solidity >=0.4.21 <0.6.0;
/**
* 需求:
* 功能说明:
* 1. USDT转入合约
* 转入合约后,记录转入地址和转入金额,然后进入排队入金队列
* 2. 轮次系统
* 第一(初始)轮众筹金额10000USDT,第一轮众筹额度没有满的时候,转入排队入金的直接成为第一轮众筹金额,多的额度以及后续转入金额继续在排队入金队列
*
* 固定在7天后进入下一轮
*
* 开始新的一轮 众筹金额递增30% 众筹13000USDT
*
* 在入金后的第二轮 提供一个接口,用户可以选择提现还是复投,如未调用 则默认提现
*
* 入金后的第三轮开始的时候,如果选择提现 则返还本金+14%收益 如果选择复投 上一轮选择复投的金额,优先成为当前开始这一轮的入金,不足额度从排队入金队列中
*
* 当某轮达到七天,还未筹集到所需资金,则进行清算
*
* 清算规则:
* 最后未众筹完成的轮次 100%退还本金到投资账户(因为未众筹完成,已经不存在排队入金队列)
*
* 倒数第二轮 的投资 返还本轮投资金额的65%
* 倒数第三轮的投资地址 返回本轮投资金额的65%
*
* 需求:只需要写出带安全算法的雏形(要求尽量优化GAS消耗)
* 注意:剩余资金要留接口转出
*/
contract FomoInvest is Ownable {
using SafeMath for uint256;
enum State { Init, Active, Refunding, Closed }
TetherToken public usdtToken;
uint256 public roundInterval = 7 days;
uint256 public defaultCap = 10000000000; // 10000 USDT, USDT 小数点为 6 位
uint256 public capIncrease = 30; // 每轮投资上限递增百分比
uint256 public interestRate = 14; // 取现投资收益百分比
uint256 public withdrawFee = 2; // 提现手续费百分比
uint256 public refundRate = 65; // 非最后一轮投资返还本金百分比
uint256 public totalAmount; // 当前投资额
uint256 public roundIndex = 0; // 当前轮次
mapping(address => mapping(uint256 => uint256)) public investRecords; // 用户投资明细 投资地址 -> 轮次 -> 投资金额
mapping(uint256 => Round) public roundInfo; // 轮次状态信息 轮次index -> 轮次动态详情
mapping(address => uint256) public waitingQueue; // 投资等待队列 投资等待用户 -> 投资金额
mapping(uint256 => address[]) public waitingUsers; // 投资等待用户 轮次 -> 所有等待用户
mapping(address => uint256) public reDepositRound; // 复投用户 -> 轮次
mapping(uint256 => address[]) public reDepositUsers; // 轮次 -> 所有复投用户
State public state; // 合约状态
struct Round {
uint256 startTime; // 开始时间
uint256 endTime; // 结束时间
uint256 capInUsdtWei; // 投资上限
uint256 investAmountInUsdtWei; // 已投资数量
uint256 index; // 轮次号
}
event StateChanged(State from, State to);
event Received(address sender, uint256 amount);
event Invested(address investor, uint256 amount, uint256 roundIndex);
event RefundDone(uint256 size, uint256 amount);
event WithdrawDone(uint256 size, uint256 amount);
event MarkReInvested(address investor, uint256 roundIndex);
event ReInvested(address investor, uint256 roundIndex, uint256 amount);
event ReInvestDone(uint256 size);
event WaitingQueueDone(uint256 size);
constructor(address tetherToken) public {
usdtToken = TetherToken(tetherToken);
state = State.Init;
}
function start() onlyOwner public {
require(state == State.Init);
roundIndex++;
roundInfo[roundIndex] = Round(now, now + roundInterval, defaultCap, 0, roundIndex);
state = State.Active;
emit StateChanged(State.Init, State.Active);
}
function setEndTime(uint256 round, uint256 time) public {
roundInfo[round].endTime = time;
}
function setCap(uint256 round, uint256 cap) public {
roundInfo[round].capInUsdtWei = cap;
}
/**
* 用户存币投资
* 调用该方法前,用户需要先调用 approve 方法
*/
function deposit(uint256 usdtWeiAmount) public {
require(state == State.Active);
require(usdtToken.allowance(msg.sender, address(this)) >= usdtWeiAmount);
usdtToken.transferFrom(msg.sender, address(this), usdtWeiAmount);
_internalDeposit(msg.sender, usdtWeiAmount);
}
/**
* 管理员为用户存币投资
* 调用该方法前,管理员需要确认确实收到了用户的投资
*/
function adminDeposit(address investor, uint256 usdtWeiAmount) public onlyOwner {
require(state == State.Active);
_internalDeposit(investor, usdtWeiAmount);
}
/**
* 批量清算
*/
function batchRefund(address[] memory investors) onlyOwner public {
require(state == State.Refunding);
uint256 batchRefundAmount;
for (uint256 i = 0; i < investors.length; i++) {
address currentInvestor = investors[i];
uint256 totalRefundAmount = investRecords[currentInvestor][roundIndex];
if (roundIndex >= 2 && investRecords[currentInvestor][roundIndex - 1] > 0) {
uint256 refundAmount = investRecords[currentInvestor][roundIndex - 1].mul(refundRate).div(100);
totalRefundAmount = totalRefundAmount.add(refundAmount);
investRecords[currentInvestor][roundIndex - 1] = 0;
}
if (roundIndex >= 3 && investRecords[currentInvestor][roundIndex - 2] > 0) {
uint256 refundAmount = investRecords[currentInvestor][roundIndex - 2].mul(refundRate).div(100);
totalRefundAmount = totalRefundAmount.add(refundAmount);
investRecords[currentInvestor][roundIndex - 2] = 0;
}
totalAmount = totalAmount.sub(totalRefundAmount);
investRecords[currentInvestor][roundIndex] = 0;
batchRefundAmount = batchRefundAmount.add(totalRefundAmount);
usdtToken.transfer(currentInvestor, totalRefundAmount);
}
emit RefundDone(investors.length, batchRefundAmount);
}
/**
* 批量提现
* 对于成功参与第一轮投资的用户,第三轮开始可以选择提现或复投,不做选择默认为提现,第四轮提现到账
* 从第四轮开始,可以调用此方法为选择提现的用户转账
* 提现计算公式为 (本金 + 本金 * interestRate) * (1 - withdrawFee)
*/
function batchWithdraw(address[] memory investors) onlyOwner public {
require(state == State.Active);
require(roundIndex >= 4);
uint256 batchWithdrawAmount;
for (uint256 i = 0; i < investors.length; i++) {
address currentInvestor = investors[i];
uint256 principle = investRecords[investors[i]][roundIndex - 3];
if (principle == 0) continue;
uint256 interest = principle.mul(interestRate).div(100);
uint256 withdrawAmount = principle.add(interest);
uint256 withdrawFeeAmount = withdrawAmount.mul(withdrawFee).div(100);
uint256 pureWithdraw = withdrawAmount.sub(withdrawFeeAmount);
totalAmount = totalAmount.sub(pureWithdraw);
investRecords[investors[i]][roundIndex - 3] = 0;
usdtToken.transfer(currentInvestor, pureWithdraw);
batchWithdrawAmount = batchWithdrawAmount.add(pureWithdraw);
}
emit WithdrawDone(investors.length, batchWithdrawAmount);
}
/**
* 用户复投
* 对于成功参与第一轮投资的用户,第三轮开始可以选择提现或复投,如果选择复投,优先被投资到第四轮
* 为了方便清算,用户选择复投时仅做记录,由预言机定时为用户做真正复投
*/
function reDeposit() public {
require(state == State.Active);
require(roundIndex >= 3);
require(investRecords[msg.sender][roundIndex - 2] > 0);
reDepositRound[msg.sender] = roundIndex + 1;
reDepositUsers[roundIndex + 1].push(msg.sender);
emit MarkReInvested(msg.sender, roundIndex + 1);
}
/**
* 管理员做真实复投
*/
function adminReDeposit(address[] memory investors) onlyOwner public {
require(state == State.Active);
require(roundIndex >= 4);
require(reDepositUsers[roundIndex].length > 0);
for (uint256 i = 0; i < investors.length; i++) {
address currentInvestor = investors[i];
if (reDepositRound[currentInvestor] == 0) continue;
uint256 principle = investRecords[currentInvestor][roundIndex - 3];
uint256 interest = principle.mul(interestRate).div(100);
uint256 amountToInvest = principle.add(interest);
reDepositRound[currentInvestor] = 0;
investRecords[currentInvestor][roundIndex] = investRecords[currentInvestor][roundIndex].add(amountToInvest);
roundInfo[roundIndex].investAmountInUsdtWei = roundInfo[roundIndex].investAmountInUsdtWei.add(amountToInvest);
investRecords[currentInvestor][roundIndex - 3] = 0;
emit ReInvested(currentInvestor, roundIndex, amountToInvest);
}
emit ReInvestDone(investors.length);
}
/**
* 管理员处理投资等待队列
*/
function adminDepositForWaitingUsers(address[] memory investors) onlyOwner public {
require(state == State.Active);
require(waitingUsers[roundIndex].length > 0);
for (uint256 i = 0; i < investors.length; i++) {
address currentInvestor = investors[i];
uint256 waitingAmount = waitingQueue[currentInvestor];
if (waitingAmount == 0) continue;
waitingQueue[currentInvestor] = 0;
_internalDepositWithoutTransfer(currentInvestor, waitingAmount);
}
emit WaitingQueueDone(investors.length);
}
/**
* 管理员日常状态巡检与调整
*/
function adminStateCheck() onlyOwner public {
require(state == State.Active);
if (now > roundInfo[roundIndex].endTime) {
if (roundInfo[roundIndex].investAmountInUsdtWei < roundInfo[roundIndex].capInUsdtWei) {
state = State.Refunding;
emit StateChanged(State.Active, State.Refunding);
} else {
_startNewRound();
}
}
}
function adminClose() onlyOwner public {
require(state == State.Refunding);
state = State.Closed;
uint256 balance = usdtToken.balanceOf(address(this));
usdtToken.transfer(owner(), balance);
emit StateChanged(State.Refunding, State.Closed);
}
function getWaitingUsers() public view returns (address[] memory) {
return waitingUsers[roundIndex];
}
function getReDepositUsers() public view returns (address[] memory) {
return reDepositUsers[roundIndex];
}
function kill() onlyOwner public {
require(state == State.Closed);
selfdestruct(msg.sender);
}
function _internalDeposit(address investor, uint256 usdtWeiAmount) private {
totalAmount = totalAmount.add(usdtWeiAmount);
require(usdtToken.balanceOf(address(this)) >= totalAmount);
emit Received(investor, usdtWeiAmount);
// owner 转入的资金不记账
if (investor == owner()) return;
// 如果当前时间已经超过当前轮次结束时间,并且投资未满额,将合约状态改为 Refunding, 但本次投资仍然正常记账
if (now > roundInfo[roundIndex].endTime) {
if (roundInfo[roundIndex].investAmountInUsdtWei < roundInfo[roundIndex].capInUsdtWei) {
state = State.Refunding;
}
}
_internalDepositWithoutTransfer(investor, usdtWeiAmount);
if (now > roundInfo[roundIndex].endTime && state != State.Refunding) {
_startNewRound();
}
}
function _internalDepositWithoutTransfer(address investor, uint256 usdtWeiAmount) private {
if (roundInfo[roundIndex].investAmountInUsdtWei.add(usdtWeiAmount) > roundInfo[roundIndex].capInUsdtWei && state != State.Refunding) {
uint256 investAmount = 0;
if (roundInfo[roundIndex].investAmountInUsdtWei < roundInfo[roundIndex].capInUsdtWei) {
investAmount = roundInfo[roundIndex].capInUsdtWei.sub(roundInfo[roundIndex].investAmountInUsdtWei);
}
investRecords[investor][roundIndex] = investRecords[investor][roundIndex].add(investAmount);
roundInfo[roundIndex].investAmountInUsdtWei = roundInfo[roundIndex].investAmountInUsdtWei.add(investAmount);
emit Invested(investor, investAmount, roundIndex);
_addToWaitingQueue(investor, usdtWeiAmount.sub(investAmount));
} else {
if (waitingUsers[roundIndex + 1].length > 0) {
_addToWaitingQueue(investor, usdtWeiAmount);
} else {
investRecords[investor][roundIndex] = investRecords[investor][roundIndex].add(usdtWeiAmount);
roundInfo[roundIndex].investAmountInUsdtWei = roundInfo[roundIndex].investAmountInUsdtWei.add(usdtWeiAmount);
emit Invested(investor, usdtWeiAmount, roundIndex);
}
}
}
function _addToWaitingQueue(address investor, uint256 usdtWeiAmount) private {
if (waitingQueue[investor] == 0) {
waitingUsers[roundIndex + 1].push(investor);
}
waitingQueue[investor] = waitingQueue[investor].add(usdtWeiAmount);
}
function _startNewRound() private {
roundIndex++;
uint256 capToIncrease = roundInfo[roundIndex - 1].capInUsdtWei.mul(capIncrease).div(100);
roundInfo[roundIndex] = Round(now, now + roundInterval, roundInfo[roundIndex - 1].capInUsdtWei.add(capToIncrease), 0, roundIndex);
}
}
/**
*Submitted for verification at Etherscan.io on 2018-02-27
*/
pragma solidity ^0.5.10;
contract Token {
/// @param _owner The address from which the balance will be retrieved
/// @return The balance
function balanceOf(address _owner) public returns (uint balance) {}
/// @notice send `_value` token to `_to` from `msg.sender`
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transfer(address _to, uint _value) public returns (bool success) {}
/// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
/// @return Whether the transfer was successful or not
function transferFrom(address _from, address _to, uint _value) public returns (bool success) {}
/// @notice `msg.sender` approves `_addr` to spend `_value` tokens
/// @param _spender The address of the account able to transfer the tokens
/// @param _value The amount of wei to be approved for transfer
/// @return Whether the approval was successful or not
function approve(address _spender, uint _value) public returns (bool success) {}
/// @param _owner The address of the account owning tokens
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens allowed to spent
function allowance(address _owner, address _spender) public returns (uint remaining) {}
event Transfer(address indexed _from, address indexed _to, uint _value);
event Approval(address indexed _owner, address indexed _spender, uint _value);
}
contract RegularToken is Token {
function transfer(address _to, uint _value) public returns (bool) {
//Default assumes totalSupply can't be over max (2^256 - 1).
if (balances[msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {
balances[msg.sender] -= _value;
balances[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
} else { return false; }
}
function transferFrom(address _from, address _to, uint _value) public returns (bool) {
if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value >= balances[_to]) {
balances[_to] += _value;
balances[_from] -= _value;
allowed[_from][msg.sender] -= _value;
emit Transfer(_from, _to, _value);
return true;
} else { return false; }
}
function balanceOf(address _owner) public returns (uint) {
return balances[_owner];
}
function approve(address _spender, uint _value) public returns (bool) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender) public returns (uint) {
return allowed[_owner][_spender];
}
mapping (address => uint) balances;
mapping (address => mapping (address => uint)) allowed;
uint public totalSupply;
}
contract UnboundedRegularToken is RegularToken {
uint constant MAX_UINT = 2**256 - 1;
/// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited amount.
/// @param _from Address to transfer from.
/// @param _to Address to transfer to.
/// @param _value Amount to transfer.
/// @return Success of transfer.
function transferFrom(address _from, address _to, uint _value)
public
returns (bool)
{
uint allowance = allowed[_from][msg.sender];
if (balances[_from] >= _value
&& allowance >= _value
&& balances[_to] + _value >= balances[_to]
) {
balances[_to] += _value;
balances[_from] -= _value;
if (allowance < MAX_UINT) {
allowed[_from][msg.sender] -= _value;
}
emit Transfer(_from, _to, _value);
return true;
} else {
return false;
}
}
}
contract HBToken is UnboundedRegularToken {
uint public totalSupply = 5*10**26;
uint8 constant public decimals = 18;
string constant public name = "HuobiToken";
string constant public symbol = "HT";
constructor() public {
balances[msg.sender] = totalSupply;
emit Transfer(address(0), msg.sender, totalSupply);
}
}
pragma solidity ^0.5.16;
contract Lab {
uint256 dataSize;
struct Demo {
int256 a1;
int256 a2;
int256 a3;
int256 a4;
int256 a5;
int256 a6;
int256 a7;
int256 a8;
int256 a9;
int256 a10;
int256 a11;
int256 a12;
int256 a14;
int256 a15;
int256 a13;
int256 a16;
int256 a17;
}
constructor() public {
Demo memory demo = Demo(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17);
demo.a1;
}
function test(uint256[] memory data) public {
dataSize = data.length;
}
}
contract RewardContract {
struct User {
bytes32 userName;
address userAddress;
uint accountBalance;
}
struct Category {
// categoryID is a string, which could be a hashtag
bytes16 categoryID;
bytes32 categoryName;
uint endTime;
uint rewardTotal;
uint nominationTotal;
uint numNominations;
uint numNominees;
// linked list of neighbouring categories; used for iteration
bytes16 nextCategoryID;
bytes16 previousCategoryID;
// mappings within a category
mapping (uint => Nomination) nominationsBySeq;
// map userIDs to sequence, and sequence to nominees
mapping (bytes16 => uint) nomineeSeqsByUserID;
mapping (uint => Nominee) nomineesBySeq;
}
struct Nomination {
User nominator;
Nominee nominee;
uint nominatedAmount;
bytes32 nominationReason;
}
struct Nominee {
User nomineeUser;
uint nomineeTotal;
}
address owner;
uint numCategories;
mapping (address => User) public usersByAddress;
mapping (bytes16 => User) public usersByUserID;
mapping (bytes16 => Category) public categories;
bytes16 public headCategoryID;
bytes16 public tailCategoryID;
function nominate(bytes16 categoryID, bytes16 nomineeUserID, uint nominatedAmount, bytes32 nominationReason) public returns (bool) {
// lookup users involved
User memory nominator = usersByAddress[msg.sender];
User memory nomineeUser = usersByUserID[nomineeUserID];
// lookup category
Category storage c = categories[categoryID];
// check that both nominator and nominee have been registered, and category exists and still open
if (nominator.userName == "" || nominator.accountBalance < nominatedAmount || nomineeUser.userName == "" || c.categoryName == "" || now > c.endTime) {
return false;
}
// get a nominee
Nominee memory n;
// check if user has been nominated in this category before
uint nomineeSeq = c.nomineeSeqsByUserID[nomineeUserID];
if (nomineeSeq == 0) {
// first time nomination in this category
nomineeSeq = c.numNominees++;
c.nomineeSeqsByUserID[nomineeUserID] = nomineeSeq;
// now construct nominee
n.nomineeUser = nomineeUser;
n.nomineeTotal = 0;
c.nomineesBySeq[nomineeSeq] = n;
} else {
n = c.nomineesBySeq[nomineeSeq];
}
// construct the nomination
Nomination storage nomination = c.nominationsBySeq[c.numNominations++];
nomination.nominator = nominator;
nomination.nominee = n;
nomination.nominatedAmount = nominatedAmount;
nomination.nominationReason = nominationReason;
// update balances
n.nomineeUser.accountBalance += nominatedAmount;
n.nomineeTotal += nominatedAmount;
nominator.accountBalance =- nominatedAmount;
c.nominationTotal += nominatedAmount;
return true;
}
}
pragma solidity ^0.5.10;
import "./SafeMath.sol";
contract ERC20 {
function transfer(address recipient, uint256 amount) public returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
}
/**
* @title LockWallet
* @dev LockWallet 是一个通过共享密钥来锁币的合约
*/
contract LockWallet {
using SafeMath for uint256;
// 锁币启动密钥,默认只有合约创建者拥有,当锁币启动后,密钥公开
string public launchKey;
// 锁币启动密钥哈希值
bytes32 public launchKeyHash;
// 锁币密钥哈希值到锁币密钥的映射
mapping(bytes32 => string) public hashToKey;
// 锁币密钥集合
string[] public unlockKeySet;
// 锁币密码哈希值集合
bytes32[] public keyHashSet;
// 锁币启动过期时间(用区块高度表示),超过这个时间锁币未启动参与者可以提币
uint256 public launchTimeoutBlock;
// 合约创建人启动过期时间(用区块高度表示),超过这个时间锁币未启动合约创建人可以提币
uint256 public ownerLaunchTimeoutBlock;
// 锁币过期时间(用区块高度表示),超过这个时间参与者可以提币
uint256 public unlockTimeoutBlock;
// 锁币过期间隔,从锁币启动时间开始算
uint256 public unlockBlockInterval;
// 解锁密钥数量限制,超过这个数量的密钥明文被提交,参与者可以自由提币
uint256 public unlockKeyNum;
// 允许的 ERC20 合约地址
mapping(address => bool) public allowedTokens;
// 记录的参与者充币余额, (参与者地址 => (代币地址 => 代币数量))
mapping(address => mapping(address => uint256)) public balances;
// 参与者人数
uint256 public participatorSize;
// 合约创建者
address public owner;
// 不同币种的锁币总量
mapping(address => uint256) public totalLockAmount;
// 不同币种的参与者
mapping(address => address[]) tokenParticipators;
// 锁币合约状态
LockStatusEnum public lockStatus;
string private constant placeHolder = "-";
enum LockStatusEnum {
Init, Launched, Unlocked
}
/**
* 用来记录锁币启动
* @param launchKey 锁币启动密钥
* @param unlockTimeoutBlock 锁币过期时间
*/
event Launched(string launchKey, uint256 unlockTimeoutBlock);
/**
* 用来记录解锁成功
* @param lastKeyHolder 最后解锁人
*/
event Unlocked(address lastKeyHolder);
event KeyReleased(address keyHolder, string key, bytes32 keyHash);
/**
* 用来记录提币操作
* @param participator 提币地址
* @param tokenAddress 代币地址,以太币地址用 0 表示
* @param amountInWei 提币金额
* @param status 当时的合约状态
*/
event Withdraw(address participator, address tokenAddress, uint256 amountInWei, LockStatusEnum status);
/**
* 用来记录收到的代币
* @param participator 参与者地址
* @param tokenAddress 代币地址
* @param amountInWei 锁币数量
*/
event Received(address participator, address tokenAddress, uint256 amountInWei);
// 判断是否满足锁币条件,合约处于未解锁状态才可锁币
modifier Lockable {
require(lockStatus != LockStatusEnum.Unlocked);
_;
}
// 判断是否可提币,在余额足够的情况下,
// 合约处于解锁状态,可任意提币
// 合约处于锁定状态,在超过锁定时间后可提币
// 合约处于未启动状态,超过启动锁定期后可提币
modifier CouldWithdraw(address tokenAddress) {
require(balances[msg.sender][tokenAddress] > 0, "No enough balance");
if (lockStatus == LockStatusEnum.Init) {
require(block.number > launchTimeoutBlock, "Cannot withdraw before timeout");
if (msg.sender == owner) require(block.number > ownerLaunchTimeoutBlock, "Cannot withdraw before timeout");
}
if (lockStatus == LockStatusEnum.Launched) {
require(block.number > unlockTimeoutBlock, "Cannot withdraw before timeout");
}
_;
}
constructor(bytes32 _launchKeyHash,
bytes32[] memory _keyHashSet,
uint256 _launchBlockInterval,
uint256 _unlockBlockInterval,
uint256 _unlockKeyNum,
address[] memory _allowedTokens) public {
require(_launchKeyHash != 0x0, "Empty startKeyHash is not allowed");
require(_keyHashSet.length > 0, "Empty keyHashSet is not allowed");
require(_unlockKeyNum <= _keyHashSet.length, "unlockKeyNum should less than or equal to keyHashSet");
launchKeyHash = _launchKeyHash;
keyHashSet = _keyHashSet;
launchTimeoutBlock = block.number.add(_launchBlockInterval);
ownerLaunchTimeoutBlock = block.number.add(_launchBlockInterval * 2);
unlockBlockInterval = _unlockBlockInterval;
unlockKeyNum = _unlockKeyNum;
owner = msg.sender;
for (uint256 i = 0; i < _keyHashSet.length; i++) {
hashToKey[_keyHashSet[i]] = placeHolder;
}
for (uint256 j = 0; j < _allowedTokens.length; j++) {
allowedTokens[_allowedTokens[i]] = true;
}
lockStatus = LockStatusEnum.Init;
}
/**
* 任何人都可以通过正确的启动密钥来启动合约锁定
*/
function launch(string memory _launchKey) public {
require(block.number < launchTimeoutBlock, "Too late to launch");
require(sha256(abi.encodePacked(_launchKey)) == launchKeyHash, "Invalid launchKey");
lockStatus = LockStatusEnum.Launched;
launchKey = _launchKey;
unlockTimeoutBlock = block.number.add(unlockBlockInterval);
emit Launched(launchKey, unlockTimeoutBlock);
}
/**
* 任何人都可以通过正确的解锁密钥来解锁合约
*/
function unlock(string memory _lockKey) public {
require(lockStatus == LockStatusEnum.Launched);
bytes32 keyHash = sha256(abi.encodePacked(_lockKey));
require(sha256(abi.encodePacked(hashToKey[keyHash])) == sha256(abi.encodePacked(placeHolder)));
hashToKey[keyHash] = _lockKey;
unlockKeySet.push(_lockKey);
emit KeyReleased(msg.sender, _lockKey, keyHash);
if (unlockKeySet.length >= unlockKeyNum) {
lockStatus = LockStatusEnum.Unlocked;
emit Unlocked(msg.sender);
}
}
/**
* 用来将自己锁定的以太币全部提出
*/
function withdrawEth() CouldWithdraw(address(0x0)) public {
uint256 balance = balances[msg.sender][address(0x0)];
balances[msg.sender][address(0x0)] = 0x0;
msg.sender.transfer(balance);
totalLockAmount[address(0x0)] = totalLockAmount[address(0x0)].sub(balance);
emit Withdraw(msg.sender, address(0x0), balance, lockStatus);
}
/**
* 用来将自己锁定的 ERC20 代币全部提出
*/
function withdrawToken(address tokenAddress) CouldWithdraw(tokenAddress) public {
uint256 balance = balances[msg.sender][tokenAddress];
balances[msg.sender][tokenAddress] = 0x0;
ERC20 tokenInstance = ERC20(tokenAddress);
tokenInstance.transfer(msg.sender, balance);
totalLockAmount[tokenAddress] = totalLockAmount[tokenAddress].sub(balance);
emit Withdraw(msg.sender, tokenAddress, balance, lockStatus);
}
/**
* 批量释放锁定代币
*/
function batchRelease(address _tokenAddress, address [] memory _participators) public {
require(lockStatus != LockStatusEnum.Init);
require(lockStatus == LockStatusEnum.Unlocked || block.number > unlockTimeoutBlock);
if (_tokenAddress != address(0x0)) {
batchTokenRelease(_tokenAddress, _participators);
} else {
batchEthRelease(_participators);
}
}
function batchEthRelease(address [] memory _participators) private {
for (uint256 i = 0; i < _participators.length - 1; i++) {
address participator = _participators[i];
uint256 balance = balances[participator][address(0x0)];
if (balance > 0) {
balances[participator][address(0x0)] = 0x0;
participator.transfer(balance);
totalLockAmount[address(0x0)] = totalLockAmount[address(0x0)].sub(balance);
emit Withdraw(participator, address(0x0), balance, lockStatus);
}
}
}
function batchTokenRelease(address tokenAddress, address [] memory _participators) private {
for (uint256 i = 0; i < _participators.length - 1; i++) {
address participator = _participators[i];
uint256 balance = balances[participator][tokenAddress];
if (balance > 0) {
balances[participator][tokenAddress] = 0x0;
ERC20 tokenInstance = ERC20(tokenAddress);
tokenInstance.transfer(participator, balance);
totalLockAmount[tokenAddress] = totalLockAmount[tokenAddress].sub(balance);
emit Withdraw(participator, tokenAddress, balance, lockStatus);
}
}
}
/**
* 锁定指定数量 ERC20 代币
*/
function lockToken(address tokenAddress, uint256 amount) Lockable public {
require(allowedTokens[tokenAddress]);
if (balances[msg.sender][tokenAddress] == 0) {
tokenParticipators[tokenAddress].push(msg.sender);
if (balances[msg.sender][address(0x0)] == 0) {
participatorSize++;
}
}
ERC20 tokenInstance = ERC20(tokenAddress);
tokenInstance.transferFrom(msg.sender, address(this), amount);
balances[msg.sender][tokenAddress] = balances[msg.sender][tokenAddress].add(amount);
totalLockAmount[tokenAddress] = totalLockAmount[tokenAddress].add(amount);
emit Received(msg.sender, tokenAddress, amount);
}
function getParticipators(address tokenAddress) public view returns (address[] memory) {
return tokenParticipators[tokenAddress];
}
function getBriefInfo() public view returns (LockStatusEnum _lockStatus, uint256 _participatorSize, bytes32[] memory _keyHashSet, uint256 _releasedKeySize) {
_lockStatus = lockStatus;
_participatorSize = participatorSize;
_keyHashSet = keyHashSet;
_releasedKeySize = unlockKeySet.length;
}
/**
* 接收以太币锁定
*/
function() Lockable payable external {
require(msg.value > 0);
if (balances[msg.sender][address(0x0)] == 0) {
participatorSize++;
tokenParticipators[address(0x0)].push(msg.sender);
}
balances[msg.sender][address(0x0)] = balances[msg.sender][address(0x0)].add(msg.value);
totalLockAmount[address(0x0)] = totalLockAmount[address(0x0)].add(msg.value);
emit Received(msg.sender, address(0x0), msg.value);
}
}
pragma solidity ^0.6.3;
import "./Storage.sol";
contract Machine {
Storage public s;
constructor(Storage addr) public {
s = addr;
//calculateResult = 0;
}
function saveValue(uint x) public returns (bool) {
s.setValue(x);
return true;
}
function getValue() public view returns (uint) {
return s.val();
}
}
pragma solidity ^0.5.3;
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
function owner() public view returns (address) {
return _owner;
}
modifier onlyOwner() {
require(isOwner());
_;
}
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
/**
* @title RelayRegistry
* @dev Singleton contract that registers a whitelist of relays accessed by the factory and smart wallets. Contract is owned by an external account for now but ownership should be transferred to a governance contract in the future.
*/
contract RelayRegistry is Ownable {
event AddedRelay(address relay);
event RemovedRelay(address relay);
mapping (address => bool) public relays;
constructor(address initialRelay) public {
relays[initialRelay] = true;
}
/**
* @dev Allows relay registry owner to add or remove a relay from the whitelist
* @param relay Address of the selected relay
* @param value True to add them to the whitelist, false to remove them
*/
function triggerRelay(address relay, bool value) onlyOwner public returns (bool) {
relays[relay] = value;
if(value) {
emit AddedRelay(relay);
} else {
emit RemovedRelay(relay);
}
return true;
}
}
interface IERC20 {
function transfer(address to, uint256 value) external returns (bool);
}
/**
* @title Smart Wallet Contract
* @dev All functions of this contract should be called using delegatecall from the Proxy contract. This allows us to significantly reduce the deployment costs of smart wallets. All functions of this contract are executed in the context of Proxy contract.
*/
contract SmartWallet {
/**
* @dev Any Ether received at the fallback function should be forwarded to the signer address. This allows them to use Ether to send direct txs to this contract in case of relay downtime or censorship
*/
function() external payable {
address payable owner = abi.decode(store["owner"], (address));
owner.transfer(msg.value);
}
event Upgrade(address indexed newImplementation);
/**
* @dev Shared key value store. Data should be encoded and decoded using abi.encode()/abi.decode() by different functions. No data is actually stored in SmartWallet, instead everything is stored in the Proxy contract's context.
*/
mapping (bytes32 => bytes) public store;
modifier onlyRelay {
RelayRegistry registry = RelayRegistry(0x4360b517f5b3b2D4ddfAEDb4fBFc7eF0F48A4Faa);
require(registry.relays(msg.sender));
_;
}
modifier onlyOwner {
require(msg.sender == abi.decode(store["factory"], (address)) || msg.sender == abi.decode(store["owner"], (address)));
_;
}
/**
* @dev Function called once by Factory contract to initiate owner and nonce. This is necessary because we cannot pass arguments to a CREATE2-created contract without changing its address.
* @param owner Wallet Owner
*/
function initiate(address owner) public returns (bool) {
// this function can only be called by the factory
if(msg.sender != abi.decode(store["factory"], (address))) return false;
// store current owner in key store
store["owner"] = abi.encode(owner);
store["nonce"] = abi.encode(0);
return true;
}
/**
* @dev Same as above, but also applies a feee to a relayer address provided by the factory
* @param owner Wallet Owner
* @param relay Address of the relayer
* @param fee Fee paid to relayer in a token
* @param token Address of ERC20 contract in which fee will be denominated.
*/
function initiate(address owner, address relay, uint fee, address token) public returns (bool) {
require(initiate(owner), "internal initiate failed");
// Access ERC20 token
IERC20 tokenContract = IERC20(token);
// Send fee to relay
require(tokenContract.transfer(relay, fee), "fee transfer failed");
return true;
}
/**
* @dev Relayed token transfer. Submitted by a relayer on behalf of the wallet owner.
* @param to Recipient address
* @param value Transfer amount
* @param fee Fee paid to the relayer
* @param tokenContract Address of the token contract used for both the transfer and the fees
*/
function pay(address to, uint value, uint fee, address tokenContract, uint8 v, bytes32 r, bytes32 s) onlyRelay public returns (bool) {
uint currentNonce = abi.decode(store["nonce"], (uint));
require(abi.decode(store["owner"], (address)) == recover(keccak256(abi.encodePacked(msg.sender, to, tokenContract, abi.decode(store["factory"], (address)), value, fee, tx.gasprice, currentNonce)), v, r, s));
IERC20 token = IERC20(tokenContract);
store["nonce"] = abi.encode(currentNonce+1);
require(token.transfer(to, value));
require(token.transfer(msg.sender, fee));
return true;
}
/**
* @dev Direct token transfer. Submitted by the wallet owner
* @param to Recipient address
* @param value Transfer amount
* @param tokenContract Address of the token contract used for the transfer
*/
function pay(address to, uint value, address tokenContract) onlyOwner public returns (bool) {
IERC20 token = IERC20(tokenContract);
require(token.transfer(to, value));
return true;
}
/**
* @dev Same as above but allows batched transfers in multiple tokens
*/
function pay(address[] memory to, uint[] memory value, address[] memory tokenContract) onlyOwner public returns (bool) {
for (uint i; i < to.length; i++) {
IERC20 token = IERC20(tokenContract[i]);
require(token.transfer(to[i], value[i]));
}
return true;
}
/**
* @dev Internal function that executes a call to any contract
* @param contractAddress Address of the contract to call
* @param data calldata to send to contractAddress
* @param msgValue Amount in wei to be sent with the call to the contract from the wallet's balance
*/
function _execCall(address contractAddress, bytes memory data, uint256 msgValue) internal returns (bool result) {
assembly {
result := call(gas, contractAddress, msgValue, add(data, 0x20), mload(data), 0, 0)
}
}
/**
* @dev Internal function that executes a delegatecallcall to any contract
* @param contractAddress Address of the contract to delegatecall
* @param data calldata to send to contractAddress
*/
function _execDelegatecall(address contractAddress, bytes memory data) internal returns (bool result) {
assembly {
result := delegatecall(gas, contractAddress, add(data, 0x20), mload(data), 0, 0)
}
}
/**
* @dev Internal function that creates any contract
* @param bytecode of the new contract
*/
function _execCreate(bytes memory data) internal returns (bool result) {
address deployedContract;
assembly {
deployedContract := create(0, add(data, 0x20), mload(data))
}
result = (deployedContract != address(0));
}
/**
* @dev Internal function that creates any contract using create2
* @param bytecode of the new contract
* @param salt Create2 salt parameter
*/
function _execCreate2(bytes memory data, uint256 salt) internal returns (bool result) {
address deployedContract;
assembly {
deployedContract := create2(0, add(data, 0x20), mload(data), salt)
}
result = (deployedContract != address(0));
}
/**
* @dev Public function that allows the owner to execute a call to any contract
* @param contractAddress Address of the contract to call
* @param data calldata to send to contractAddress
* @param msgValue Amount in wei to be sent with the call to the contract from the wallet's balance
*/
function execCall(address contractAddress, bytes memory data, uint256 msgValue) onlyOwner public returns (bool) {
require(_execCall(contractAddress, data, msgValue));
return true;
}
/**
* @dev Public function that allows a relayer to execute a call to any contract on behalf of the owner
* @param contractAddress Address of the contract to call
* @param data calldata to send to contractAddress
* @param msgValue Amount in wei to be sent with the call to the contract from the wallet's balance
* @param fee Fee paid to the relayer
* @param tokenContract Address of the token contract used for the fee
*/
function execCall(address contractAddress, bytes memory data, uint256 msgValue, uint fee, address tokenContract, uint8 v, bytes32 r, bytes32 s) onlyRelay public returns (bool) {
uint currentNonce = abi.decode(store["nonce"], (uint));
require(abi.decode(store["owner"], (address)) == recover(keccak256(abi.encodePacked(msg.sender, contractAddress, tokenContract, abi.decode(store["factory"], (address)), data, msgValue, fee, tx.gasprice, currentNonce)), v, r, s));
require(_execCall(contractAddress, data, msgValue));
IERC20 token = IERC20(tokenContract);
store["nonce"] = abi.encode(currentNonce+1);
require(token.transfer(msg.sender, fee));
return true;
}
/**
* @dev Public function that allows the owner to execute a delegatecall to any contract
* @param contractAddress Address of the contract to delegatecall
* @param data calldata to send to contractAddress
*/
function execDelegatecall(address contractAddress, bytes memory data) onlyOwner public returns (bool) {
require(_execDelegatecall(contractAddress, data));
return true;
}
/**
* @dev Public function that allows a relayer to execute a delegatecall to any contract on behalf of the owner
* @param contractAddress Address of the contract to delegatecall
* @param data calldata to send to contractAddress
* @param fee Fee paid to the relayer
* @param tokenContract Address of the token contract used for the fee
*/
function execDelegatecall(address contractAddress, bytes memory data, uint fee, address tokenContract, uint8 v, bytes32 r, bytes32 s) onlyRelay public returns (bool) {
uint currentNonce = abi.decode(store["nonce"], (uint));
require(abi.decode(store["owner"], (address)) == recover(keccak256(abi.encodePacked(msg.sender, contractAddress, tokenContract, abi.decode(store["factory"], (address)), data, fee, tx.gasprice, currentNonce)), v, r, s));
require(_execDelegatecall(contractAddress, data));
IERC20 token = IERC20(tokenContract);
store["nonce"] = abi.encode(currentNonce+1);
require(token.transfer(msg.sender, fee));
return true;
}
/**
* @dev Public function that allows the owner to create any contract
* @param data bytecode of the new contract
*/
function execCreate(bytes memory data) onlyOwner public returns (bool) {
require(_execCreate(data));
return true;
}
/**
* @dev Public function that allows a relayer to create any contract on behalf of the owner
* @param data new contract bytecode
* @param fee Fee paid to the relayer
* @param tokenContract Address of the token contract used for the fee
*/
function execCreate(bytes memory data, uint fee, address tokenContract, uint8 v, bytes32 r, bytes32 s) onlyRelay public returns (bool) {
uint currentNonce = abi.decode(store["nonce"], (uint));
require(abi.decode(store["owner"], (address)) == recover(keccak256(abi.encodePacked(msg.sender, tokenContract, abi.decode(store["factory"], (address)), data, fee, tx.gasprice, currentNonce)), v, r, s));
require(_execCreate(data));
IERC20 token = IERC20(tokenContract);
store["nonce"] = abi.encode(currentNonce+1);
require(token.transfer(msg.sender, fee));
return true;
}
/**
* @dev Public function that allows the owner to create any contract using create2
* @param data bytecode of the new contract
* @param salt Create2 salt parameter
*/
function execCreate2(bytes memory data, uint salt) onlyOwner public returns (bool) {
require(_execCreate2(data, salt));
return true;
}
/**
* @dev Public function that allows a relayer to create any contract on behalf of the owner using create2
* @param data new contract bytecode
* @param salt Create2 salt parameter
* @param fee Fee paid to the relayer
* @param tokenContract Address of the token contract used for the fee
*/
function execCreate2(bytes memory data, uint salt, uint fee, address tokenContract, uint8 v, bytes32 r, bytes32 s) onlyRelay public returns (bool) {
uint currentNonce = abi.decode(store["nonce"], (uint));
require(abi.decode(store["owner"], (address)) == recover(keccak256(abi.encodePacked(msg.sender, tokenContract, abi.decode(store["factory"], (address)), data, salt, fee, tx.gasprice, currentNonce)), v, r, s));
require(_execCreate2(data, salt));
IERC20 token = IERC20(tokenContract);
store["nonce"] = abi.encode(currentNonce+1);
require(token.transfer(msg.sender, fee));
return true;
}
/**
* @dev Since all eth transfers to this contract are redirected to the owner. This is the only way for anyone, including the owner, to keep ETH on this contract.
*/
function depositEth() public payable {}
/**
* @dev Allows the owner to withdraw all ETH from the contract.
*/
function withdrawEth() public onlyOwner() {
address payable owner = abi.decode(store["owner"], (address));
owner.transfer(address(this).balance);
}
/**
* @dev Allows a relayer to change the address of the smart wallet implementation contract on behalf of the owner. New contract should have its own upgradability logic or Proxy will be stuck on it.
* @param implementation Address of the new implementation contract to replace this one.
* @param fee Fee paid to the relayer
* @param feeContract Address of the fee token contract
*/
function upgrade(address implementation, uint fee, address feeContract, uint8 v, bytes32 r, bytes32 s) onlyRelay public returns (bool) {
uint currentNonce = abi.decode(store["nonce"], (uint));
address owner = abi.decode(store["owner"], (address));
address factory = abi.decode(store["factory"], (address));
require(owner == recover(keccak256(abi.encodePacked(msg.sender, implementation, feeContract, factory, fee, tx.gasprice, currentNonce)), v, r, s));
store["nonce"] = abi.encode(currentNonce+1);
store["fallback"] = abi.encode(implementation);
IERC20 feeToken = IERC20(feeContract);
require(feeToken.transfer(msg.sender, fee));
emit Upgrade(implementation);
return true;
}
/**
* @dev Same as above, but activated directly by the owner.
* @param implementation Address of the new implementation contract to replace this one.
*/
function upgrade(address implementation) onlyOwner public returns (bool) {
store["fallback"] = abi.encode(implementation);
emit Upgrade(implementation);
return true;
}
/**
* @dev Internal function used to prefix hashes to allow for compatibility with signers such as Metamask
* @param messageHash Original hash
*/
function recover(bytes32 messageHash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 prefixedMessageHash = keccak256(abi.encodePacked(prefix, messageHash));
return ecrecover(prefixedMessageHash, v, r, s);
}
}
/**
* @title Proxy
* @dev This contract is usually deployed as part of every user's first gasless transaction. It refers to a hardcoded address of the smart wallet contract and uses its functions via delegatecall.
*/
contract Proxy {
/**
* @dev Shared key value store. All data across different SmartWallet implementations is stored here. It also keeps storage across different upgrades.
*/
mapping (bytes32 => bytes) public store;
/**
* @dev The Proxy constructor adds the hardcoded address of SmartWallet and the address of the factory (from msg.sender) to the store for later transactions
*/
constructor() public {
// set implementation address in storage
store["fallback"] = abi.encode(0xEfc66C37a06507bCcABc0ce8d8bb5Ac4c1A2a8AA); // SmartWallet address
// set factory address in storage
store["factory"] = abi.encode(msg.sender);
}
/**
* @dev The fallback functions forwards everything as a delegatecall to the implementation SmartWallet contract
*/
function() external {
address impl = abi.decode(store["fallback"], (address));
assembly {
let ptr := mload(0x40)
// (1) copy incoming call data
calldatacopy(ptr, 0, calldatasize)
// (2) forward call to logic contract
let result := delegatecall(gas, impl, ptr, calldatasize, 0, 0)
let size := returndatasize
// (3) retrieve return data
returndatacopy(ptr, 0, size)
// (4) forward return data back to caller
switch result
case 0 { revert(ptr, size) }
default { return(ptr, size) }
}
}
}
/**
* @title Smart wallet factory
* @dev Singleton contract responsible for deploying new smart wallet instances
*/
contract Factory {
event Deployed(address indexed addr, address indexed owner);
modifier onlyRelay {
RelayRegistry registry = RelayRegistry(0x4360b517f5b3b2D4ddfAEDb4fBFc7eF0F48A4Faa); // Relay Registry address
require(registry.relays(msg.sender));
_;
}
/**
* @dev Internal function used for deploying smart wallets using create2
* @param owner Address of the wallet signer address (external account) associated with the smart wallet
*/
function deployCreate2(address owner) internal returns (address) {
bytes memory code = type(Proxy).creationCode;
address addr;
assembly {
// create2
addr := create2(0, add(code, 0x20), mload(code), owner)
// revert if contract was not created
if iszero(extcodesize(addr)) {revert(0, 0)}
}
return addr;
}
/**
* @dev Allows a relayer to deploy a smart wallet on behalf of a user
* @param fee Fee paid from the user's newly deployed smart wallet to the relay
* @param token Address of token contract for the fee
*/
function deployWallet(uint fee, address token, uint8 v, bytes32 r, bytes32 s) onlyRelay public returns (address) {
address signer = recover(keccak256(abi.encodePacked(address(this), msg.sender, token, tx.gasprice, fee)), v, r, s);
address addr = deployCreate2(signer);
SmartWallet wallet = SmartWallet(uint160(addr));
require(wallet.initiate(signer, msg.sender, fee, token));
emit Deployed(addr, signer);
return addr;
}
/**
* @dev Allows a relayer to deploy a smart wallet and send a token transfer on behalf of a user
* @param fee Fee paid from the user's newly deployed smart wallet to the relay
* @param token Address of token contract for the fee
* @param to Transfer recipient address
* @param value Transfer amount
*/
function deployWallet(uint fee, address token, address to, uint value, uint8 v, bytes32 r, bytes32 s) onlyRelay public returns (address addr) {
address signer = recover(keccak256(abi.encodePacked(address(this), msg.sender, token, to, tx.gasprice, fee, value)), v, r, s);
addr = deployCreate2(signer);
SmartWallet wallet = SmartWallet(uint160(addr));
require(wallet.initiate(signer, msg.sender, fee, token));
require(wallet.pay(to, value, token));
emit Deployed(addr, signer);
}
/**
* @dev Allows a user to directly deploy their own smart wallet
*/
function deployWallet() public returns (address) {
address addr = deployCreate2(msg.sender);
SmartWallet wallet = SmartWallet(uint160(addr));
require(wallet.initiate(msg.sender));
emit Deployed(addr, msg.sender);
return addr;
}
/**
* @dev Same as above, but also sends a transfer from the newly-deployed smart wallet
* @param token Address of the token contract for the transfer
* @param to Transfer recipient address
* @param value Transfer amount
*/
function deployWallet(address token, address to, uint value) public returns (address) {
address addr = deployCreate2(msg.sender);
SmartWallet wallet = SmartWallet(uint160(addr));
require(wallet.pay(to, value, token));
require(wallet.initiate(msg.sender));
emit Deployed(addr, msg.sender);
return addr;
}
/**
* @dev Allows user to deploy their wallet and execute a call operation to a foreign contract.
* @notice The order of wallet.execCall & wallet.initiate is important. It allows the fee to be paid after the execution is finished. This allows collect-call use cases.
* @param contractAddress Address of the contract to call
* @param data calldata to send to contractAddress
* @param msgValue Amount in wei to be sent with the call to the contract from the wallet's balance
*/
function deployWalletExecCall(address contractAddress, bytes memory data, uint msgValue) public returns (address) {
address addr = deployCreate2(msg.sender);
SmartWallet wallet = SmartWallet(uint160(addr));
require(wallet.execCall(contractAddress, data, msgValue));
require(wallet.initiate(msg.sender));
emit Deployed(addr, msg.sender);
return addr;
}
/**
* @dev Allows a relayer to deploy a wallet and execute a call operation to a foreign contract on behalf of a user.
* @param contractAddress Address of the contract to call
* @param data calldata to send to contractAddress
* @param msgValue Amount in wei to be sent with the call to the contract from the wallet's balance
* @param fee Fee paid to the relayer
* @param token Address of the token contract for the fee
*/
function deployWalletExecCall(address contractAddress, bytes memory data, uint msgValue, uint fee, address token, uint8 v, bytes32 r, bytes32 s) onlyRelay public returns (address addr) {
address signer = recover(keccak256(abi.encodePacked(address(this), msg.sender, token, contractAddress, data, msgValue, tx.gasprice, fee)), v, r, s);
addr = deployCreate2(signer);
SmartWallet wallet = SmartWallet(uint160(addr));
require(wallet.execCall(contractAddress, data, msgValue));
require(wallet.initiate(signer, msg.sender, fee, token));
emit Deployed(addr, signer);
}
/**
* @dev Allows user to deploy their wallet and execute a delegatecall operation to a foreign contract.
* @param contractAddress Address of the contract to delegatecall
* @param data calldata to send to contractAddress
*/
function deployWalletExecDelegatecall(address contractAddress, bytes memory data) public returns (address) {
address addr = deployCreate2(msg.sender);
SmartWallet wallet = SmartWallet(uint160(addr));
require(wallet.execDelegatecall(contractAddress, data));
require(wallet.initiate(msg.sender));
emit Deployed(addr, msg.sender);
return addr;
}
/**
* @dev Allows a relayer to deploy a wallet and execute a delegatecall operation to a foreign contract on behalf of a user.
* @param contractAddress Address of the contract to delegatecall
* @param data calldata to send to contractAddress
* @param fee Fee paid to the relayer
* @param token Address of the token contract for the fee
*/
function deployWalletExecDelegatecall(address contractAddress, bytes memory data, uint fee, address token, uint8 v, bytes32 r, bytes32 s) onlyRelay public returns (address addr) {
address signer = recover(keccak256(abi.encodePacked(address(this), msg.sender, token, contractAddress, data, tx.gasprice, fee)), v, r, s);
addr = deployCreate2(signer);
SmartWallet wallet = SmartWallet(uint160(addr));
require(wallet.execDelegatecall(contractAddress, data));
require(wallet.initiate(signer, msg.sender, fee, token));
emit Deployed(addr, signer);
}
/**
* @dev Allows user to deploy their wallet and deploy a new contract through their wallet
* @param data bytecode of the new contract
*/
function deployWalletExecCreate(bytes memory data) public returns (address) {
address addr = deployCreate2(msg.sender);
SmartWallet wallet = SmartWallet(uint160(addr));
require(wallet.execCreate(data));
require(wallet.initiate(msg.sender));
emit Deployed(addr, msg.sender);
return addr;
}
/**
* @dev Allows a relayer to deploy a wallet and deploy a new contract through the wallet on behalf of a user.
* @param data bytecode of the new contract
* @param fee Fee paid to the relayer
* @param token Address of the token contract for the fee
*/
function deployWalletExecCreate(bytes memory data, uint fee, address token, uint8 v, bytes32 r, bytes32 s) onlyRelay public returns (address addr) {
address signer = recover(keccak256(abi.encodePacked(address(this), msg.sender, token, data, tx.gasprice, fee)), v, r, s);
addr = deployCreate2(signer);
SmartWallet wallet = SmartWallet(uint160(addr));
require(wallet.execCreate(data));
require(wallet.initiate(signer, msg.sender, fee, token));
emit Deployed(addr, signer);
}
/**
* @dev Allows user to deploy their wallet and deploy a new contract through their wallet using create2
* @param data bytecode of the new contract
* @param salt create2 salt parameter
*/
function deployWalletExecCreate2(bytes memory data, uint salt) public returns (address) {
address addr = deployCreate2(msg.sender);
SmartWallet wallet = SmartWallet(uint160(addr));
require(wallet.execCreate2(data, salt));
require(wallet.initiate(msg.sender));
emit Deployed(addr, msg.sender);
return addr;
}
/**
* @dev Allows a relayer to deploy a wallet and deploy a new contract through the wallet using create2 on behalf of a user.
* @param data bytecode of the new contract
* @param salt create2 salt parameter
* @param fee Fee paid to the relayer
* @param token Address of the token contract for the fee
*/
function deployWalletExecCreate2(bytes memory data, uint salt, uint fee, address token, uint8 v, bytes32 r, bytes32 s) onlyRelay public returns (address addr) {
address signer = recover(keccak256(abi.encodePacked(address(this), msg.sender, token, data, tx.gasprice, salt, fee)), v, r, s);
addr = deployCreate2(signer);
SmartWallet wallet = SmartWallet(uint160(addr));
require(wallet.execCreate2(data, salt));
require(wallet.initiate(signer, msg.sender, fee, token));
emit Deployed(addr, signer);
}
/**
* @dev Utility view function that allows clients to fetch a smart wallet address of any signer address
* @param owner Signer address
*/
function getCreate2Address(address owner) public view returns (address) {
bytes32 temp = keccak256(abi.encodePacked(bytes1(0xff), address(this), uint(owner), bytes32(keccak256(type(Proxy).creationCode))));
address ret;
uint mask = 2 ** 160 - 1;
assembly {
ret := and(temp, mask)
}
return ret;
}
/**
* @dev Utility view function that allows clients to fetch own smart wallet address
*/
function getCreate2Address() public view returns (address) {
return getCreate2Address(msg.sender);
}
/**
* @dev Utility view function that allows clients to query whether a signer's smart wallet can be deployed or has already been
* @param owner Signer address
*/
function canDeploy(address owner) public view returns (bool inexistent) {
address wallet = getCreate2Address(owner);
assembly {
inexistent := eq(extcodesize(wallet), 0)
}
}
/**
* @dev Utility view function that allows clients to query whether their signer's smart wallet can be deployed or has already been
*/
function canDeploy() public view returns (bool) {
return canDeploy(msg.sender);
}
/**
* @dev Internal function used to prefix hashes to allow for compatibility with signers such as Metamask
* @param messageHash Original hash
*/
function recover(bytes32 messageHash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 prefixedMessageHash = keccak256(abi.encodePacked(prefix, messageHash));
return ecrecover(prefixedMessageHash, v, r, s);
}
}
// File: @openzeppelin/contracts/math/SafeMath.sol
pragma solidity ^0.5.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
// File: @openzeppelin/contracts/token/ERC20/IERC20.sol
pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see `ERC20Detailed`.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through `transferFrom`. This is
* zero by default.
*
* This value changes when `approve` or `transferFrom` are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* > Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an `Approval` event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to `approve`. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: @openzeppelin/contracts/token/ERC20/ERC20.sol
pragma solidity ^0.5.0;
/**
* @dev Implementation of the `IERC20` interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using `_mint`.
* For a generic mechanism see `ERC20Mintable`.
*
* *For a detailed writeup see our guide [How to implement supply
* mechanisms](https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226).*
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an `Approval` event is emitted on calls to `transferFrom`.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard `decreaseAllowance` and `increaseAllowance`
* functions have been added to mitigate the well-known issues around setting
* allowances. See `IERC20.approve`.
*/
contract ERC20 is IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
/**
* @dev See `IERC20.totalSupply`.
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev See `IERC20.balanceOf`.
*/
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
/**
* @dev See `IERC20.transfer`.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public returns (bool) {
_transfer(msg.sender, recipient, amount);
return true;
}
/**
* @dev See `IERC20.allowance`.
*/
function allowance(address owner, address spender) public view returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See `IERC20.approve`.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
/**
* @dev See `IERC20.transferFrom`.
*
* Emits an `Approval` event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of `ERC20`;
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `value`.
* - the caller must have allowance for `sender`'s tokens of at least
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to `approve` that can be used as a mitigation for
* problems described in `IERC20.approve`.
*
* Emits an `Approval` event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to `approve` that can be used as a mitigation for
* problems described in `IERC20.approve`.
*
* Emits an `Approval` event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue));
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to `transfer`, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a `Transfer` event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] = _balances[sender].sub(amount);
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a `Transfer` event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destoys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a `Transfer` event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 value) internal {
require(account != address(0), "ERC20: burn from the zero address");
_totalSupply = _totalSupply.sub(value);
_balances[account] = _balances[account].sub(value);
emit Transfer(account, address(0), value);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
*
* This is internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an `Approval` event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 value) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = value;
emit Approval(owner, spender, value);
}
/**
* @dev Destoys `amount` tokens from `account`.`amount` is then deducted
* from the caller's allowance.
*
* See `_burn` and `_approve`.
*/
function _burnFrom(address account, uint256 amount) internal {
_burn(account, amount);
_approve(account, msg.sender, _allowances[account][msg.sender].sub(amount));
}
}
// File: @openzeppelin/contracts/ownership/Ownable.sol
pragma solidity ^0.5.0;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be aplied to your functions to restrict their use to
* the owner.
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* > Note: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// File: contracts/TokenRecipient.sol
pragma solidity ^0.5.0;
interface TokenRecipient {
function tokensReceived(address from, address to, uint amount) external;
}
// File: contracts/TaurusToken.sol
pragma solidity ^0.5.0;
contract TaurusToken is ERC20, Ownable {
string public name = "Taurus Token";
string public symbol = "TAU";
uint8 public decimals = 18;
uint256 private _cap;
TokenRecipient public tokenRecipient;
constructor(uint256 cap) public {
require(cap > 0, "cap is 0");
_cap = cap;
}
function mint(address account, uint256 amount) public onlyOwner returns (bool) {
require(totalSupply().add(amount) <= _cap, "cap exceeded");
_mint(account, amount);
return true;
}
function setTokenRecipient(address _tokenRecipient) onlyOwner public {
tokenRecipient = TokenRecipient(_tokenRecipient);
}
function transfer(address recipient, uint256 amount) public returns (bool) {
if (address(tokenRecipient) != address(0x0)) tokenRecipient.tokensReceived(msg.sender, recipient, amount);
return super.transfer(recipient, amount);
}
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
if (address(tokenRecipient) != address(0x0)) tokenRecipient.tokensReceived(sender, recipient, amount);
return super.transferFrom(sender, recipient, amount);
}
}
// File: contracts/MockMinterToken.sol
pragma solidity ^0.5.0;
contract MockMinterToken is TaurusToken {
using SafeMath for uint256;
//Special Params
uint256 private bonusOfFirstBlock = 42 * 10 ** uint256(decimals); //Bonus of first block
uint256 private bonusChangeHeight = 1314000; //How many heights the bonus will be changed
uint32 private bonusChangeRate = 1; //How much percent the bonus will be changed, 1%
uint256 private zeroHeight;
uint256 private latestTotalSupply;
uint256 public depositLimit = 1000000 * 10 ** uint256(decimals); // At least 1000000 taurus to be a BP
uint256 public voteLimit = 100 * 10 ** uint256(decimals); // At least 100 taurus to be a voter
uint8 private defaultShareRate = 40;//Default block Producer's share rate, 40%
uint32 public dampChangeHeight = 43200 * 14; //Depreciation height change
uint32 public defaultDampSpeed = 10;
address public foundationAccount = address(0x29b1D23b5E0Da32B8490b2Fa6C78FE9d1eba1C3A);//Foundation Account
address public rcAccount = address(0x9A24B8113F35F330A82d8EE0F6E1D3511481478F); //Risk Controll Account
uint8 public foundationRate = 10; //10%
uint8 private minBPs = 1; // At least 1 BP to mine
uint32 public WithdrawWaitHeight = 43200; //withdraw per 24housr at least
event BPChanged(address bpAddress, uint8 status); // status: 1 for newly registered, 2 for unregistered
constructor(uint256 cap) TaurusToken(cap)public {
zeroHeight = block.number - 1;
latestTotalSupply = 0;
}
function transfer(address recipient, uint256 amount) public returns (bool success) {
if(isAmendVote(recipient)) amendVote(amount, recipient, true);
if(isAmendVote(msg.sender)) amendVote(amount, msg.sender, false);
return super.transfer(recipient, amount);
}
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool success) {
if(isAmendVote(recipient)) amendVote(amount, recipient, true);
if(isAmendVote(sender)) amendVote(amount, sender, false);
return super.transferFrom(sender, recipient, amount);
}
function () external payable {
revert();
}
struct VoteData {
address votedBP;
bool status;
uint256 voteHeight;
uint32 dampSpeed;
uint256 payableBonus;
uint256 lastWithdrawHeight;
}
mapping(address => VoteData) public voteData;
mapping(address => uint32) public voterDampSpeed;
address[] public voterAddress;
struct BlockProducer {
uint32 shareRate;
uint8 status;
uint256 votes;
uint256 payableBonus;
uint256 lastWithdrawHeight;
}
mapping(address => BlockProducer) public blockProducer;
address[] public bpAddress;
modifier onlyBP {
require(blockProducer[msg.sender].status == 1);
_;
}
function setManagerAccounts(address fAddress, address rcAddress) public onlyOwner {
require(fAddress != address(0) && rcAddress != address(0));
foundationAccount = fAddress;
rcAccount = rcAddress;
}
function mine() public onlyOwner {
uint256 h = getBlockHeight();
uint256 totalSupply = totalBonus(h);
if(totalSupply > latestTotalSupply) {
uint256 newTokens = totalSupply.sub(latestTotalSupply);
super.mint(owner(), newTokens);
latestTotalSupply = totalSupply;
}
}
function getBlockHeight() public view returns(uint256 blockHeight) {
return (block.number - 1 - zeroHeight) * 7;
}
/**
* Register New BP
*/
function registerBP() public {
//Any address has more than 100 million tokens can be apply for being BP
require(balanceOf(msg.sender) >= depositLimit, "You should have <depositLimit> tokens at least to be block producer!");
require(blockProducer[msg.sender].status != 1, "You account is block producer."); // Already registered
blockProducer[msg.sender].shareRate = defaultShareRate;
blockProducer[msg.sender].status = 1;
blockProducer[msg.sender].votes = balanceOf(msg.sender);
bpAddress.push(msg.sender);
emit BPChanged(msg.sender, 1);
}
/**
* Delete BP
*/
function deleteBP(address bpAddr) public onlyOwner {
_deleteBP(bpAddr);
}
function unRegisterBP() public {
_deleteBP(msg.sender);
}
function _deleteBP(address bpAddr) private {
require(blockProducer[bpAddr].status == 1, "The address of block producer is not available!");
blockProducer[bpAddr].status = 2;
blockProducer[bpAddr].votes = 0;
deleteAddressFromArray(bpAddr, bpAddress);
emit BPChanged(bpAddr, 2);
}
function getAllBPAddress() public view returns (address[] memory) {
return bpAddress;
}
function isBP(address addr) public view returns(bool, uint256) {
uint256 i = 0;
for(; i < bpAddress.length; i++) {
if(bpAddress[i] == addr) {
return (true, i);
}
}
return (false, i);
}
/*
* Change the Deposit token limit
*/
function setDepositLimit(uint256 amount) public onlyOwner {
require(amount >= 0 && amount <= 100000000 * 10 ** uint256(decimals), "depositLimit should be 0-100000000.");
depositLimit = amount;
}
function setVoteLimit(uint8 amount) public onlyOwner {
require(amount >= 0, "VoteLimit should be more than zero.");
voteLimit = amount;
}
function deleteAddressFromArray(address addr, address[] storage arr) private {
//delete the address from the array
uint256 index = 0;
bool has = false;
for(uint256 i = 0; i < arr.length; i++) {
if(arr[i] == addr) {
index = i;
has = true;
break;
}
}
//remove the index
if(has) {
for (uint256 i = index; i < arr.length - 1; i++){
arr[i] = arr[i+1];
}
delete arr[arr.length - 1];
arr.length--;
}
}
/**
* set share rate with voters
*/
function setShareRate(uint32 rate) public onlyBP {
// During height range
require(rate > 0 && rate < 100, "The share rate should be 1-99.");
blockProducer[msg.sender].shareRate = rate;
}
/**
* Count shared part to bp from owner
*/
function sendBonus() public onlyOwner {
require(bpAddress.length >= minBPs, "There should be more than <minBPs> block producers at least."); // Min bp quantity
uint256 ttlMinedAmount = balanceOf(owner());
//Send to Foundation
uint256 foundationAmount = ttlMinedAmount.mul(foundationRate).div(100);
transfer(foundationAccount, foundationAmount);
//Count the bonus of bp and voters
uint256 balance = ttlMinedAmount.sub(foundationAmount);
uint256 ttlAmountForBP = 0;
uint256 ttlAmountForVoters = 0;
uint256 totalVotes = getTotalVotes();
for(uint256 i = 0; i < bpAddress.length; i++) {
if(blockProducer[bpAddress[i]].status == 1 && balanceOf(bpAddress[i]) >= depositLimit) {
uint256 currentBPAllVotes = blockProducer[bpAddress[i]].votes; //Including node himself
uint256 currentBPVotesExceptNode = currentBPAllVotes.sub(balanceOf(bpAddress[i]));
//Count the bp bonus, but do not transfer to bp account.
uint256 bpAmount = balance * idiv(currentBPAllVotes, totalVotes, decimals) * blockProducer[bpAddress[i]].shareRate / (100 * 10 ** uint256(decimals));
blockProducer[bpAddress[i]].payableBonus = blockProducer[bpAddress[i]].payableBonus.add(bpAmount);
ttlAmountForBP = ttlAmountForBP.add(bpAmount);
//Count voters bonus, but do not transfer to the voter's account untill the owner call batch transfer function.
uint256 votersAmount = balance * idiv(currentBPAllVotes, totalVotes, decimals) * (100 - blockProducer[bpAddress[i]].shareRate) / (100 * 10 ** uint256(decimals));
uint256 ttl = setVotersBonus(votersAmount, currentBPVotesExceptNode, bpAddress[i]);
ttlAmountForVoters = ttlAmountForVoters.add(ttl);
} else {
blockProducer[bpAddress[i]].payableBonus = 0;
}
}
//Send balance to RiskControl Pool
balance = balance.sub(ttlAmountForBP).sub(ttlAmountForVoters);
if(balance > 0 && balanceOf(owner()) >= balance) transfer(rcAccount, balance);
}
function setVotersBonus(uint256 amount, uint256 totalVotes, address bp) private onlyOwner returns(uint256) {
//uint256 totalVotes = getBPVotes(bp) - balances[bp]; //Only Voters except the bp tokens
uint256 ttlAmountForVoters = 0;
for(uint256 i = 0; i < voterAddress.length; i++) {
if(voteData[voterAddress[i]].votedBP == bp && voteData[voterAddress[i]].status) {
uint256 voterAmount = amount * idiv(balanceOf(voterAddress[i]), totalVotes, decimals) / 10 ** uint256(decimals);
//矿机折旧 = (1 - r) ^ int[(当前高度 - 投票时高度) / 43200 / 14]
uint256 e = (getBlockHeight() - voteData[voterAddress[i]].voteHeight) / dampChangeHeight;
uint256 damp = idiv((100 - voteData[voterAddress[i]].dampSpeed) ** e, 100 ** e, decimals);
voterAmount = voterAmount.mul(damp) / 10 ** uint256(decimals);
ttlAmountForVoters = ttlAmountForVoters.add(voterAmount);
voteData[voterAddress[i]].payableBonus = voteData[voterAddress[i]].payableBonus.add(voterAmount);
}
}
return(ttlAmountForVoters);
}
function isAmendVote(address addr) private view returns(bool) {
bool re = true;
if(addr == owner() || addr == foundationAccount || addr == rcAccount) re = false;
return re;
}
function preVoterWithdraw(address[] memory voters) public view returns(uint32 i, uint8 errorCode) {
for(i = 0; i < voters.length; i++) {
if(voteData[voters[i]].status) {
if(voteData[voters[i]].payableBonus > 0) {
if(balanceOf(owner()) >= voteData[voters[i]].payableBonus) {
if(block.number - 1 - zeroHeight - voteData[voters[i]].lastWithdrawHeight > WithdrawWaitHeight) {
//
} else {
errorCode = 6; // Can not withdraw within 24 housrs
break;
}
} else {
errorCode = 4; //owner's balance is not enough
break;
}
} else {
errorCode = 3; //payableBonus is 0
break;
}
} else {
errorCode = 2; //Not voter
break;
}
}
return (i, errorCode);
}
function preBPWithdraw(address[] memory bps) public view returns(uint32 i, uint8 errorCode) {
for(i = 0; i < bps.length; i++) {
if(blockProducer[bps[i]].status == 1) {
if(blockProducer[bps[i]].payableBonus > 0) {
if(block.number - 1 - zeroHeight - blockProducer[bps[i]].lastWithdrawHeight > WithdrawWaitHeight) {
//
} else {
errorCode = 6; // Can not withdraw within 24 hours
break;
}
} else {
errorCode = 3; // payableBonus is 0
break;
}
} else {
errorCode = 2; // Not BP
break;
}
}
return (i, errorCode);
}
function voterWithdraw(address[] memory voters) public onlyOwner returns(bool re) {
re = true;
for(uint32 i = 0; i < voters.length; i++) {
if(transfer(voters[i], voteData[voters[i]].payableBonus)) {
voteData[voters[i]].payableBonus = 0;
voteData[voters[i]].lastWithdrawHeight = getBlockHeight();
} else {
re = false; //false while transfer
break;
}
}
return re;
}
function bpWithdraw(address[] memory bps) public onlyOwner returns(bool re) {
re = true;
for(uint32 i = 0; i < bps.length; i++) {
if(transfer(bps[i], blockProducer[bps[i]].payableBonus)) {
blockProducer[bps[i]].payableBonus = 0;
blockProducer[bps[i]].lastWithdrawHeight = getBlockHeight();
} else {
re = false; //false while transfer
break;
}
}
return re;
}
function getVoterCount() public view returns(uint256 count) {
return voterAddress.length;
}
function getBPCount() public view returns(uint256 count) {
return bpAddress.length;
}
function getTotalVotes() public view returns(uint256 totalVotes) {
uint256 ttlVotes = 0;
for(uint256 i = 0; i < bpAddress.length; i++) {
if(blockProducer[bpAddress[i]].status == 1) {
ttlVotes = ttlVotes.add(blockProducer[bpAddress[i]].votes);
}
}
return(ttlVotes);
}
function getBPVotes(address bpAddr) public view returns(uint256 votes) {
return (blockProducer[bpAddr].votes);
}
function setVoterDampSpeed(address voter, uint32 dampSpeed) onlyOwner public {
require(dampSpeed < 100);
voterDampSpeed[voter] = dampSpeed;
}
function getVoterDampSpeed(address voter) public view returns(uint32) {
if (voterDampSpeed[voter] == 0x0) return defaultDampSpeed;
return voterDampSpeed[voter];
}
/**
* vote
*/
function vote(address bpAddr) public {
// The voter will vote all his account to the bp
// If change the bpAddress, means the previous bp will lose his votes
require(msg.sender != owner() && msg.sender != foundationAccount && msg.sender != rcAccount, "Voter could not be owner/foundation/RCP!");
require(blockProducer[msg.sender].status != 1, "BP Can not vote.");
require(balanceOf(msg.sender) >= voteLimit, "Balance of voter's account should have <voteLimit> Tokens at least!");
require(blockProducer[bpAddr].status == 1, "The account to be voted should be Block Producer!");
require(!(voteData[msg.sender].status && voteData[msg.sender].votedBP == bpAddr), "You have voted to this Block Producer!");
if(voteData[msg.sender].status) {
//Revote new BP
cancelVote();
} else {
//New vote
if (voteData[msg.sender].votedBP == address(0x0)) {
voteData[msg.sender].voteHeight = getBlockHeight();
voteData[msg.sender].dampSpeed = getVoterDampSpeed(msg.sender);
}
}
voteData[msg.sender].status = true;
voteData[msg.sender].votedBP = bpAddr;
blockProducer[bpAddr].votes = blockProducer[bpAddr].votes.add(balanceOf(msg.sender));
voterAddress.push(msg.sender);
}
function cancelVote() public {
// Cancel the previous vote data
require(voteData[msg.sender].status, "Your account has not voted to any block producer.");
address preVotedBP = voteData[msg.sender].votedBP;
blockProducer[preVotedBP].votes = blockProducer[preVotedBP].votes.sub(balanceOf(msg.sender));
voteData[msg.sender].status = false;
deleteAddressFromArray(msg.sender, voterAddress);// Big bug ******
}
function amendVote(uint256 amount, address addr, bool isAdd) private {
//Change the vote after transfer operation
//首先看addr是否已经参与投票
//对blockProducer的票仓做改变
if(voteData[addr].status) {
if(isAdd) blockProducer[voteData[addr].votedBP].votes = blockProducer[voteData[addr].votedBP].votes.add(amount);
else blockProducer[voteData[addr].votedBP].votes = blockProducer[voteData[addr].votedBP].votes.sub(amount);
}
//If the transfer account is bp:
if(blockProducer[addr].status == 1) {
if(isAdd) blockProducer[addr].votes = blockProducer[addr].votes.add(amount);
else blockProducer[addr].votes = blockProducer[addr].votes.sub(amount);
}
}
//Big Number Calculation
//Ex. idiv(10,6,8) = 166666667, 166666667 / 10 ** 8 = 1.66666667
function idiv(uint256 numerator, uint256 denominator, uint256 precision) private pure returns(uint256 quotient) {
uint256 _numerator = numerator * 10 ** (precision + 1);
uint256 _quotient = ((_numerator / denominator) + 5) / 10;
return (_quotient);
}
function geometricProgression(uint256 r, uint256 q) private pure returns(uint256 result) {
// r < 100, while r = 99 means 99%
uint256 b = r.mul(100 ** q - r ** q);
return idiv(b, (100 - r) * 100 ** q, 8);
}
function getA(uint256 r, uint256 q) private view returns(uint256 result) {
return (geometricProgression(r, q) + 10 ** 8).mul(bonusChangeHeight) / 10 ** 8;
}
function getB(uint256 r, uint256 q, uint256 height) private view returns(uint256 result) {
return (bonusChangeHeight.mul(q + 1).sub(height)).mul(idiv(r ** q, 100 ** q, 8)) / 10 ** 8;
}
function totalBonus(uint256 height) private view returns(uint256 result) {
uint256 q = height / bonusChangeHeight;
return (getA(100 - bonusChangeRate, q).sub(getB(100 - bonusChangeRate, q, height))).mul(bonusOfFirstBlock);
}
}
// File: @openzeppelin/contracts/math/SafeMath.sol
pragma solidity ^0.5.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 sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
// File: @openzeppelin/contracts/token/ERC20/IERC20.sol
pragma solidity ^0.5.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);
}
// File: @openzeppelin/contracts/token/ERC20/ERC20.sol
pragma solidity ^0.5.0;
contract ERC20 is IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
function transfer(address recipient, uint256 amount) public returns (bool) {
_transfer(msg.sender, recipient, amount);
return true;
}
function allowance(address owner, address spender) public view returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 value) public returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount));
return true;
}
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue));
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue));
return true;
}
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] = _balances[sender].sub(amount);
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
function _burn(address account, uint256 value) internal {
require(account != address(0), "ERC20: burn from the zero address");
_totalSupply = _totalSupply.sub(value);
_balances[account] = _balances[account].sub(value);
emit Transfer(account, address(0), value);
}
function _approve(address owner, address spender, uint256 value) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = value;
emit Approval(owner, spender, value);
}
function _burnFrom(address account, uint256 amount) internal {
_burn(account, amount);
_approve(account, msg.sender, _allowances[account][msg.sender].sub(amount));
}
}
// File: @openzeppelin/contracts/ownership/Ownable.sol
pragma solidity ^0.5.0;
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
function owner() public view returns (address) {
return _owner;
}
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// File: contracts/TokenRecipient.sol
pragma solidity ^0.5.0;
interface TokenRecipient {
function tokensReceived(address from, address to, uint amount) external;
}
// File: contracts/TaurusToken.sol
pragma solidity ^0.5.0;
contract TaurusToken is ERC20, Ownable {
string public name = "Taurus Token";
string public symbol = "TAU";
uint8 private _decimals = 18;
uint256 private _maxSupply = 100000000 * 10 ** uint256(_decimals);
//###Need to add this controller, to let other tokenmanager controller this token contract
address public controller;
function setController(address controllerContractAddress) public onlyOwner {
controller = controllerContractAddress;
}
modifier onlyController {
require(msg.sender == controller);
_;
}
TokenRecipient public tokenRecipient;
constructor() public {
}
function maxSupply() public view returns (uint256) {
return _maxSupply;
}
function decimals() public view returns (uint8) {
return _decimals;
}
function mint(uint256 amount) public onlyController returns (bool) {
require(totalSupply().add(amount) <= _maxSupply, "cap exceeded");
_mint(owner(), amount);
return true;
}
/*
function setTokenRecipient(address _tokenRecipient) onlyOwner public {
tokenRecipient = TokenRecipient(_tokenRecipient);
}
*/
function transfer(address recipient, uint256 amount) public returns (bool) {
if (address(tokenRecipient) != address(0x0)) tokenRecipient.tokensReceived(msg.sender, recipient, amount);
return super.transfer(recipient, amount);
}
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
if (address(tokenRecipient) != address(0x0)) tokenRecipient.tokensReceived(sender, recipient, amount);
return super.transferFrom(sender, recipient, amount);
}
}
// File: contracts/MockMinterToken.sol
pragma solidity ^0.5.0;
pragma experimental ABIEncoderV2;
contract MockMinterToken is Ownable {
using SafeMath for uint256;
TaurusToken public token;
// Special Params
uint256 private bonusOfFirstBlock; // Bonus of first block
uint256 public zeroHeight;
uint256 public depositLimit; // You need at least 1000000 taurus to be a BP
uint256 public SuperBPDepositLimit; //
uint256 public voteLimit; // You need at least 100 taurus to be a voter
uint256 private bonusChangeHeight = 518400; // Bonus changing interval
uint32 private bonusChangeRate = 5; // How much percent of the bonus will be changed, 1%
uint256 public latestTotalSupply = 0;
// address public foundationAccount = address(0x8d24783e1c47F94f4d4081c5869Bd307E81DE642); // Foundation Account
// address public rcAccount = address(0x148b55cDD62ef56e7175Bbef869C1b583A67735B); // Risk Controll Account
// address public operatorAccount = address(0x29b1D23b5E0Da32B8490b2Fa6C78FE9d1eba1C3A);
// address public withdrawAccount = address(0x9A24B8113F35F330A82d8EE0F6E1D3511481478F);
address public foundationAccount = address(0xdD870fA1b7C4700F2BD7f44238821C26f7392148); // Foundation Account
address public rcAccount = address(0xdD870fA1b7C4700F2BD7f44238821C26f7392148); // Risk Controll Account
address public operatorAccount = address(0xdD870fA1b7C4700F2BD7f44238821C26f7392148);
address public bonusManagerAccount = address(0xdD870fA1b7C4700F2BD7f44238821C26f7392148);
uint8 public defaultDampSpeed = 0;
uint8 public defaultShareRate = 20; // Default block producer's share rate, 40%
// Event is signaled on BP changing: register, unregister
// address: the address of the BP changed
// status: 1 for newly registered, 2 for unregistered
event BPChanged(address bpAddress, uint8 status);
// Event is signaled on Voters' changing their votes: add vote, remove vote
// voter: voter's address
// bp: the BP's address that is voted to
// flag: 1 for add, 2 for remove
event VoteChanged(address voter, address bp, uint256 amount, uint8 flag);
struct VoteData {
address votedBP; // The BP that current voter is voted to
bool status; // true: voted to BP, false: the votes are cancelled
uint256 voteHeight; // The block number when the vote is cast
uint32 dampSpeed; // Voting channels' damp speed, if no damp, it's 0
uint256 payableBonus; // The bonus that is not yet released to the voter
uint256 lastWithdrawHeight; // The block number updated when the voter asks for bonus withdraw
}
mapping(address => VoteData) public voteData;
mapping(address => uint32) public voterDampSpeed;
struct BlockProducer {
uint32 shareRate; // The share rate between block producer and voters
uint8 status; // 0: not registered, 1: is BP, 2: was BP in the past
uint256 votes; // Number of votes including all voters' votes and self's votes
uint256 activeVoters; // Number of active voters voting to this BP
uint256 payableBonus; // The bonus that is not yet released to the BP
uint256 lastWithdrawHeight; // The block number updated when the BP asks for bonus withdraw
string description; // BP's desciption, format being: NameʭLocationʭDescription
uint8 bpType; // BP's type, 0-Super BP, 1-Normal BP
address[] relatives; // is byType is 0, relative address is normal bp. if byType is 1, relative address is super bp
}
mapping(address => BlockProducer) public blockProducer;
address[] public bpAddress;
mapping(address => address[]) public bpVoter;
modifier onlyBP {
require(blockProducer[msg.sender].status == 1, 'The function is only callable by BP');
_;
}
modifier onlyOperator {
require(msg.sender == operatorAccount, 'The function is only called by operator account');
_;
}
function () external payable {
revert('The contract is not payable');
}
//constructor and initialization function
constructor(address taurusTokenAddress) public {
setERC20Contract(taurusTokenAddress);
init();
}
function setERC20Contract(address taurusTokenAddress) public onlyOwner {
token = TaurusToken(taurusTokenAddress);
bonusOfFirstBlock = 738 * 10 ** uint256(token.decimals()-2);
depositLimit = 0; // You need at least 1000000 taurus to be a BP
SuperBPDepositLimit = 200 * 10 ** uint256(token.decimals()); //
voteLimit = 0; // You need at least 100 taurus to be a voter
}
function init() public onlyOwner {
zeroHeight = block.number - 1;
latestTotalSupply = 0;
}
// Set foundation account and RC account addresses
function setManagerAccounts(address _fAddress,
address _rcAddress,
address _operatorAccount)
public onlyOwner {
require(_fAddress != address(0) && _rcAddress != address(0) && _operatorAccount != address(0), 'Address cannot be 0');
foundationAccount = _fAddress;
rcAccount = _rcAddress;
operatorAccount = _operatorAccount;
}
// Mining
function mine() public onlyOwner {
uint256 h = getBlockHeight();
uint256 totalSupply = totalBonus(h);
if(totalSupply > latestTotalSupply) {
uint256 newTokens = totalSupply.sub(latestTotalSupply);
token.mint(newTokens);
latestTotalSupply = totalSupply;
}
}
// Register New BP
function registerBP(address _bpAddress, string memory _description, uint8 _bpType) public onlyOperator{
// Any address has more than 100 million tokens can apply for BP position
if(_bpType == 0) require(token.balanceOf(_bpAddress) >= SuperBPDepositLimit, "You should have <super depositLimit> tokens at least to be block producer!");
else require(token.balanceOf(_bpAddress) >= depositLimit, "You should have <depositLimit> tokens at least to be block producer!");
// Already registered
require(blockProducer[_bpAddress].status != 1, "You account is block producer.");
// A voter cannot apply for BP position
require(voteData[_bpAddress].status != true, "You cannot be a voter and a BP at the same time");
blockProducer[_bpAddress].shareRate = defaultShareRate;
blockProducer[_bpAddress].status = 1;
blockProducer[_bpAddress].votes = token.balanceOf(_bpAddress); //Vote by self
blockProducer[_bpAddress].activeVoters = 0;
blockProducer[_bpAddress].description = _description;
blockProducer[_bpAddress].bpType = _bpType;
blockProducer[_bpAddress].relatives = new address[](0);
bpAddress.push(_bpAddress);
emit BPChanged(_bpAddress, 1);
}
function deleteBP(address _bpAddress) public onlyOperator{
require(blockProducer[_bpAddress].status == 1, "The address of block producer is not available!");
if(blockProducer[_bpAddress].bpType == 0) {
//If super block producer is deleted
for(uint i = 0; i < blockProducer[_bpAddress].relatives.length; i++) {
address boundedBPAddress = blockProducer[_bpAddress].relatives[i];
blockProducer[boundedBPAddress].relatives.length = 0;
}
blockProducer[_bpAddress].relatives.length = 0;
} else {
//if normal block producer is deleted
unbindSuperBP(blockProducer[_bpAddress].relatives[0], _bpAddress);
}
blockProducer[_bpAddress].status = 2;
blockProducer[_bpAddress].activeVoters = 0;
bpVoter[_bpAddress].length = 0;
deleteAddressFromArray(_bpAddress, bpAddress);
emit BPChanged(_bpAddress, 2);
}
//*** TEST SCRIPT ***
function testSendtoBps() public onlyOperator {
transferFrom(address(0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c), address(0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C), 100000000000000000000);
transferFrom(address(0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c), address(0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB), 100000000000000000000);
transferFrom(address(0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c), address(0x583031D1113aD414F02576BD6afaBfb302140225), 100000000000000000000);
transferFrom(address(0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c), address(0xdD870fA1b7C4700F2BD7f44238821C26f7392148), 100000000000000000000);
transferFrom(address(0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c), address(0x265a5C3DD46EC82e2744f1d0E9fB4eD75D56132a), 100000000000000000000);
}
function testRegisterBPs() public onlyOperator {
/*
registerBP(address(0x0bD170e705ba74d6E260da59AF38EE3980Cf1ce3), "I am kov#2", 0);
registerBP(address(0x3444E23231619b361c8350F4C83F82BCfAB36F65), "I am kov#3", 1);
registerBP(address(0x54937FFd054B6B78c87C48D113B6108481D46462), "I am kov#4", 1);
registerBP(address(0x754Bd5d99a18ECd71429b1BFF7F5054d2C61A142), "I am kov#5", 1);
registerBP(address(0x94E03C64B11e318ecAbDd823289940E54dBCaA2C), "I am kov#6", 1);
*/
registerBP(address(0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C), "I am kov#2", 0);
registerBP(address(0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB), "I am kov#3", 1);
registerBP(address(0x583031D1113aD414F02576BD6afaBfb302140225), "I am kov#4", 1);
registerBP(address(0xdD870fA1b7C4700F2BD7f44238821C26f7392148), "I am kov#5", 1);
registerBP(address(0x265a5C3DD46EC82e2744f1d0E9fB4eD75D56132a), "I am kov#6", 1);
}
function testBindBps() public onlyOperator {
bindSuperBP(address(0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C), address(0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB));
bindSuperBP(address(0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C), address(0x583031D1113aD414F02576BD6afaBfb302140225));
bindSuperBP(address(0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C), address(0xdD870fA1b7C4700F2BD7f44238821C26f7392148));
bindSuperBP(address(0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C), address(0x265a5C3DD46EC82e2744f1d0E9fB4eD75D56132a));
}
function testSendVoter() public {
transferFrom(address(0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c), address(0xC863E6F19DDbC18E79Ff48C4c4ceC17AB7Bbf967), 13000000000000000000);
transferFrom(address(0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c), address(0x496Bc829586027Da96fD4C0AC3a1bD2E2C67b179), 14000000000000000000);
transferFrom(address(0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c), address(0xfA010eA1030C353cebFe9075EAa028fFf44DE6A8), 15000000000000000000);
transferFrom(address(0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c), address(0xDA4d54aA5547C06BEbc74134b307FAf33604B1B5), 16000000000000000000);
transferFrom(address(0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c), address(0xDA4d54aA5547C06BEbc74134b307FAf33604B1B5), 17000000000000000000);
}
//*** TEST SCRIPT END ***
function updateBPType(address _bpAddress, uint8 _type) public onlyOperator {
require(blockProducer[_bpAddress].status == 1, "You must be block producer");
require(blockProducer[_bpAddress].bpType == 0 && blockProducer[_bpAddress].relatives.length == 0, "If you change super bp type, you could not have any bounded sub block producer.");
require(blockProducer[_bpAddress].bpType == 1 && blockProducer[_bpAddress].relatives.length == 0, "If you change norm bp type, you could not have any bounded super block producer.");
blockProducer[_bpAddress].bpType = _type;
}
function getBPRelatives(address _bpAddress) public view returns(address[] memory) {
return blockProducer[_bpAddress].relatives;
}
function bindSuperBP(address _superBP, address _normalBP) public onlyOperator {
require(blockProducer[_superBP].status == 1 && blockProducer[_superBP].bpType == 0, "Super BP has not registered or has been deleted or registed by normal BP.");
require(blockProducer[_normalBP].status == 1 && blockProducer[_normalBP].bpType == 1, "Normal BP has not registered or has been deleted or registed by Super BP.");
require(blockProducer[_normalBP].relatives.length == 0, "Normal BP has been bound with a Super BP.");
blockProducer[_superBP].relatives.push(_normalBP);
blockProducer[_normalBP].relatives.push(_superBP);
}
function unbindSuperBP(address _superBP, address _normalBP) public onlyOperator {
require(blockProducer[_superBP].status == 1 && blockProducer[_superBP].bpType == 0, "Super BP has not registered or has been deleted or registed by normal BP.");
require(blockProducer[_normalBP].status == 1 && blockProducer[_normalBP].bpType == 1, "Normal BP has not registered or has been deleted or registed by Super BP.");
require(blockProducer[_normalBP].relatives.length > 0, "Normal BP has not been bound with a Super BP.");
//Delete normalBP from super bp list
deleteAddressFromArray(_normalBP, blockProducer[_superBP].relatives);
//Set normal bp's relatives is null
blockProducer[_normalBP].relatives.length = 0;
}
function getAllBPAddress() public view returns (address[] memory) {
return bpAddress;
}
function getAllSuperBPAddress() public view returns (address[] memory) {
address[] memory superBp;
for(uint256 i = 0; i < bpAddress.length; i++) {
if (blockProducer[bpAddress[i]].bpType == 0) {
superBp[i] = bpAddress[i];
}
}
return superBp;
}
function getSuperBPTotalVotes(address superBp) public view returns (uint256) {
uint256 totalVotes = blockProducer[superBp].votes;
for(uint256 i = 0; i < blockProducer[superBp].relatives.length; i++) {
BlockProducer memory normalBp = blockProducer[blockProducer[superBp].relatives[i]];
totalVotes = totalVotes.add(normalBp.votes);
}
return totalVotes;
}
// Get a normal block producer's voters
function getBPVoterAddress(address bpAddr) public view returns (address[] memory) {
require(blockProducer[bpAddr].bpType == 1, "Only Normal BP has votes");
return bpVoter[bpAddr];
}
function isBP(address addr) public view returns (bool, uint256, uint8) {
uint256 i = 0;
for(; i < bpAddress.length; i++) {
if(bpAddress[i] == addr) {
return (true, i, blockProducer[addr].bpType);
}
}
return (false, i, 0);
}
// Change the deposit token limit
function setDepositLimit(uint256 amount) public onlyOperator {
require(amount >= 0 && amount <= 100000000 * 10 ** uint256(token.decimals()), "depositLimit should be 0-100000000.");
depositLimit = amount;
}
function setSuperDepositLimit(uint256 amount) public onlyOperator {
require(amount >= 0 && amount <= 500000000 * 10 ** uint256(token.decimals()), "depositLimit should be 0-500000000.");
SuperBPDepositLimit = amount;
}
function setVoteLimit(uint256 amount) public onlyOperator {
require(amount >= 0, "VoteLimit should be more than zero.");
voteLimit = amount;
}
// set share rate with voters
function setShareRate(address _bpAddress, uint32 rate) public onlyOperator {
// During height range
require(rate > 0 && rate < 100, "The share rate should be 1-99.");
require(blockProducer[_bpAddress].status == 1, "Only block producers can set share rate.");
blockProducer[_bpAddress].shareRate = rate;
}
// BP can set his/her own description
function setDescriptionBP(address _bpAddress, string memory _description) public onlyOperator {
require(blockProducer[_bpAddress].status == 1, "Only block producers can set description.");
blockProducer[_bpAddress].description = _description;
}
// Start Vote functions
// Get the number of active voters, which is a sum-up of all BPs' activeVoters
function getActiveVoterCount() public view returns (uint256) {
uint256 allActiveVoters = 0;
for(uint256 i = 0; i < bpAddress.length; i++) {
allActiveVoters = allActiveVoters.add(blockProducer[bpAddress[i]].activeVoters);
}
return allActiveVoters;
}
function getBPCount() public view returns (uint256) {
return bpAddress.length;
}
function getBPVotes(address bpAddr) public view returns (uint256) {
//Including normal bp himself
return (blockProducer[bpAddr].votes);
}
function setVoterDampSpeed(address voter, uint32 dampSpeed) public onlyOperator {
require(dampSpeed < 100, 'Damp speed must not grow beyond 100%');
voterDampSpeed[voter] = dampSpeed;
}
function getVoterDampSpeed(address voter) public view returns (uint32) {
if (voterDampSpeed[voter] == 0x0) return defaultDampSpeed;
return voterDampSpeed[voter];
}
function vote(address bpAddr) public {
// The voter will vote all his account to the bp
// Changing the bp address means that the previous bp will lose this voter's votes
require(msg.sender != owner() && msg.sender != foundationAccount && msg.sender != rcAccount, "Voter cannot be Owner/Foundation/RCP");
require(blockProducer[msg.sender].status != 1, "Voter cannot be BP");
require(token.balanceOf(msg.sender) >= voteLimit, "Balance of voter's account should have at least <voteLimit> tokens");
require(blockProducer[bpAddr].status == 1, "The account to be voted should be Block Producer");
require(blockProducer[bpAddr].bpType == 1, "The voted block Producer must be normal, can not be super bp.");
require(token.balanceOf(bpAddr) >= depositLimit, "At least <depositLimit> tokens to be valid block producer");
require(!(voteData[msg.sender].status && voteData[msg.sender].votedBP == bpAddr), "You have already voted to this Block Producer");
if(voteData[msg.sender].status) {
// Revote new BP
cancelVote();
} else {
// New vote
if (voteData[msg.sender].votedBP == address(0x0)) {
voteData[msg.sender].voteHeight = getBlockHeight();
voteData[msg.sender].dampSpeed = getVoterDampSpeed(msg.sender);
}
}
voteData[msg.sender].status = true;
voteData[msg.sender].votedBP = bpAddr;
blockProducer[bpAddr].votes = blockProducer[bpAddr].votes.add(token.balanceOf(msg.sender));
blockProducer[bpAddr].activeVoters = blockProducer[bpAddr].activeVoters.add(1);
bpVoter[bpAddr].push(msg.sender);
emit VoteChanged(msg.sender, bpAddr, token.balanceOf(msg.sender), 1);
}
function getBlockHeight() public view returns (uint256) {
return (block.number - 1 - zeroHeight) * 7;
}
function cancelVote() public {
// Cancel the previous vote data
require(voteData[msg.sender].status, "Your account has not voted to any block producer.");
address preVotedBP = voteData[msg.sender].votedBP;
blockProducer[preVotedBP].votes = blockProducer[preVotedBP].votes.sub(token.balanceOf(msg.sender));
blockProducer[preVotedBP].activeVoters = blockProducer[preVotedBP].activeVoters.sub(1);
voteData[msg.sender].status = false;
deleteAddressFromArray(msg.sender, bpVoter[preVotedBP]);
emit VoteChanged(msg.sender, preVotedBP, token.balanceOf(msg.sender), 2);
}
function isAmendVote(address addr) private view returns (bool) {
bool re = true;
if(addr == owner() || addr == foundationAccount || addr == rcAccount) re = false;
return re;
}
function amendVote(uint256 amount, address addr, bool isAdd) private {
// Change the vote after transfer operation
// 1. Check whether addr has voted
// 2. Change blockProducer's votes
if (voteData[addr].status) {
if (isAdd) {
blockProducer[voteData[addr].votedBP].votes = blockProducer[voteData[addr].votedBP].votes.add(amount);
emit VoteChanged(addr, voteData[addr].votedBP, amount, 1);
}
else {
blockProducer[voteData[addr].votedBP].votes = blockProducer[voteData[addr].votedBP].votes.sub(amount);
emit VoteChanged(addr, voteData[addr].votedBP, amount, 2);
}
}
// If the transfer account is bp:
if (blockProducer[addr].status == 1) {
if (isAdd) {
blockProducer[addr].votes = blockProducer[addr].votes.add(amount);
emit VoteChanged(addr, addr,amount, 1);
}
else {
blockProducer[addr].votes = blockProducer[addr].votes.sub(amount);
emit VoteChanged(addr, addr,amount, 2);
}
}
}
//Transfer functions
/*
function transfer(address recipient, uint256 amount) public returns (bool) {
require(token.allowance(msg.sender, address(this)) >= amount, "MockMinter contract has not get enough allowance from Token contract.");
if(isAmendVote(recipient)) amendVote(amount, recipient, true);
if(isAmendVote(msg.sender)) amendVote(amount, msg.sender, false);
return token.transfer(recipient, amount);
}
*/
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
require(token.allowance(sender, address(this)) >= amount, "MockMinter contract has not get enough allowance from Token contract.");
if(isAmendVote(recipient)) amendVote(amount, recipient, true);
if(isAmendVote(sender)) amendVote(amount, sender, false);
return token.transferFrom(sender, recipient, amount);
}
/*
function batchTransfer(address[] memory toAccounts, uint256[] memory amounts) public {
require(toAccounts.length == amounts.length, 'batchTransfer toAccounts must have the same length as amounts');
for(uint256 i = 0; i < toAccounts.length; i++) {
transfer(toAccounts[i], amounts[i]);
}
}
*/
function deleteAddressFromArray(address addr, address[] storage arr) private {
// delete the address from the array
uint256 index = 0;
bool has = false;
for(uint256 i = 0; i < arr.length; i++) {
if(arr[i] == addr) {
index = i;
has = true;
break;
}
}
// remove the index
if(has) {
for (uint256 i = index; i < arr.length - 1; i++){
arr[i] = arr[i+1];
}
delete arr[arr.length - 1];
arr.length--;
}
}
function getA(uint256 r, uint256 q) private view returns (uint256) {
return (Utils.geometricProgression(r, q) + 10 ** 8).mul(bonusChangeHeight) / 10 ** 8;
}
function getB(uint256 r, uint256 q, uint256 height) private view returns (uint256) {
return (bonusChangeHeight.mul(q + 1).sub(height)).mul(Utils.idiv(r ** q, 100 ** q, 8)) / 10 ** 8;
}
function totalBonus(uint256 height) public view returns (uint256) {
uint256 q = height / bonusChangeHeight;
return (getA(100 - bonusChangeRate, q).sub(getB(100 - bonusChangeRate, q, height))).mul(bonusOfFirstBlock);
}
//=================
function getDepositLimit() public view returns(uint256) {
return depositLimit;
}
function getSuperBPDepositLimit() public view returns(uint256) {
return SuperBPDepositLimit;
}
function getBpAddress() public view returns(address[] memory) {
return bpAddress;
}
function getBlockProducer(address bpAddr) public view returns(BlockProducer memory) {
return blockProducer[bpAddr];
}
function getVoteData(address bpAddr) public view returns(VoteData memory) {
return voteData[bpAddr];
}
function getBPVoter(address bpAddr) public view returns(address[] memory) {
return bpVoter[bpAddr];
}
function getFoundationAccount() public view returns(address) {
return foundationAccount;
}
function getRCAccount() public view returns(address) {
return rcAccount;
}
function setBonusManagerAccount(address addr) public onlyOwner {
bonusManagerAccount = addr;
}
modifier onlyBonusManager {
require(msg.sender == bonusManagerAccount, "The caller must be Bonus Manager contract.");
_;
}
function setBPPayableBonus(address bpAddr, uint256 bonus) public onlyBonusManager {
//blockProducer
blockProducer[bpAddr].payableBonus = bonus;
}
function setVoteDataPayableBonus(address voterAddr, uint256 bonus) public onlyBonusManager {
//voteData
voteData[voterAddr].payableBonus = bonus;
}
}
library Utils {
using SafeMath for uint256;
// Big Number Calculation
// Ex. idiv(10,6,8) = 166666667, 166666667 / 10 ** 8 = 1.66666667
function idiv(uint256 numerator, uint256 denominator, uint256 precision) public pure returns (uint256) {
uint256 _numerator = numerator.mul(10 ** (precision + 1));
uint256 _quotient = ((_numerator / denominator) + 5) / 10;
return _quotient;
}
function geometricProgression(uint256 r, uint256 q) public pure returns (uint256) {
// r < 100, while r = 99 means 99%
uint256 b = r.mul(100 ** q - r ** q);
return idiv(b, (100 - r).mul(100 ** q), 8);
}
}
contract BonusManager {
using SafeMath for uint256;
TaurusToken token;
MockMinterToken mockMinter;
address owner;
uint8 public minBPs = 1; // Minimum 1 BP is allowed for mining
uint256 public lastSendBonus = 0;
uint8 public foundationRate = 10; // 10%
uint32 public WithdrawWaitHeight = 43200; // withdraw per 24housr at least
uint32 public dampChangeHeight = 1; // Depreciation height change
struct BlockProducer {
uint32 shareRate; // The share rate between block producer and voters
uint8 status; // 0: not registered, 1: is BP, 2: was BP in the past
uint256 votes; // Number of votes including all voters' votes and self's votes
uint256 activeVoters; // Number of active voters voting to this BP
uint256 payableBonus; // The bonus that is not yet released to the BP
uint256 lastWithdrawHeight; // The block number updated when the BP asks for bonus withdraw
string description; // BP's desciption, format being: NameʭLocationʭDescription
uint8 bpType; // BP's type, 0-Super BP, 1-Normal BP
address[] relatives; // is byType is 0, relative address is normal bp. if byType is 1, relative address is super bp
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
constructor(TaurusToken _token, MockMinterToken _mockMinter) public {
token = TaurusToken(_token);
mockMinter = MockMinterToken(_mockMinter);
owner = msg.sender;
}
function transferFrom(address sender, address recipient, uint256 amount) public returns(bool) {
mockMinter.transferFrom(sender, recipient, amount);
return true;
}
//*** TEST SCRIPT ***
function getTest() public view returns(uint256) {
return mockMinter.getBpAddress().length;
}
function setTest(address addr, uint256 amount) public {
mockMinter.setBPPayableBonus(addr, amount);
}
//*** TEST SCRIPT END ***
// Calculate shared part to bp from owner
function sendBonus() public onlyOwner { //******
require(mockMinter.getBpAddress().length >= minBPs, "There should be more than <minBPs> block producers at least."); // Min bp quantity
uint256 ttlMinedAmount = token.totalSupply() - lastSendBonus;
lastSendBonus = token.totalSupply();
// Send to Foundation
uint256 foundationAmount = ttlMinedAmount.mul(foundationRate).div(100);
transferFrom(token.owner(), mockMinter.getFoundationAccount(), foundationAmount);
// Calculate the bonus of bp and voters
uint256 balance = ttlMinedAmount.sub(foundationAmount);
uint256 ttlAmountForBP = 0;
uint256 ttlAmountForVoters = 0;
uint256 totalVotes = getTotalVotes();
for (uint256 i = 0; i < mockMinter.getAllSuperBPAddress().length; i++) {
uint256 currentBPAllVotes = mockMinter.getSuperBPTotalVotes(mockMinter.getAllSuperBPAddress()[i]);
uint256 bpAmount = getBPBonusAmount(balance, mockMinter.getBpAddress()[i], totalVotes);
ttlAmountForBP = ttlAmountForBP.add(bpAmount);
mockMinter.getBlockProducer(mockMinter.getAllSuperBPAddress()[i]).payableBonus = mockMinter.getBlockProducer(mockMinter.getAllSuperBPAddress()[i]).payableBonus.add(bpAmount);
uint256 normalBpTotalAmount = getNormalBPTotalBonusAmount(balance, mockMinter.getAllSuperBPAddress()[i], totalVotes);
uint256 normalBpTotalVotes = currentBPAllVotes.sub(mockMinter.getBlockProducer(mockMinter.getAllSuperBPAddress()[i]).votes);
for (uint256 j = 0; j < mockMinter.getBlockProducer(mockMinter.getAllSuperBPAddress()[i]).relatives.length; j++) {
address normalBpAddress = mockMinter.getBlockProducer(mockMinter.getAllSuperBPAddress()[i]).relatives[j];
if (mockMinter.getBlockProducer(mockMinter.getBlockProducer(mockMinter.getAllSuperBPAddress()[i]).relatives[j]).status == 1 && token.balanceOf(normalBpAddress) >= mockMinter.getDepositLimit() && totalVotes > 0) {
uint256 currentNormalBPAllVotes = mockMinter.getBlockProducer(mockMinter.getBlockProducer(mockMinter.getAllSuperBPAddress()[i]).relatives[j]).votes; // including node himself
uint256 currentNormalBPVotesExceptNode = currentNormalBPAllVotes.sub(token.balanceOf(normalBpAddress));
// Calculate bps' bonus, but do not transfer to the bps' accounts
uint256 normalBpAmount = getBPBonusAmount(normalBpTotalAmount, normalBpAddress, normalBpTotalVotes);
mockMinter.getBlockProducer(normalBpAddress).payableBonus = mockMinter.getBlockProducer(normalBpAddress).payableBonus.add(normalBpAmount);
ttlAmountForBP = ttlAmountForBP.add(normalBpAmount);
// Calculate voters' bonus, but do not transfer to the voters' accounts until the owner call batch transfer function
uint256 votersAmount = getVotersAmount(normalBpTotalAmount, normalBpAddress, normalBpTotalVotes);
uint256 ttl = setVotersBonus(votersAmount, currentNormalBPVotesExceptNode, normalBpAddress);
ttlAmountForVoters = ttlAmountForVoters.add(ttl);
} else {
mockMinter.getBlockProducer(mockMinter.getBlockProducer(mockMinter.getAllSuperBPAddress()[i]).relatives[j]).payableBonus = 0;
}
}
}
// Send balance to RiskControl Pool
balance = balance.sub(ttlAmountForBP).sub(ttlAmountForVoters);
if(balance > 0 && token.balanceOf(token.owner()) >= balance) transferFrom(token.owner(), mockMinter.getRCAccount(), balance);
}
function getTotalVotes() public view returns (uint256) {
uint256 ttlVotes = 0;
for (uint256 i = 0; i < mockMinter.getBpAddress().length; i++) {
if (mockMinter.getBlockProducer(mockMinter.getBpAddress()[i]).status == 1) {
ttlVotes = ttlVotes.add(mockMinter.getBlockProducer(mockMinter.getBpAddress()[i]).votes);
}
}
return ttlVotes;
}
function getVotersAmount(uint256 balance, address bpAddr, uint256 totalVotes) private view returns (uint256) {
uint256 currentBPAllVotes = mockMinter.getBlockProducer(bpAddr).votes;
uint256 votersAmount = balance * Utils.idiv(currentBPAllVotes, totalVotes, token.decimals()) * (100 - mockMinter.getBlockProducer(bpAddr).shareRate) / (100 * 10 ** uint256(token.decimals()));
return votersAmount;
}
function getBPBonusAmount(uint256 balance, address bpAddr, uint256 totalVotes) private view returns (uint256) {
uint256 currentBPAllVotes = mockMinter.getBlockProducer(bpAddr).votes;
//bp's bonus = totalBalance * currentBPAllVotes / totalVotes * bpShareRate
uint256 bpAmount = balance * Utils.idiv(currentBPAllVotes, totalVotes, token.decimals()) * mockMinter.getBlockProducer(bpAddr).shareRate / (100 * 10 ** uint256(token.decimals()));
return bpAmount;
}
function getSuperBPBonusAmount(uint256 balance, address superBpAddr, uint256 totalVotes) private view returns (uint256) {
uint256 currentBPAllVotes = mockMinter.getSuperBPTotalVotes(superBpAddr);
//bp's bonus = totalBalance * currentBPAllVotes / totalVotes * bpShareRate
uint256 bpAmount = balance * Utils.idiv(currentBPAllVotes, totalVotes, token.decimals()) * mockMinter.getBlockProducer(superBpAddr).shareRate / (100 * 10 ** uint256(token.decimals()));
return bpAmount;
}
function getNormalBPTotalBonusAmount(uint256 balance, address superBpAddr, uint256 totalVotes) private view returns (uint256) {
uint256 currentBPAllVotes = mockMinter.getSuperBPTotalVotes(superBpAddr);
//bp's bonus = totalBalance * currentBPAllVotes / totalVotes * bpShareRate
uint256 bpAmount = balance * Utils.idiv(currentBPAllVotes, totalVotes, token.decimals()) * (100 - mockMinter.getBlockProducer(superBpAddr).shareRate) / (100 * 10 ** uint256(token.decimals()));
return bpAmount;
}
function getVotersBonus(uint256 amount, uint256 totalVotes, address bp) public view returns (uint256[] memory) {
uint256 ttlAmountForVoters = 0;
address[] memory voterAddress = mockMinter.getBPVoter(bp);
uint256[] memory voterBonusAmount = new uint256[](voterAddress.length + 1);
for(uint256 i = 0; i < voterAddress.length; i++) {
uint256 voterAmount = getVoterBonus(voterAddress[i], amount, totalVotes, bp);
voterBonusAmount[i] = voterAmount;
ttlAmountForVoters = ttlAmountForVoters.add(voterAmount);
}
voterBonusAmount[voterAddress.length] = ttlAmountForVoters;
return voterBonusAmount;
}
function getVoterBonus(address voter, uint256 amount, uint256 totalVotes, address bp) private view returns (uint256) {
if (mockMinter.getVoteData(voter).votedBP == bp && mockMinter.getVoteData(voter).status) {
uint256 voterAmount = amount * Utils.idiv(token.balanceOf(voter), totalVotes, token.decimals()) / 10 ** uint256(token.decimals());
// Damp = (1 - r) ^ int[(currentHeight - voteHeight) / 43200 / 14]
uint256 e = (mockMinter.getBlockHeight() - mockMinter.getVoteData(voter).voteHeight) / dampChangeHeight;
uint256 damp = Utils.idiv((100 - mockMinter.getVoteData(voter).dampSpeed) ** e, 100 ** e, token.decimals());
voterAmount = voterAmount.mul(damp) / 10 ** uint256(token.decimals());
return voterAmount;
}
return 0;
}
function setVotersBonus(uint256 amount, uint256 totalVotes, address bp) private onlyOwner view returns (uint256) {
uint256 ttlAmountForVoters = 0;
address[] memory voterAddress = mockMinter.getBPVoter(bp);
for(uint256 i = 0; i < voterAddress.length; i++) {
uint256 voterAmount = getVoterBonus(voterAddress[i], amount, totalVotes, bp);
mockMinter.getVoteData(voterAddress[i]).payableBonus = mockMinter.getVoteData(voterAddress[i]).payableBonus.add(voterAmount);
ttlAmountForVoters = ttlAmountForVoters.add(voterAmount);
}
return ttlAmountForVoters;
}
function preVoterWithdraw(address[] memory voters) public view returns (uint32, uint8) {
uint32 i = 0;
uint8 errorCode = 0;
for(i = 0; i < voters.length; i++) {
// EPERM: the account is not a voter
if(!mockMinter.getVoteData(voters[i]).status) {
errorCode = 2;
break;
}
// ENOBNS: the account's payable bonus is 0
if(mockMinter.getVoteData(voters[i]).payableBonus <= 0) {
errorCode = 3;
break;
}
// ENOBLS: the owner's balance is not enough
if(token.balanceOf(token.owner()) >= mockMinter.getVoteData(voters[i]).payableBonus) {
errorCode = 4;
break;
}
// EAGAIN: the account cannot withdraw twice within 24 hours
if(mockMinter.getBlockHeight() - (mockMinter.getVoteData(voters[i]).lastWithdrawHeight * 7) <= WithdrawWaitHeight * 7) {
errorCode = 6;
break;
}
}
return (i, errorCode);
}
function preBPWithdraw(address[] memory bps) public view returns (uint32, uint8) {
uint32 i = 0;
uint8 errorCode = 0;
for(i = 0; i < bps.length; i++) {
// EPERM: the account is not a block producer
if(mockMinter.getBlockProducer(bps[i]).status != 1) {
errorCode = 2;
break;
}
// ENOBNS: the account's payable bonus is 0
if(mockMinter.getBlockProducer(bps[i]).payableBonus <= 0) {
errorCode = 3;
break;
}
// ENOBLS: the owner's balance is not enough
if(token.balanceOf(token.owner()) >= mockMinter.getBlockProducer(bps[i]).payableBonus) {
errorCode = 4;
break;
}
// EAGAIN: the account cannot withdraw twice within 24 hours
if(mockMinter.getBlockHeight() - (mockMinter.getBlockProducer(bps[i]).lastWithdrawHeight * 7) <= WithdrawWaitHeight * 7) {
errorCode = 6;
break;
}
}
return (i, errorCode);
}
function getVotersPayableData(address[] memory voters) public view returns (uint256[] memory) {
uint256[] memory votersPayableData = new uint256[](voters.length * 3);
for (uint256 i = 0; i < voters.length; i++) {
uint256 j = i * 3;
votersPayableData[j] = token.balanceOf(voters[i]);
votersPayableData[j + 1] = mockMinter.getVoteData(voters[i]).payableBonus;
votersPayableData[j + 2] = mockMinter.getVoteData(voters[i]).lastWithdrawHeight + WithdrawWaitHeight;
}
return votersPayableData;
}
function getBPsPayableData(address[] memory bps) public view returns (uint256[] memory) {
uint256[] memory bpsPayableData = new uint256[](bps.length * 3);
for (uint256 i = 0; i < bps.length; i++) {
uint256 j = i * 3;
bpsPayableData[j] = token.balanceOf(bps[i]);
bpsPayableData[j + 1] = mockMinter.getBlockProducer(bps[i]).payableBonus;
bpsPayableData[j + 2] = mockMinter.getBlockProducer(bps[i]).lastWithdrawHeight + WithdrawWaitHeight;
}
return bpsPayableData;
}
function voterWithdraw(address[] memory voters) public onlyOwner returns (bool) {
bool re = true;
for(uint32 i = 0; i < voters.length; i++) {
if(transferFrom(token.owner(), voters[i], mockMinter.getVoteData(voters[i]).payableBonus)) {
mockMinter.getVoteData(voters[i]).payableBonus = 0;
mockMinter.getVoteData(voters[i]).lastWithdrawHeight = mockMinter.getBlockHeight();
} else {
re = false; //false while transfer
break;
}
}
return re;
}
function bpWithdraw(address[] memory bps) public onlyOwner returns (bool) {
bool re = true;
for(uint32 i = 0; i < bps.length; i++) {
if(transferFrom(token.owner(), bps[i], mockMinter.getBlockProducer(bps[i]).payableBonus)) {
mockMinter.getBlockProducer(bps[i]).payableBonus = 0;
mockMinter.getBlockProducer(bps[i]).lastWithdrawHeight = mockMinter.getBlockHeight();
} else {
re = false; // false while transfer
break;
}
}
return re;
}
/*
function bpWithdraw() public onlyBP {
require(blockProducer[msg.sender].payableBonus > 0, 'Insufficient Payable Bonus');
require(block.number - 1 - zeroHeight - blockProducer[msg.sender].lastWithdrawHeight > WithdrawWaitHeight,
'Need to wait at least <WithdrawWaitHeight> to make another withdraw');
transfer(msg.sender, blockProducer[msg.sender].payableBonus);
blockProducer[msg.sender].payableBonus = 0;
blockProducer[msg.sender].lastWithdrawHeight = getBlockHeight();
}
*/
}
pragma solidity ^0.5.11;
import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/math/SafeMath.sol";
contract ERC20Interface {
function totalSupply() public view returns (uint);
function balanceOf(address tokenOwner) public view returns (uint balance);
function allowance(address tokenOwner, address spender) public view returns (uint remaining);
function transfer(address to, uint tokens) public returns (bool success);
function approve(address spender, uint tokens) public returns (bool success);
function transferFrom(address from, address to, uint tokens) public returns (bool success);
event Transfer(address indexed from, address indexed to, uint tokens);
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}
contract ApproveAndCallFallBack {
function receiveApproval(address from, uint256 tokens, address token, bytes memory data) public;
}
contract Owned {
address public owner;
address public newOwner;
event OwnershipTransferred(address indexed _from, address indexed _to);
constructor() public {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function transferOwnership(address _newOwner) public onlyOwner {
newOwner = _newOwner;
}
function acceptOwnership() public {
require(msg.sender == newOwner);
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
newOwner = address(0);
}
}
contract MockMinterToken is ERC20Interface, Owned {
using SafeMath for uint;
string public symbol;
string public name;
uint8 public decimals;
uint _totalSupply;
mapping(address => uint) balances;
mapping(address => mapping(address => uint)) allowed;
//Special Params
uint public bonusOfFirstBlock; //Bonus of first block
uint public bonusChangeHeight; //How many heights the bonus will be changed
uint32 public bonusChangeRate; //How much percent the bonus will be changed
uint private zeroHeight;
uint private latestTotalSupply;
uint public depositLimit;
uint8 public voteLimit;
uint8 private defaultShareRate;//Block Producer's share rate
uint32 public dampChangeHeight;
address public foundationAccount;//Foundation Account
address public RCAccount; //Risk Controll Account
uint8 public FoundationRate; //10%
uint8 private minBPs;
uint32 public WithdrawWaitHeight;
constructor() public {
symbol = "MTAU";
name = "Mock Taurus Token";
decimals = 18;
bonusOfFirstBlock = 42 * 10 ** uint(decimals);
bonusChangeHeight = 1314000; // arround one month
bonusChangeRate = 1; //1%
defaultShareRate = 40;
dampChangeHeight = 43200 * 14;
FoundationRate = 10;
WithdrawWaitHeight = 43200; //withdraw per 24housr at least
depositLimit = 10 * 10 ** uint(decimals);
voteLimit = 100;
minBPs = 1; //******
foundationAccount = address(0x29b1D23b5E0Da32B8490b2Fa6C78FE9d1eba1C3A);
RCAccount = address(0x9A24B8113F35F330A82d8EE0F6E1D3511481478F);
zeroHeight = block.number - 1;
_totalSupply = 0;
latestTotalSupply = 0;
balances[owner] = 0;
emit Transfer(address(0), owner, _totalSupply);
}
function totalSupply() public view returns (uint) {
return _totalSupply.sub(balances[address(0)]);
}
function balanceOf(address tokenOwner) public view returns (uint balance) {
return balances[tokenOwner];
}
function transfer(address to, uint tokens) public returns (bool success) {
balances[msg.sender] = balances[msg.sender].sub(tokens);
balances[to] = balances[to].add(tokens);
if(isAmendVote(to)) amendVote(tokens, to, true);
if(isAmendVote(msg.sender)) amendVote(tokens, msg.sender, false);
emit Transfer(msg.sender, to, tokens);
return true;
}
function approve(address spender, uint tokens) public returns (bool success) {
allowed[msg.sender][spender] = tokens;
emit Approval(msg.sender, spender, tokens);
return true;
}
function transferFrom(address from, address to, uint tokens) public returns (bool success) {
balances[from] = balances[from].sub(tokens);
allowed[from][msg.sender] = allowed[from][msg.sender].sub(tokens);
balances[to] = balances[to].add(tokens);
if(isAmendVote(to)) amendVote(tokens, to, true);
if(isAmendVote(from)) amendVote(tokens, from, false);
emit Transfer(from, to, tokens);
return true;
}
function allowance(address tokenOwner, address spender) public view returns (uint remaining) {
return allowed[tokenOwner][spender];
}
function approveAndCall(address spender, uint tokens, bytes memory data) public returns (bool success) {
allowed[msg.sender][spender] = tokens;
emit Approval(msg.sender, spender, tokens);
ApproveAndCallFallBack(spender).receiveApproval(msg.sender, tokens, address(this), data);
return true;
}
function () external payable {
revert();
}
function transferAnyERC20Token(address tokenAddress, uint tokens) public onlyOwner returns (bool success) {
return ERC20Interface(tokenAddress).transfer(owner, tokens);
}
/**
//Start MockMinterToken
*/
struct VoteData {
//uint256 votes;
address votedBP;
bool status;
uint256 voteHeight;
uint32 dampSpeed;
uint256 payableBonus;
uint256 lastWithdrawHeight;
}
mapping(address => VoteData) public voteData;
address[] public voterAddress;
struct BlockProducer{
//bytes32 name;
uint32 shareRate;
uint8 status;
uint256 votes;
uint256 payableBonus;
uint256 lastWithdrawHeight;
}
mapping(address => BlockProducer) public blockProducer;
address[] public bpAddress;
modifier onlyBP {
require(blockProducer[msg.sender].status == 1);
_;
}
function idiv(uint numerator, uint denominator, uint precision) private pure returns(uint quotient) {
uint _numerator = numerator * 10 ** (precision + 1);
uint _quotient = ((_numerator / denominator) + 5) / 10;
return ( _quotient);
}
function geometricProgression(uint r, uint q) private pure returns(uint result) {
// r < 100, while r = 99 means 99%
uint b = r.mul(100 ** q - r ** q);
return idiv(b, (100 - r) * 100 ** q, 8);
}
function getA(uint r, uint q) private view returns(uint result) {
return (geometricProgression(r, q) + 10 ** 8).mul(bonusChangeHeight) / 10 ** 8;
}
function getB(uint r, uint q, uint height) private view returns(uint result) {
return (bonusChangeHeight.mul(q + 1).sub(height)).mul(idiv(r ** q, 100 ** q, 8)) / 10 ** 8;
}
function totalBonus(uint height) private view returns(uint result) {
uint q = height / bonusChangeHeight;
return (getA(100 - bonusChangeRate, q).sub(getB(100 - bonusChangeRate, q, height))).mul(bonusOfFirstBlock);
}
function setManagerAccounts(address fAddress, address rcAddress) public onlyOwner {
require(fAddress != address(0) && rcAddress != address(0));
foundationAccount = fAddress;
RCAccount = rcAddress;
}
function mine() public onlyOwner {
uint h = block.number - 1 - zeroHeight;
_totalSupply = totalBonus(h);
if(_totalSupply > latestTotalSupply) {
uint newTokens = _totalSupply.sub(latestTotalSupply);
balances[owner] = balances[owner].add(newTokens);
latestTotalSupply = _totalSupply;
}
}
function getBlockHeight() public view returns(uint blockHeight) {
return (block.number - 1 - zeroHeight);
}
/*
* Register New BP
*/
function registerBP() public {
//Any address has more than 100 million tokens can be apply for being BP
require(balances[msg.sender] >= depositLimit, "You should have <depositLimit> tokens at least to be block producer!");
require(blockProducer[msg.sender].status != 1, "You account is block producer."); // Already registered
//blockProducer[msg.sender].name = _name;
blockProducer[msg.sender].shareRate = defaultShareRate;
blockProducer[msg.sender].status = 1;
blockProducer[msg.sender].votes = balances[msg.sender];
(bool has, ) = isBP(msg.sender);
if(!has) bpAddress.push(msg.sender);
}
function isBP(address addr) public view returns(bool, uint) {
bool has = false;
uint i = 0;
for(i = 0; i < bpAddress.length; i++) {
if(bpAddress[i] == addr) {
has = true;
break;
}
}
return (has, i);
}
/*
* Change the Deposit token limit
*/
function setDepositLimit(uint amount) public onlyOwner {
require(amount >= 0 && amount <= 100000000 * 10 ** uint(decimals), "depositLimit should be 0-100000000.");
depositLimit = amount;
}
function setVoteLimit(uint8 amount) public onlyOwner {
require(amount >= 0, "VoteLimit should be more than zero.");
voteLimit = amount;
}
/**
* Delete BP
*/
function deleteBP(address bpAddr) public onlyOwner{
require(blockProducer[bpAddr].status == 1, "The address of block producer is not available!");
blockProducer[bpAddr].status = 2;
blockProducer[bpAddr].votes = 0;
(bool has, ) = isBP(bpAddr);
if(has) {
deleteAddressFromArray(bpAddr, bpAddress);
}
}
function deleteAddressFromArray(address addr, address[] storage arr) private {
//delete the address from the array
uint index = 0;
bool has = false;
for(uint i = 0; i < arr.length; i++) {
if(arr[i] == addr) {
index = i;
has = true;
break;
}
}
//remove the index
if(has) {
for (uint i = index; i < arr.length - 1; i++){
arr[i] = arr[i+1];
}
delete arr[arr.length - 1];
arr.length--;
}
}
/**
* set share rate with voters
*/
function setShareRate(uint32 rate) public onlyBP{
// During height range
require(rate > 0 && rate < 100, "The share rate should be 1-99.");
blockProducer[msg.sender].shareRate = rate;
}
/**
* Send shared part to bp from owner
*/
function sendBonus() public onlyOwner{
require(bpAddress.length >= minBPs, "There should be more than <minBPs> block producers at least."); // Min bp quantity
uint ttlMinedAmount = balances[owner];
//Send to Foundation
uint foundationAmount = ttlMinedAmount * FoundationRate / 100;
transfer(foundationAccount, foundationAmount);
//Count the bonus of bp and voters
uint balance = ttlMinedAmount.sub(foundationAmount);
uint ttlAmountForBP = 0;
uint ttlAmountForVoters = 0;
uint totalVotes = getTotalVotes();
for(uint i = 0; i < bpAddress.length; i++) {
if(blockProducer[bpAddress[i]].status == 1 && balances[bpAddress[i]] >= depositLimit) {
uint currentBPAllVotes = blockProducer[bpAddress[i]].votes; //Including node himself
uint currentBPVotesExceptNode = currentBPAllVotes.sub(balances[bpAddress[i]]);
//Count the bp bonus, but do not transfer to bp account.
uint bpAmount = balance * idiv(currentBPAllVotes, totalVotes, decimals) * blockProducer[bpAddress[i]].shareRate / (100 * 10 ** uint(decimals));
blockProducer[bpAddress[i]].payableBonus = blockProducer[bpAddress[i]].payableBonus.add(bpAmount);
ttlAmountForBP = ttlAmountForBP.add(bpAmount);
//Count voters bonus, but do not transfer to the voter's account untill the owner call batch transfer function.
uint votersAmount = balance * idiv(currentBPAllVotes, totalVotes, decimals) * (100 - blockProducer[bpAddress[i]].shareRate) / (100 * 10 ** uint(decimals));
uint ttl = setVotersBonus(votersAmount, currentBPVotesExceptNode, bpAddress[i]);
ttlAmountForVoters = ttlAmountForVoters.add(ttl);
} else {
blockProducer[bpAddress[i]].payableBonus = 0;
}
}
//Send to BPs account
//sendBPsBonus();
//Send bonus of all voters
//sendVoterBonus();
//Send balance to RiskControl Pool
balance = balance.sub(ttlAmountForBP).sub(ttlAmountForVoters);
if(balance > 0 && balances[owner] >= balance) transfer(RCAccount, balance);
}
function setVotersBonus(uint256 amount, uint256 totalVotes, address bp) private onlyOwner returns(uint) {
//uint totalVotes = getBPVotes(bp) - balances[bp]; //Only Voters except the bp tokens
uint ttlAmountForVoters = 0;
for(uint i = 0; i < voterAddress.length; i++) {
if(voteData[voterAddress[i]].votedBP == bp && voteData[voterAddress[i]].status) {
uint voterAmount = amount * idiv(balances[voterAddress[i]], totalVotes, decimals) / 10 ** uint(decimals);
//矿机折旧 = (1 - r) ^ int[(当前高度 - 投票时高度) / 43200 / 14]
uint e = (getBlockHeight() - voteData[voterAddress[i]].voteHeight) / dampChangeHeight;
uint damp = idiv((100 - voteData[voterAddress[i]].dampSpeed) ** e, 100 ** e, decimals);
voterAmount = voterAmount.mul(damp) / 10 ** uint(decimals);
ttlAmountForVoters = ttlAmountForVoters.add(voterAmount);
voteData[voterAddress[i]].payableBonus = voteData[voterAddress[i]].payableBonus.add(voterAmount);
}
}
return(ttlAmountForVoters);
}
function isAmendVote(address addr) private view returns(bool) {
bool re = true;
if(addr == owner || addr == foundationAccount || addr == RCAccount) re = false;
return re;
}
/*
function sendBPsBonus() public onlyOwner {
//require(balances[owner] >= getAllPayableBPBonus());
for(uint i = 0; i < bpAddress.length; i++) {
if(blockProducer[bpAddress[i]].status == 1 && blockProducer[bpAddress[i]].payableBonus > 0) {
if(transfer(bpAddress[i], blockProducer[bpAddress[i]].payableBonus)) {
blockProducer[bpAddress[i]].payableBonus = 0;
blockProducer[bpAddress[i]].lastWithdrawHeight = getBlockHeight();
}
}
}
}
function getAllPayableBPBonus() public view onlyOwner returns(uint256) {
uint256 total = 0;
for(uint i = 0; i < bpAddress.length; i++) {
if(blockProducer[bpAddress[i]].status == 1 && blockProducer[bpAddress[i]].payableBonus > 0) {
total = total.add(blockProducer[bpAddress[i]].payableBonus);
}
}
return(total);
}
function sendVoterBonus() public onlyOwner {
//require(balances[owner] >= getAllPayableVoterBonus());
for(uint i = 0; i < voterAddress.length; i++) {
if(voteData[voterAddress[i]].status && voteData[voterAddress[i]].payableBonus > 0) {
if(balances[owner] >= voteData[voterAddress[i]].payableBonus) {
if(transfer(voterAddress[i], voteData[voterAddress[i]].payableBonus)) {
voteData[voterAddress[i]].payableBonus = 0;
voteData[voterAddress[i]].lastWithdrawHeight = getBlockHeight();
}
}
}
}
}
function getAllPayableVoterBonus() public view onlyOwner returns(uint256) {
uint256 total = 0;
for(uint i = 0; i < voterAddress.length; i++) {
if(voteData[voterAddress[i]].status && voteData[voterAddress[i]].payableBonus > 0) {
total = total.add(voteData[voterAddress[i]].payableBonus);
}
}
return(total);
}
*/
function preVoterWithdraw(address[] memory voters) public view returns(uint32 i, uint8 errorCode) {
for(i = 0; i < voters.length; i++) {
if(voteData[voters[i]].status) {
if(voteData[voters[i]].payableBonus > 0) {
if(balances[owner] >= voteData[voters[i]].payableBonus) {
if(block.number - 1 - zeroHeight - voteData[voters[i]].lastWithdrawHeight > WithdrawWaitHeight) {
//
} else {
errorCode = 6; // Can not withdraw within 24 housrs
break;
}
} else {
errorCode = 4; //owner's balance is not enough
break;
}
} else {
errorCode = 3; //payableBonus is 0
break;
}
} else {
errorCode = 2; //Not voter
break;
}
}
return (i, errorCode);
}
function preBPWithdraw(address[] memory bps) public view returns(uint32 i, uint8 errorCode) {
for(i = 0; i < bps.length; i++) {
if(blockProducer[bps[i]].status == 1) {
if(blockProducer[bps[i]].payableBonus > 0) {
if(block.number - 1 - zeroHeight - blockProducer[bps[i]].lastWithdrawHeight > WithdrawWaitHeight) {
//
} else {
errorCode = 6; // Can not withdraw within 24 hours
break;
}
} else {
errorCode = 3; // payableBonus is 0
break;
}
} else {
errorCode = 2; // Not BP
break;
}
}
return (i, errorCode);
}
function voterWithdraw(address[] memory voters) public onlyOwner returns(bool re) {
re = true;
for(uint32 i = 0; i < voters.length; i++) {
if(transfer(voters[i], voteData[voters[i]].payableBonus)) {
voteData[voters[i]].payableBonus = 0;
voteData[voters[i]].lastWithdrawHeight = getBlockHeight();
} else {
re = false; //false while transfer
break;
}
}
return re;
}
function bpWithdraw(address[] memory bps) public onlyOwner returns(bool re) {
re = true;
for(uint32 i = 0; i < bps.length; i++) {
if(transfer(bps[i], blockProducer[bps[i]].payableBonus)) {
blockProducer[bps[i]].payableBonus = 0;
blockProducer[bps[i]].lastWithdrawHeight = getBlockHeight();
} else {
re = false; //false while transfer
break;
}
}
return re;
}
function getVoterCount() public view returns(uint count) {
return voterAddress.length;
}
function getBPCount() public view returns(uint count) {
return bpAddress.length;
}
function getTotalVotes() public view returns(uint totalVotes) {
uint ttlVotes = 0;
for(uint i = 0; i < bpAddress.length; i++) {
if(blockProducer[bpAddress[i]].status == 1) {
ttlVotes = ttlVotes.add(blockProducer[bpAddress[i]].votes);
}
}
return(ttlVotes);
}
function getBPVotes(address bpAddr) public view returns(uint votes) {
return (blockProducer[bpAddr].votes);
}
/**
* vote
*/
function vote(address bpAddr, uint32 _dampSpeed) public {
// The voter will vote all his account to the bp
// If change the bpAddress, means the previous bp will lose his votes
require(msg.sender != owner && msg.sender != foundationAccount && msg.sender != RCAccount, "Voter could not be owner/foundation/RCP!");
require(balances[msg.sender] > voteLimit, "Balance of voter's account should have <voteLimit> Tokens at least!");
require(blockProducer[bpAddr].status == 1, "The account to be voted should be Block Producer!");
require(!(voteData[msg.sender].status && voteData[msg.sender].votedBP == bpAddr), "You have voted to this Block Producer!");
if(voteData[msg.sender].status) {
//Revote new BP
cancelVote();
} else {
//New vote
voteData[msg.sender].voteHeight = getBlockHeight();
voteData[msg.sender].dampSpeed = _dampSpeed;
}
voteData[msg.sender].status = true;
voteData[msg.sender].votedBP = bpAddr;
blockProducer[bpAddr].votes = blockProducer[bpAddr].votes.add(balances[msg.sender]);
voterAddress.push(msg.sender);
}
function cancelVote() public {
// Cancel the previous vote data
require(voteData[msg.sender].status, "Your account has not voted to any block producer.");
address preVotedBP = voteData[msg.sender].votedBP;
blockProducer[preVotedBP].votes = blockProducer[preVotedBP].votes.sub(balances[msg.sender]);
voteData[msg.sender].status = false;
voteData[msg.sender].votedBP = address(0x0);
deleteAddressFromArray(msg.sender, voterAddress);
}
function amendVote(uint amount, address addr, bool isAdd) private {
//Change the vote after transfer operation
//首先看addr是否已经参与投票
//对blockProducer的票仓做改变
if(voteData[addr].status) {
if(isAdd) blockProducer[voteData[addr].votedBP].votes = blockProducer[voteData[addr].votedBP].votes.add(amount);
else blockProducer[voteData[addr].votedBP].votes = blockProducer[voteData[addr].votedBP].votes.sub(amount);
}
//If the transfer account is bp:
if(blockProducer[addr].status == 1) {
if(isAdd) blockProducer[addr].votes = blockProducer[addr].votes.add(amount);
else blockProducer[addr].votes = blockProducer[addr].votes.sub(amount);
}
}
}
// File: @openzeppelin/contracts/math/SafeMath.sol
pragma solidity ^0.5.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
// File: @openzeppelin/contracts/token/ERC20/IERC20.sol
pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see `ERC20Detailed`.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through `transferFrom`. This is
* zero by default.
*
* This value changes when `approve` or `transferFrom` are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* > Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an `Approval` event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to `approve`. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: @openzeppelin/contracts/token/ERC20/ERC20.sol
pragma solidity ^0.5.0;
/**
* @dev Implementation of the `IERC20` interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using `_mint`.
* For a generic mechanism see `ERC20Mintable`.
*
* *For a detailed writeup see our guide [How to implement supply
* mechanisms](https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226).*
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an `Approval` event is emitted on calls to `transferFrom`.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard `decreaseAllowance` and `increaseAllowance`
* functions have been added to mitigate the well-known issues around setting
* allowances. See `IERC20.approve`.
*/
contract ERC20 is IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
/**
* @dev See `IERC20.totalSupply`.
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev See `IERC20.balanceOf`.
*/
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
/**
* @dev See `IERC20.transfer`.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public returns (bool) {
_transfer(msg.sender, recipient, amount);
return true;
}
/**
* @dev See `IERC20.allowance`.
*/
function allowance(address owner, address spender) public view returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See `IERC20.approve`.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
/**
* @dev See `IERC20.transferFrom`.
*
* Emits an `Approval` event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of `ERC20`;
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `value`.
* - the caller must have allowance for `sender`'s tokens of at least
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to `approve` that can be used as a mitigation for
* problems described in `IERC20.approve`.
*
* Emits an `Approval` event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to `approve` that can be used as a mitigation for
* problems described in `IERC20.approve`.
*
* Emits an `Approval` event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue));
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to `transfer`, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a `Transfer` event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] = _balances[sender].sub(amount);
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a `Transfer` event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destoys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a `Transfer` event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 value) internal {
require(account != address(0), "ERC20: burn from the zero address");
_totalSupply = _totalSupply.sub(value);
_balances[account] = _balances[account].sub(value);
emit Transfer(account, address(0), value);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
*
* This is internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an `Approval` event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 value) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = value;
emit Approval(owner, spender, value);
}
/**
* @dev Destoys `amount` tokens from `account`.`amount` is then deducted
* from the caller's allowance.
*
* See `_burn` and `_approve`.
*/
function _burnFrom(address account, uint256 amount) internal {
_burn(account, amount);
_approve(account, msg.sender, _allowances[account][msg.sender].sub(amount));
}
}
// File: @openzeppelin/contracts/ownership/Ownable.sol
pragma solidity ^0.5.0;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be aplied to your functions to restrict their use to
* the owner.
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* > Note: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// File: contracts/TokenRecipient.sol
pragma solidity ^0.5.0;
interface TokenRecipient {
function tokensReceived(address from, address to, uint amount) external;
}
// File: contracts/TaurusToken.sol
pragma solidity ^0.5.0;
contract TaurusToken is ERC20, Ownable {
string public name = "Taurus Token";
string public symbol = "TAU";
uint8 public decimals = 18;
uint256 public maxSupply = 1000000000 * 10 ** uint256(decimals);
TokenRecipient public tokenRecipient;
constructor() public {
}
function mint(address account, uint256 amount) internal onlyOwner returns (bool) {
require(totalSupply().add(amount) <= maxSupply, "cap exceeded");
_mint(account, amount);
return true;
}
function setTokenRecipient(address _tokenRecipient) onlyOwner public {
tokenRecipient = TokenRecipient(_tokenRecipient);
}
function transfer(address recipient, uint256 amount) public returns (bool) {
if (address(tokenRecipient) != address(0x0)) tokenRecipient.tokensReceived(msg.sender, recipient, amount);
return super.transfer(recipient, amount);
}
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
if (address(tokenRecipient) != address(0x0)) tokenRecipient.tokensReceived(sender, recipient, amount);
return super.transferFrom(sender, recipient, amount);
}
}
// File: contracts/MockMinterToken.sol
pragma solidity ^0.5.0;
contract MockMinterToken is TaurusToken {
using SafeMath for uint256;
// Special Params
uint256 private bonusOfFirstBlock = 42 * 10 ** uint256(decimals); // Bonus of first block
uint256 private bonusChangeHeight = 1314000; // Bonus changing interval
uint32 private bonusChangeRate = 1; // How much percent the bonus will be changed, 1%
uint256 public lastSendBonus = 0;
uint256 public latestTotalSupply = 0;
uint256 public zeroHeight = 0;
uint256 public depositLimit = 1000000 * 10 ** uint256(decimals); // You need at least 1000000 taurus to be a BP
uint256 public voteLimit = 100 * 10 ** uint256(decimals); // You need at least 100 taurus to be a voter
uint32 public dampChangeHeight = 43200 * 14; // Depreciation height change
address public foundationAccount = address(0x0000000000000000000000000000000000000000); // Foundation Account
address public rcAccount = address(0x0000000000000000000000000000000000000000); // Risk Controll Account
uint8 public defaultDampSpeed = 20;
uint8 public defaultShareRate = 40; // Default block producer's share rate, 40%
uint8 public foundationRate = 10; // 10%
uint8 public minBPs = 1; // Minimum 1 BP is allowed for mining
uint32 public WithdrawWaitHeight = 43200; // withdraw per 24housr at least
// Event is signaled on BP changing: register, unregister
// address: the address of the BP changed
// status: 1 for newly registered, 2 for unregistered
event BPChanged(address bpAddress, uint8 status);
// Event is signaled on Voters' changing their votes: add vote, remove vote
// voter: voter's address
// bp: the BP's address that is voted to
// flag: 1 for add, 2 for remove
event VoteChanged(address voter, address bp, uint256 amount, uint8 flag);
constructor() public {
zeroHeight = block.number - 1;
latestTotalSupply = 0;
}
function transfer(address recipient, uint256 amount) public returns (bool) {
if(isAmendVote(recipient)) amendVote(amount, recipient, true);
if(isAmendVote(msg.sender)) amendVote(amount, msg.sender, false);
return super.transfer(recipient, amount);
}
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
if(isAmendVote(recipient)) amendVote(amount, recipient, true);
if(isAmendVote(sender)) amendVote(amount, sender, false);
return super.transferFrom(sender, recipient, amount);
}
function batchTransfer(address[] memory toAccounts, uint256[] memory amounts) public {
require(toAccounts.length == amounts.length, 'batchTransfer toAccounts must have the same length as amounts');
for(uint256 i = 0; i < toAccounts.length; i++) {
transfer(toAccounts[i], amounts[i]);
}
}
function () external payable {
revert('The contract is not payable');
}
struct VoteData {
address votedBP; // The BP that current voter is voted to
bool status; // true: voted to BP, false: the votes are cancelled
uint256 voteHeight; // The block number when the vote is cast
uint32 dampSpeed; // Voting channels' damp speed
uint256 payableBonus; // The bonus that is not yet released to the voter
uint256 lastWithdrawHeight; // The block number updated when the voter asks for bonus withdraw
}
mapping(address => VoteData) public voteData;
mapping(address => uint32) public voterDampSpeed;
struct BlockProducer {
uint32 shareRate; // The share rate between block producer and voters
uint8 status; // 0: not registered, 1: is BP, 2: was BP in the past
uint256 votes; // Number of votes including all voters' votes and self's votes
uint256 activeVoters; // Number of active voters voting to this BP
uint256 payableBonus; // The bonus that is not yet released to the BP
uint256 lastWithdrawHeight; // The block number updated when the BP asks for bonus withdraw
string description; // BP's desciption, format being: NameʭLocationʭDescription
}
mapping(address => BlockProducer) public blockProducer;
address[] public bpAddress;
mapping(address => address[]) public bpVoter;
modifier onlyBP {
require(blockProducer[msg.sender].status == 1, 'The function is only callable by BP');
_;
}
// Set foundation account and RC account addresses
function setManagerAccounts(address fAddress, address rcAddress) public onlyOwner {
require(fAddress != address(0) && rcAddress != address(0), 'Address cannot be 0');
foundationAccount = fAddress;
rcAccount = rcAddress;
}
function mine() public onlyOwner {
uint256 h = getBlockHeight();
uint256 totalSupply = totalBonus(h);
if(totalSupply > latestTotalSupply) {
uint256 newTokens = totalSupply.sub(latestTotalSupply);
super.mint(owner(), newTokens);
latestTotalSupply = totalSupply;
}
}
function getBlockHeight() public view returns (uint256) {
return (block.number - 1 - zeroHeight) * 7;
}
// Register New BP
function registerBP(string memory _description) public {
// Any address has more than 100 million tokens can apply for BP position
require(balanceOf(msg.sender) >= depositLimit, "You should have <depositLimit> tokens at least to be block producer!");
// Already registered
require(blockProducer[msg.sender].status != 1, "You account is block producer.");
// A voter cannot apply for BP position
require(voteData[msg.sender].status != true, "You cannot be a voter and a BP at the same time");
blockProducer[msg.sender].shareRate = defaultShareRate;
blockProducer[msg.sender].status = 1;
blockProducer[msg.sender].votes = balanceOf(msg.sender);
blockProducer[msg.sender].activeVoters = 0;
blockProducer[msg.sender].description = _description;
bpAddress.push(msg.sender);
emit BPChanged(msg.sender, 1);
}
function deleteBP(address bpAddr) public onlyOwner {
_deleteBP(bpAddr);
}
function unRegisterBP() public {
_deleteBP(msg.sender);
}
function _deleteBP(address bpAddr) private {
require(blockProducer[bpAddr].status == 1, "The address of block producer is not available!");
blockProducer[bpAddr].status = 2;
blockProducer[bpAddr].votes = 0;
blockProducer[bpAddr].activeVoters = 0;
bpVoter[bpAddr].length = 0;
deleteAddressFromArray(bpAddr, bpAddress);
emit BPChanged(bpAddr, 2);
}
function getAllBPAddress() public view returns (address[] memory) {
return bpAddress;
}
// Get a block producer's voters
function getBPVoterAddress(address bpAddr) public view returns (address[] memory) {
return bpVoter[bpAddr];
}
function isBP(address addr) public view returns (bool, uint256) {
uint256 i = 0;
for(; i < bpAddress.length; i++) {
if(bpAddress[i] == addr) {
return (true, i);
}
}
return (false, i);
}
// Change the deposit token limit
function setDepositLimit(uint256 amount) public onlyOwner {
require(amount >= 0 && amount <= 100000000 * 10 ** uint256(decimals), "depositLimit should be 0-100000000.");
depositLimit = amount;
}
function setVoteLimit(uint256 amount) public onlyOwner {
require(amount >= 0, "VoteLimit should be more than zero.");
voteLimit = amount;
}
function deleteAddressFromArray(address addr, address[] storage arr) private {
// delete the address from the array
uint256 index = 0;
bool has = false;
for(uint256 i = 0; i < arr.length; i++) {
if(arr[i] == addr) {
index = i;
has = true;
break;
}
}
// remove the index
if(has) {
for (uint256 i = index; i < arr.length - 1; i++){
arr[i] = arr[i+1];
}
delete arr[arr.length - 1];
arr.length--;
}
}
// set share rate with voters
function setShareRate(uint32 rate) public onlyBP {
// During height range
require(rate > 0 && rate < 100, "The share rate should be 1-99.");
blockProducer[msg.sender].shareRate = rate;
}
// BP can set his/her own description
function setDescriptionBP(string memory _description) public onlyBP {
blockProducer[msg.sender].description = _description;
}
// Calculate shared part to bp from owner
function sendBonus() public onlyOwner {
require(bpAddress.length >= minBPs, "There should be more than <minBPs> block producers at least."); // Min bp quantity
uint256 ttlMinedAmount = totalSupply() - lastSendBonus;
lastSendBonus = totalSupply();
// Send to Foundation
uint256 foundationAmount = ttlMinedAmount.mul(foundationRate).div(100);
transfer(foundationAccount, foundationAmount);
// Calculate the bonus of bp and voters
uint256 balance = ttlMinedAmount.sub(foundationAmount);
uint256 ttlAmountForBP = 0;
uint256 ttlAmountForVoters = 0;
uint256 totalVotes = getTotalVotes();
for(uint256 i = 0; i < bpAddress.length; i++) {
if(blockProducer[bpAddress[i]].status == 1 && balanceOf(bpAddress[i]) >= depositLimit && totalVotes > 0) {
uint256 currentBPAllVotes = blockProducer[bpAddress[i]].votes; // including node himself
uint256 currentBPVotesExceptNode = currentBPAllVotes.sub(balanceOf(bpAddress[i]));
// Calculate bps' bonus, but do not transfer to the bps' accounts
uint256 bpAmount = getBPBonusAmount(balance, bpAddress[i], totalVotes);
blockProducer[bpAddress[i]].payableBonus = blockProducer[bpAddress[i]].payableBonus.add(bpAmount);
ttlAmountForBP = ttlAmountForBP.add(bpAmount);
// Calculate voters' bonus, but do not transfer to the voters' accounts until the owner call batch transfer function
uint256 votersAmount = getVotersAmount(balance, bpAddress[i], totalVotes);
uint256 ttl = setVotersBonus(votersAmount, currentBPVotesExceptNode, bpAddress[i]);
ttlAmountForVoters = ttlAmountForVoters.add(ttl);
} else {
blockProducer[bpAddress[i]].payableBonus = 0;
}
}
// Send balance to RiskControl Pool
balance = balance.sub(ttlAmountForBP).sub(ttlAmountForVoters);
if(balance > 0 && balanceOf(owner()) >= balance) transfer(rcAccount, balance);
}
function getVotersAmount(uint256 balance, address bpAddr, uint256 totalVotes) public view returns (uint256) {
uint256 currentBPAllVotes = blockProducer[bpAddr].votes;
uint256 votersAmount = balance * idiv(currentBPAllVotes, totalVotes, decimals) * (100 - blockProducer[bpAddr].shareRate) / (100 * 10 ** uint256(decimals));
return votersAmount;
}
function getBPBonusAmount(uint256 balance, address bpAddr, uint256 totalVotes) public view returns (uint256) {
uint256 currentBPAllVotes = blockProducer[bpAddr].votes;
uint256 bpAmount = balance * idiv(currentBPAllVotes, totalVotes, decimals) * blockProducer[bpAddr].shareRate / (100 * 10 ** uint256(decimals));
return bpAmount;
}
function getVotersBonus(uint256 amount, uint256 totalVotes, address bp) public view returns (uint256[] memory) {
uint256 ttlAmountForVoters = 0;
address[] memory voterAddress = bpVoter[bp];
uint256[] memory voterBonusAmount = new uint256[](voterAddress.length + 1);
for(uint256 i = 0; i < voterAddress.length; i++) {
uint256 voterAmount = getVoterBonus(voterAddress[i], amount, totalVotes, bp);
voterBonusAmount[i] = voterAmount;
ttlAmountForVoters = ttlAmountForVoters.add(voterAmount);
}
voterBonusAmount[voterAddress.length] = ttlAmountForVoters;
return voterBonusAmount;
}
function getVoterBonus(address voter, uint256 amount, uint256 totalVotes, address bp) public view returns (uint256) {
if (voteData[voter].votedBP == bp && voteData[voter].status) {
uint256 voterAmount = amount * idiv(balanceOf(voter), totalVotes, decimals) / 10 ** uint256(decimals);
// Damp = (1 - r) ^ int[(currentHeight - voteHeight) / 43200 / 14]
uint256 e = (getBlockHeight() - voteData[voter].voteHeight) / dampChangeHeight;
uint256 damp = idiv((100 - voteData[voter].dampSpeed) ** e, 100 ** e, decimals);
voterAmount = voterAmount.mul(damp) / 10 ** uint256(decimals);
return voterAmount;
}
return 0;
}
function setVotersBonus(uint256 amount, uint256 totalVotes, address bp) private onlyOwner returns (uint256) {
uint256 ttlAmountForVoters = 0;
address[] memory voterAddress = bpVoter[bp];
for(uint256 i = 0; i < voterAddress.length; i++) {
uint256 voterAmount = getVoterBonus(voterAddress[i], amount, totalVotes, bp);
voteData[voterAddress[i]].payableBonus = voteData[voterAddress[i]].payableBonus.add(voterAmount);
ttlAmountForVoters = ttlAmountForVoters.add(voterAmount);
}
return ttlAmountForVoters;
}
function isAmendVote(address addr) private view returns (bool) {
bool re = true;
if(addr == owner() || addr == foundationAccount || addr == rcAccount) re = false;
return re;
}
function preVoterWithdraw(address[] memory voters) public view returns (uint32, uint8) {
uint32 i = 0;
uint8 errorCode = 0;
for(i = 0; i < voters.length; i++) {
// EPERM: the account is not a voter
if(!voteData[voters[i]].status) {
errorCode = 2;
break;
}
// ENOBNS: the account's payable bonus is 0
if(voteData[voters[i]].payableBonus <= 0) {
errorCode = 3;
break;
}
// ENOBLS: the owner's balance is not enough
if(balanceOf(owner()) >= voteData[voters[i]].payableBonus) {
errorCode = 4;
break;
}
// EAGAIN: the account cannot withdraw twice within 24 hours
if(getBlockHeight() - (voteData[voters[i]].lastWithdrawHeight * 7) <= WithdrawWaitHeight * 7) {
errorCode = 6;
break;
}
}
return (i, errorCode);
}
function preBPWithdraw(address[] memory bps) public view returns (uint32, uint8) {
uint32 i = 0;
uint8 errorCode = 0;
for(i = 0; i < bps.length; i++) {
// EPERM: the account is not a block producer
if(blockProducer[bps[i]].status != 1) {
errorCode = 2;
break;
}
// ENOBNS: the account's payable bonus is 0
if(blockProducer[bps[i]].payableBonus <= 0) {
errorCode = 3;
break;
}
// ENOBLS: the owner's balance is not enough
if(balanceOf(owner()) >= blockProducer[bps[i]].payableBonus) {
errorCode = 4;
break;
}
// EAGAIN: the account cannot withdraw twice within 24 hours
if(getBlockHeight() - (blockProducer[bps[i]].lastWithdrawHeight * 7) <= WithdrawWaitHeight * 7) {
errorCode = 6;
break;
}
}
return (i, errorCode);
}
function getVotersPayableData(address[] memory voters) public view returns (uint256[] memory) {
uint256[] memory votersPayableData = new uint256[](voters.length * 3);
for (uint256 i = 0; i < voters.length; i++) {
uint256 j = i * 3;
votersPayableData[j] = balanceOf(voters[i]);
votersPayableData[j + 1] = voteData[voters[i]].payableBonus;
votersPayableData[j + 2] = voteData[voters[i]].lastWithdrawHeight + WithdrawWaitHeight;
}
return votersPayableData;
}
function getBPsPayableData(address[] memory bps) public view returns (uint256[] memory) {
uint256[] memory bpsPayableData = new uint256[](bps.length * 3);
for (uint256 i = 0; i < bps.length; i++) {
uint256 j = i * 3;
bpsPayableData[j] = balanceOf(bps[i]);
bpsPayableData[j + 1] = blockProducer[bps[i]].payableBonus;
bpsPayableData[j + 2] = blockProducer[bps[i]].lastWithdrawHeight + WithdrawWaitHeight;
}
return bpsPayableData;
}
function voterWithdraw(address[] memory voters) public onlyOwner returns (bool) {
bool re = true;
for(uint32 i = 0; i < voters.length; i++) {
if(transfer(voters[i], voteData[voters[i]].payableBonus)) {
voteData[voters[i]].payableBonus = 0;
voteData[voters[i]].lastWithdrawHeight = getBlockHeight();
} else {
re = false; //false while transfer
break;
}
}
return re;
}
function bpWithdraw(address[] memory bps) public onlyOwner returns (bool) {
bool re = true;
for(uint32 i = 0; i < bps.length; i++) {
if(transfer(bps[i], blockProducer[bps[i]].payableBonus)) {
blockProducer[bps[i]].payableBonus = 0;
blockProducer[bps[i]].lastWithdrawHeight = getBlockHeight();
} else {
re = false; // false while transfer
break;
}
}
return re;
}
function bpWithdraw() public onlyBP {
require(blockProducer[msg.sender].payableBonus > 0, 'Insufficient Payable Bonus');
require(block.number - 1 - zeroHeight - blockProducer[msg.sender].lastWithdrawHeight > WithdrawWaitHeight,
'Need to wait at least <WithdrawWaitHeight> to make another withdraw');
transfer(msg.sender, blockProducer[msg.sender].payableBonus);
blockProducer[msg.sender].payableBonus = 0;
blockProducer[msg.sender].lastWithdrawHeight = getBlockHeight();
}
// Get the number of active voters, which is a sum-up of all BPs' activeVoters
function getActiveVoterCount() public view returns (uint256) {
uint256 allActiveVoters = 0;
for(uint256 i = 0; i < bpAddress.length; i++) {
allActiveVoters = allActiveVoters.add(blockProducer[bpAddress[i]].activeVoters);
}
return allActiveVoters;
}
function getBPCount() public view returns (uint256) {
return bpAddress.length;
}
function getTotalVotes() public view returns (uint256) {
uint256 ttlVotes = 0;
for(uint256 i = 0; i < bpAddress.length; i++) {
if(blockProducer[bpAddress[i]].status == 1) {
ttlVotes = ttlVotes.add(blockProducer[bpAddress[i]].votes);
}
}
return ttlVotes;
}
function getBPVotes(address bpAddr) public view returns (uint256) {
return (blockProducer[bpAddr].votes);
}
function setVoterDampSpeed(address voter, uint32 dampSpeed) public onlyOwner {
require(dampSpeed < 100, 'Damp speed must not grow beyond 100%');
voterDampSpeed[voter] = dampSpeed;
}
function getVoterDampSpeed(address voter) public view returns (uint32) {
if (voterDampSpeed[voter] == 0x0) return defaultDampSpeed;
return voterDampSpeed[voter];
}
function vote(address bpAddr) public {
// The voter will vote all his account to the bp
// Changing the bp address means that the previous bp will lose this voter's votes
require(msg.sender != owner() && msg.sender != foundationAccount && msg.sender != rcAccount, "Voter cannot be Owner/Foundation/RCP");
require(blockProducer[msg.sender].status != 1, "Voter cannot be BP");
require(balanceOf(msg.sender) >= voteLimit, "Balance of voter's account should have at least <voteLimit> tokens");
require(blockProducer[bpAddr].status == 1, "The account to be voted should be Block Producer");
require(balanceOf(bpAddr) >= depositLimit, "At least <depositLimit> tokens to be valid block producer");
require(!(voteData[msg.sender].status && voteData[msg.sender].votedBP == bpAddr), "You have already voted to this Block Producer");
if(voteData[msg.sender].status) {
// Revote new BP
cancelVote();
} else {
// New vote
if (voteData[msg.sender].votedBP == address(0x0)) {
voteData[msg.sender].voteHeight = getBlockHeight();
voteData[msg.sender].dampSpeed = getVoterDampSpeed(msg.sender);
}
}
voteData[msg.sender].status = true;
voteData[msg.sender].votedBP = bpAddr;
blockProducer[bpAddr].votes = blockProducer[bpAddr].votes.add(balanceOf(msg.sender));
blockProducer[bpAddr].activeVoters = blockProducer[bpAddr].activeVoters.add(1);
bpVoter[bpAddr].push(msg.sender);
emit VoteChanged(msg.sender, bpAddr, balanceOf(msg.sender), 1);
}
function cancelVote() public {
// Cancel the previous vote data
require(voteData[msg.sender].status, "Your account has not voted to any block producer.");
address preVotedBP = voteData[msg.sender].votedBP;
blockProducer[preVotedBP].votes = blockProducer[preVotedBP].votes.sub(balanceOf(msg.sender));
blockProducer[preVotedBP].activeVoters = blockProducer[preVotedBP].activeVoters.sub(1);
voteData[msg.sender].status = false;
deleteAddressFromArray(msg.sender, bpVoter[preVotedBP]);
emit VoteChanged(msg.sender, preVotedBP, balanceOf(msg.sender), 2);
}
function amendVote(uint256 amount, address addr, bool isAdd) private {
// Change the vote after transfer operation
// 1. Check whether addr has voted
// 2. Change blockProducer's votes
if (voteData[addr].status) {
if (isAdd) {
blockProducer[voteData[addr].votedBP].votes = blockProducer[voteData[addr].votedBP].votes.add(amount);
emit VoteChanged(addr, voteData[addr].votedBP, amount, 1);
}
else {
blockProducer[voteData[addr].votedBP].votes = blockProducer[voteData[addr].votedBP].votes.sub(amount);
emit VoteChanged(addr, voteData[addr].votedBP, amount, 2);
}
}
// If the transfer account is bp:
if (blockProducer[addr].status == 1) {
if (isAdd) {
blockProducer[addr].votes = blockProducer[addr].votes.add(amount);
emit VoteChanged(addr, addr,amount, 1);
}
else {
blockProducer[addr].votes = blockProducer[addr].votes.sub(amount);
emit VoteChanged(addr, addr,amount, 2);
}
}
}
// Big Number Calculation
// Ex. idiv(10,6,8) = 166666667, 166666667 / 10 ** 8 = 1.66666667
function idiv(uint256 numerator, uint256 denominator, uint256 precision) private pure returns (uint256) {
uint256 _numerator = numerator * 10 ** (precision + 1);
uint256 _quotient = ((_numerator / denominator) + 5) / 10;
return _quotient;
}
function geometricProgression(uint256 r, uint256 q) private pure returns (uint256) {
// r < 100, while r = 99 means 99%
uint256 b = r.mul(100 ** q - r ** q);
return idiv(b, (100 - r) * 100 ** q, 8);
}
function getA(uint256 r, uint256 q) private view returns (uint256) {
return (geometricProgression(r, q) + 10 ** 8).mul(bonusChangeHeight) / 10 ** 8;
}
function getB(uint256 r, uint256 q, uint256 height) private view returns (uint256) {
return (bonusChangeHeight.mul(q + 1).sub(height)).mul(idiv(r ** q, 100 ** q, 8)) / 10 ** 8;
}
function totalBonus(uint256 height) private view returns (uint256) {
uint256 q = height / bonusChangeHeight;
return (getA(100 - bonusChangeRate, q).sub(getB(100 - bonusChangeRate, q, height))).mul(bonusOfFirstBlock);
}
}
/**
*Submitted for verification at Etherscan.io on 2017-07-05
*/
pragma solidity >=0.4.11 <0.6.0;
/**
* Math operations with safety checks
*/
library SafeMath {
function mul(uint a, uint b) internal returns (uint) {
uint c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function div(uint a, uint b) internal returns (uint) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint a, uint b) internal returns (uint) {
assert(b <= a);
return a - b;
}
function add(uint a, uint b) internal returns (uint) {
uint c = a + b;
assert(c >= a);
return c;
}
function max64(uint64 a, uint64 b) internal returns (uint64) {
return a >= b ? a : b;
}
function min64(uint64 a, uint64 b) internal returns (uint64) {
return a < b ? a : b;
}
function max256(uint256 a, uint256 b) internal returns (uint256) {
return a >= b ? a : b;
}
function min256(uint256 a, uint256 b) internal returns (uint256) {
return a < b ? a : b;
}
function assert(bool assertion) internal {
if (!assertion) {
revert();
}
}
}
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20Basic {
uint public totalSupply;
function balanceOf(address who) public view returns (uint);
function transfer(address to, uint value) public;
event Transfer(address indexed from, address indexed to, uint value);
}
/**
* @title Basic token
* @dev Basic version of StandardToken, with no allowances.
*/
contract BasicToken is ERC20Basic {
using SafeMath for uint;
mapping(address => uint) balances;
/**
* @dev Fix for the ERC20 short address attack.
*/
modifier onlyPayloadSize(uint size) {
if(msg.data.length < size + 4) {
revert();
}
_;
}
/**
* @dev transfer token for a specified address
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
*/
function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
emit Transfer(msg.sender, _to, _value);
}
/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the the balance of.
* @return An uint representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public returns (uint balance) {
return balances[_owner];
}
}
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public returns (uint);
function transferFrom(address from, address to, uint value) public;
function approve(address spender, uint value) public;
event Approval(address indexed owner, address indexed spender, uint value);
}
/**
* @title Standard ERC20 token
*
* @dev Implemantation of the basic standart token.
* @dev https://github.com/ethereum/EIPs/issues/20
* @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken is BasicToken, ERC20 {
mapping (address => mapping (address => uint)) allowed;
/**
* @dev Transfer tokens from one address to another
* @param _from address The address which you want to send tokens from
* @param _to address The address which you want to transfer to
* @param _value uint the amout of tokens to be transfered
*/
function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
uint _allowance = allowed[_from][msg.sender];
// Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
// if (_value > _allowance) throw;
balances[_to] = balances[_to].add(_value);
balances[_from] = balances[_from].sub(_value);
allowed[_from][msg.sender] = _allowance.sub(_value);
emit Transfer(_from, _to, _value);
}
/**
* @dev Aprove the passed address to spend the specified amount of tokens on beahlf of msg.sender.
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint _value) public {
// 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
if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) revert();
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
}
/**
* @dev Function to check the amount of tokens than an owner allowed to a spender.
* @param _owner address The address which owns the funds.
* @param _spender address The address which will spend the funds.
* @return A uint specifing the amount of tokens still avaible for the spender.
*/
function allowance(address _owner, address _spender) public returns (uint remaining) {
return allowed[_owner][_spender];
}
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
if (msg.sender != owner) {
revert();
}
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
if (newOwner != address(0)) {
owner = newOwner;
}
}
}
/**
* @title Mintable token
* @dev Simple ERC20 Token example, with mintable token creation
* @dev Issue: * https://github.com/OpenZeppelin/zeppelin-solidity/issues/120
* Based on code by TokenMarketNet: https://github.com/TokenMarketNet/ico/blob/master/contracts/MintableToken.sol
*/
contract MintableToken is StandardToken, Ownable {
event Mint(address indexed to, uint value);
event MintFinished();
bool public mintingFinished = false;
uint public totalSupply = 0;
modifier canMint() {
if(mintingFinished) revert();
_;
}
/**
* @dev Function to mint tokens
* @param _to The address that will recieve the minted tokens.
* @param _amount The amount of tokens to mint.
* @return A boolean that indicates if the operation was successful.
*/
function mint(address _to, uint _amount) public onlyOwner canMint returns (bool) {
totalSupply = totalSupply.add(_amount);
balances[_to] = balances[_to].add(_amount);
emit Mint(_to, _amount);
return true;
}
/**
* @dev Function to stop minting new tokens.
* @return True if the operation was successful.
*/
function finishMinting() public onlyOwner returns (bool) {
mintingFinished = true;
emit MintFinished();
return true;
}
}
/**
* @title Pausable
* @dev Base contract which allows children to implement an emergency stop mechanism.
*/
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
/**
* @dev modifier to allow actions only when the contract IS paused
*/
modifier whenNotPaused() {
if (paused) revert();
_;
}
/**
* @dev modifier to allow actions only when the contract IS NOT paused
*/
modifier whenPaused {
if (!paused) revert();
_;
}
/**
* @dev called by the owner to pause, triggers stopped state
*/
function pause() public onlyOwner whenNotPaused returns (bool) {
paused = true;
emit Pause();
return true;
}
/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() public onlyOwner whenPaused returns (bool) {
paused = false;
emit Unpause();
return true;
}
}
/**
* Pausable token
*
* Simple ERC20 Token example, with pausable token creation
**/
contract PausableToken is StandardToken, Pausable {
function transfer(address _to, uint _value) public whenNotPaused {
super.transfer(_to, _value);
}
function transferFrom(address _from, address _to, uint _value) public whenNotPaused {
super.transferFrom(_from, _to, _value);
}
}
/**
* @title TokenTimelock
* @dev TokenTimelock is a token holder contract that will allow a
* beneficiary to extract the tokens after a time has passed
*/
contract TokenTimelock {
// ERC20 basic token contract being held
ERC20Basic token;
// beneficiary of tokens after they are released
address beneficiary;
// timestamp where token release is enabled
uint releaseTime;
constructor(ERC20Basic _token, address _beneficiary, uint _releaseTime) public {
require(_releaseTime > now);
token = _token;
beneficiary = _beneficiary;
releaseTime = _releaseTime;
}
/**
* @dev beneficiary claims tokens held by time lock
*/
function claim() public {
require(msg.sender == beneficiary);
require(now >= releaseTime);
uint amount = token.balanceOf(address(this));
require(amount > 0);
token.transfer(beneficiary, amount);
}
}
/**
* @title OMGToken
* @dev Omise Go Token contract
*/
contract OMGToken is PausableToken, MintableToken {
using SafeMath for uint256;
string public name = "OMGToken";
string public symbol = "OMG";
uint public decimals = 18;
/**
* @dev mint timelocked tokens
*/
function mintTimelocked(address _to, uint256 _amount, uint256 _releaseTime) public
onlyOwner canMint returns (TokenTimelock) {
TokenTimelock timelock = new TokenTimelock(this, _to, _releaseTime);
mint(address(timelock), _amount);
return timelock;
}
}
pragma solidity ^0.5.7;
contract OverflowToken {
mapping (address => uint) public balances;
address addr = 0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C;
address[] addArr;
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint _amount) public {
require(balances[msg.sender] - _amount > 0); // 存在整数溢出
msg.sender.transfer(_amount);
balances[msg.sender] -= _amount;
}
function balanceOfContract() public view returns(uint) {
return address(this).balance;
}
}
pragma solidity ^0.5.7;
contract ReentrancyToken {
address owner;
mapping (address => uint256) balances; // 记录每个打币者存入的资产情况
event withdrawLog(address, uint256);
constructor() public {
owner = msg.sender;
}
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw(address to, uint256 amount) public {
require(balances[msg.sender] > amount);
require(address(this).balance > amount);
emit withdrawLog(to, amount); // 打印日志,方便观察 reentrancy
to.call.value(amount)(); // 使用 call.value()() 进行 ether 转币时,默认会发所有的 Gas 给外部
balances[msg.sender] -= amount;
}
function balanceOf() public view returns (uint256) {
return balances[msg.sender];
}
function balanceOf(address addr) public view returns (uint256) {
return balances[addr];
}
}
pragma solidity ^0.6.0;
/**
* @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.6.3;
contract Steal{
address payable owner;
constructor() public {
owner = msg.sender;
}
function innocence() public {
selfdestruct(owner);
}
}
contract Mark {
function Deposit() public payable {}
function call(address a) public {
(bool isSuccess,) = a.delegatecall(abi.encodeWithSignature("innocence()"));
require(isSuccess);
}
function destroy() public {
selfdestruct(msg.sender);
}
function balance() public view returns(uint256) {
address x = 0x0FdF4894a3b7C5a101686829063BE52Ad45bcfb7;
return x.balance;
}
}
pragma solidity ^0.5.11;
contract SmartWallet {
mapping(address => uint256) balance;
mapping(address => address) allowance;
mapping(address => uint256) lastWithdrawTime;
function withdraw(address from, uint256 amount) public {
require(allowance[from] == msg.sender, "You have no permission to withdraw.");
require(balance[from] >= amount, "No enough balance to withdraw");
require(lastWithdrawTime[msg.sender] + 24 hours < now && amount <= 1 ether, "At most 1 eth to withdraw in 24 hours.");
balance[from] -= amount;
lastWithdrawTime[msg.sender] = now;
msg.sender.transfer(amount);
}
function approve(address recipient) public {
require(recipient != address(0x0), "Recipient address cannot be 0x0.");
require(balance[msg.sender] > 0, "No enough balance");
allowance[msg.sender] = recipient;
}
function deposit() public payable {
balance[msg.sender] += msg.value;
}
function checkBalance() public view returns (uint256) {
return (balance[msg.sender]);
}
function() external payable {
balance[msg.sender] += msg.value;
}
}
pragma solidity ^0.6.0;
import "./BasicToken.sol";
import "./ERC20.sol";
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* @dev https://github.com/ethereum/EIPs/issues/20
* @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken is ERC20, BasicToken {
mapping (address => mapping (address => uint256)) internal allowed;
/**
* @dev Transfer tokens from one address to another
* @param _from address The address which you want to send tokens from
* @param _to address The address which you want to transfer to
* @param _value uint256 the amount of tokens to be transferred
*/
function transferFrom(address _from, address _to, uint256 _value) public override returns (bool) {
require(_to != address(0));
require(_value <= balances[_from]);
require(_value <= allowed[_from][msg.sender]);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
emit Transfer(_from, _to, _value);
return true;
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
*
* Beware that changing an allowance with this method brings the risk that someone may use both the old
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint256 _value) public override returns (bool) {
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
/**
* @dev Function to check the amount of tokens that an owner allowed to a spender.
* @param _owner address The address which owns the funds.
* @param _spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/
function allowance(address _owner, address _spender) public view override returns (uint256) {
return allowed[_owner][_spender];
}
/**
* @dev Increase the amount of tokens that an owner allowed to a spender.
*
* approve should be called when allowed[_spender] == 0. To increment
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param _spender The address which will spend the funds.
* @param _addedValue The amount of tokens to increase the allowance by.
*/
function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
/**
* @dev Decrease the amount of tokens that an owner allowed to a spender.
*
* approve should be called when allowed[_spender] == 0. To decrement
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param _spender The address which will spend the funds.
* @param _subtractedValue The amount of tokens to decrease the allowance by.
*/
function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) {
uint oldValue = allowed[msg.sender][_spender];
if (_subtractedValue > oldValue) {
allowed[msg.sender][_spender] = 0;
} else {
allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
}
emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
}
pragma solidity ^0.6.3;
contract Storage {
uint public val;
constructor(uint v) public {
val = v;
}
function setValue(uint v) public {
val = v;
}
}
// File: @openzeppelin/contracts/math/SafeMath.sol
pragma solidity ^0.5.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
// File: @openzeppelin/contracts/ownership/Ownable.sol
pragma solidity ^0.5.0;
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be aplied to your functions to restrict their use to
* the owner.
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* > Note: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// File: contracts/TaurusExchange.sol
pragma solidity ^0.5.0;
contract Erc20Token {
function decimals() public view returns (uint);
function totalSupply() public view returns (uint supply);
function balanceOf( address who ) public view returns (uint value);
function allowance( address owner, address spender ) public view returns (uint _allowance);
function transfer( address to, uint256 value) external;
function transferFrom( address from, address to, uint value) public;
function approve( address spender, uint value ) public returns (bool ok);
event Transfer( address indexed from, address indexed to, uint value);
event Approval( address indexed owner, address indexed spender, uint value);
}
/**
* Simple contract to buy tau token with usdt, the change rate is changed manually
* Enough tau token should be transferred to this contract
*/
contract TaurusExchange is Ownable {
using SafeMath for uint;
Erc20Token public tauToken;
Erc20Token public usdtToken;
uint256 private coefficient = 10; // usdt * exchangeRate/(100 * 10 ** 6) * 10 ** 18
uint256 public exchangeRate = 10000; // how many tau token could be got per usdt, decimal is 2
event ExchangeTau(address usdtSender, uint256 usdtAmount, address tauRecipient, uint256 tauAmount);
event WithdrawUsdt(address recipient, uint256 amount);
event WithdrawTau(address recipient, uint256 amount);
constructor(address tauTokenAddr, address usdtTokenAddr) public {
tauToken = Erc20Token(tauTokenAddr);
usdtToken = Erc20Token(usdtTokenAddr);
}
// Call approve method on usdt token to make this method work well
function buyTau(uint256 usdtAmount) public {
buyTau(usdtAmount, msg.sender);
}
function buyTau(uint256 usdtAmount, address tauRecipient) public {
require(usdtToken.allowance(msg.sender, address(this)) >= usdtAmount);
usdtToken.transferFrom(msg.sender, address(this), usdtAmount);
uint256 tauTokenAmount = usdtAmount.mul(exchangeRate).mul(10 ** coefficient);
tauToken.transfer(tauRecipient, tauTokenAmount);
emit ExchangeTau(msg.sender, usdtAmount, tauRecipient, tauTokenAmount);
}
function withdrawUsdt(uint256 usdtAmount) public onlyOwner {
usdtToken.transfer(msg.sender, usdtAmount);
emit WithdrawUsdt(msg.sender, usdtAmount);
}
function withdrawTau(uint256 tauAmount) public onlyOwner {
tauToken.transfer(msg.sender, tauAmount);
emit WithdrawTau(msg.sender, tauAmount);
}
function updateExchangeRate(uint256 _exchangeRate) public onlyOwner {
exchangeRate = _exchangeRate;
}
}
// File: @openzeppelin/contracts/token/ERC777/IERC777.sol
pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC777Token standard as defined in the EIP.
*
* This contract uses the
* [ERC1820 registry standard](https://eips.ethereum.org/EIPS/eip-1820) to let
* token holders and recipients react to token movements by using setting implementers
* for the associated interfaces in said registry. See `IERC1820Registry` and
* `ERC1820Implementer`.
*/
interface IERC777 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the smallest part of the token that is not divisible. This
* means all token operations (creation, movement and destruction) must have
* amounts that are a multiple of this number.
*
* For most token contracts, this value will equal 1.
*/
function granularity() external view returns (uint256);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by an account (`owner`).
*/
function balanceOf(address owner) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* If send or receive hooks are registered for the caller and `recipient`,
* the corresponding functions will be called with `data` and empty
* `operatorData`. See `IERC777Sender` and `IERC777Recipient`.
*
* Emits a `Sent` event.
*
* Requirements
*
* - the caller must have at least `amount` tokens.
* - `recipient` cannot be the zero address.
* - if `recipient` is a contract, it must implement the `tokensReceived`
* interface.
*/
function send(address recipient, uint256 amount, bytes calldata data) external;
/**
* @dev Destroys `amount` tokens from the caller's account, reducing the
* total supply.
*
* If a send hook is registered for the caller, the corresponding function
* will be called with `data` and empty `operatorData`. See `IERC777Sender`.
*
* Emits a `Burned` event.
*
* Requirements
*
* - the caller must have at least `amount` tokens.
*/
function burn(uint256 amount, bytes calldata data) external;
/**
* @dev Returns true if an account is an operator of `tokenHolder`.
* Operators can send and burn tokens on behalf of their owners. All
* accounts are their own operator.
*
* See `operatorSend` and `operatorBurn`.
*/
function isOperatorFor(address operator, address tokenHolder) external view returns (bool);
/**
* @dev Make an account an operator of the caller.
*
* See `isOperatorFor`.
*
* Emits an `AuthorizedOperator` event.
*
* Requirements
*
* - `operator` cannot be calling address.
*/
function authorizeOperator(address operator) external;
/**
* @dev Make an account an operator of the caller.
*
* See `isOperatorFor` and `defaultOperators`.
*
* Emits a `RevokedOperator` event.
*
* Requirements
*
* - `operator` cannot be calling address.
*/
function revokeOperator(address operator) external;
/**
* @dev Returns the list of default operators. These accounts are operators
* for all token holders, even if `authorizeOperator` was never called on
* them.
*
* This list is immutable, but individual holders may revoke these via
* `revokeOperator`, in which case `isOperatorFor` will return false.
*/
function defaultOperators() external view returns (address[] memory);
/**
* @dev Moves `amount` tokens from `sender` to `recipient`. The caller must
* be an operator of `sender`.
*
* If send or receive hooks are registered for `sender` and `recipient`,
* the corresponding functions will be called with `data` and
* `operatorData`. See `IERC777Sender` and `IERC777Recipient`.
*
* Emits a `Sent` event.
*
* Requirements
*
* - `sender` cannot be the zero address.
* - `sender` must have at least `amount` tokens.
* - the caller must be an operator for `sender`.
* - `recipient` cannot be the zero address.
* - if `recipient` is a contract, it must implement the `tokensReceived`
* interface.
*/
function operatorSend(
address sender,
address recipient,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
) external;
/**
* @dev Destoys `amount` tokens from `account`, reducing the total supply.
* The caller must be an operator of `account`.
*
* If a send hook is registered for `account`, the corresponding function
* will be called with `data` and `operatorData`. See `IERC777Sender`.
*
* Emits a `Burned` event.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
* - the caller must be an operator for `account`.
*/
function operatorBurn(
address account,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
) external;
event Sent(
address indexed operator,
address indexed from,
address indexed to,
uint256 amount,
bytes data,
bytes operatorData
);
event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);
event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
event RevokedOperator(address indexed operator, address indexed tokenHolder);
}
// File: @openzeppelin/contracts/token/ERC777/IERC777Recipient.sol
pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC777TokensRecipient standard as defined in the EIP.
*
* Accounts can be notified of `IERC777` tokens being sent to them by having a
* contract implement this interface (contract holders can be their own
* implementer) and registering it on the
* [ERC1820 global registry](https://eips.ethereum.org/EIPS/eip-1820).
*
* See `IERC1820Registry` and `ERC1820Implementer`.
*/
interface IERC777Recipient {
/**
* @dev Called by an `IERC777` token contract whenever tokens are being
* moved or created into a registered account (`to`). The type of operation
* is conveyed by `from` being the zero address or not.
*
* This call occurs _after_ the token contract's state is updated, so
* `IERC777.balanceOf`, etc., can be used to query the post-operation state.
*
* This function may revert to prevent the operation from being executed.
*/
function tokensReceived(
address operator,
address from,
address to,
uint amount,
bytes calldata userData,
bytes calldata operatorData
) external;
}
// File: @openzeppelin/contracts/token/ERC777/IERC777Sender.sol
pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC777TokensSender standard as defined in the EIP.
*
* `IERC777` Token holders can be notified of operations performed on their
* tokens by having a contract implement this interface (contract holders can be
* their own implementer) and registering it on the
* [ERC1820 global registry](https://eips.ethereum.org/EIPS/eip-1820).
*
* See `IERC1820Registry` and `ERC1820Implementer`.
*/
interface IERC777Sender {
/**
* @dev Called by an `IERC777` token contract whenever a registered holder's
* (`from`) tokens are about to be moved or destroyed. The type of operation
* is conveyed by `to` being the zero address or not.
*
* This call occurs _before_ the token contract's state is updated, so
* `IERC777.balanceOf`, etc., can be used to query the pre-operation state.
*
* This function may revert to prevent the operation from being executed.
*/
function tokensToSend(
address operator,
address from,
address to,
uint amount,
bytes calldata userData,
bytes calldata operatorData
) external;
}
// File: @openzeppelin/contracts/token/ERC20/IERC20.sol
pragma solidity ^0.5.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see `ERC20Detailed`.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through `transferFrom`. This is
* zero by default.
*
* This value changes when `approve` or `transferFrom` are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* > Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an `Approval` event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a `Transfer` event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to `approve`. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: @openzeppelin/contracts/math/SafeMath.sol
pragma solidity ^0.5.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
// File: @openzeppelin/contracts/utils/Address.sol
pragma solidity ^0.5.0;
/**
* @dev Collection of functions related to the address type,
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* This test is non-exhaustive, and there may be false-negatives: during the
* execution of a contract's constructor, its address will be reported as
* not containing a contract.
*
* > It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*/
function isContract(address account) internal view returns (bool) {
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
}
// File: @openzeppelin/contracts/introspection/IERC1820Registry.sol
pragma solidity ^0.5.0;
/**
* @dev Interface of the global ERC1820 Registry, as defined in the
* [EIP](https://eips.ethereum.org/EIPS/eip-1820). Accounts may register
* implementers for interfaces in this registry, as well as query support.
*
* Implementers may be shared by multiple accounts, and can also implement more
* than a single interface for each account. Contracts can implement interfaces
* for themselves, but externally-owned accounts (EOA) must delegate this to a
* contract.
*
* `IERC165` interfaces can also be queried via the registry.
*
* For an in-depth explanation and source code analysis, see the EIP text.
*/
interface IERC1820Registry {
/**
* @dev Sets `newManager` as the manager for `account`. A manager of an
* account is able to set interface implementers for it.
*
* By default, each account is its own manager. Passing a value of `0x0` in
* `newManager` will reset the manager to this initial state.
*
* Emits a `ManagerChanged` event.
*
* Requirements:
*
* - the caller must be the current manager for `account`.
*/
function setManager(address account, address newManager) external;
/**
* @dev Returns the manager for `account`.
*
* See `setManager`.
*/
function getManager(address account) external view returns (address);
/**
* @dev Sets the `implementer` contract as `account`'s implementer for
* `interfaceHash`.
*
* `account` being the zero address is an alias for the caller's address.
* The zero address can also be used in `implementer` to remove an old one.
*
* See `interfaceHash` to learn how these are created.
*
* Emits an `InterfaceImplementerSet` event.
*
* Requirements:
*
* - the caller must be the current manager for `account`.
* - `interfaceHash` must not be an `IERC165` interface id (i.e. it must not
* end in 28 zeroes).
* - `implementer` must implement `IERC1820Implementer` and return true when
* queried for support, unless `implementer` is the caller. See
* `IERC1820Implementer.canImplementInterfaceForAddress`.
*/
function setInterfaceImplementer(address account, bytes32 interfaceHash, address implementer) external;
/**
* @dev Returns the implementer of `interfaceHash` for `account`. If no such
* implementer is registered, returns the zero address.
*
* If `interfaceHash` is an `IERC165` interface id (i.e. it ends with 28
* zeroes), `account` will be queried for support of it.
*
* `account` being the zero address is an alias for the caller's address.
*/
function getInterfaceImplementer(address account, bytes32 interfaceHash) external view returns (address);
/**
* @dev Returns the interface hash for an `interfaceName`, as defined in the
* corresponding
* [section of the EIP](https://eips.ethereum.org/EIPS/eip-1820#interface-name).
*/
function interfaceHash(string calldata interfaceName) external pure returns (bytes32);
/**
* @notice Updates the cache with whether the contract implements an ERC165 interface or not.
* @param account Address of the contract for which to update the cache.
* @param interfaceId ERC165 interface for which to update the cache.
*/
function updateERC165Cache(address account, bytes4 interfaceId) external;
/**
* @notice Checks whether a contract implements an ERC165 interface or not.
* If the result is not cached a direct lookup on the contract address is performed.
* If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
* 'updateERC165Cache' with the contract address.
* @param account Address of the contract to check.
* @param interfaceId ERC165 interface to check.
* @return True if `account.address()` implements `interfaceId`, false otherwise.
*/
function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool);
/**
* @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
* @param account Address of the contract to check.
* @param interfaceId ERC165 interface to check.
* @return True if `account.address()` implements `interfaceId`, false otherwise.
*/
function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);
event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);
event ManagerChanged(address indexed account, address indexed newManager);
}
// File: @openzeppelin/contracts/token/ERC777/ERC777.sol
pragma solidity ^0.5.0;
/**
* @dev Implementation of the `IERC777` interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using `_mint`.
*
* Support for ERC20 is included in this contract, as specified by the EIP: both
* the ERC777 and ERC20 interfaces can be safely used when interacting with it.
* Both `IERC777.Sent` and `IERC20.Transfer` events are emitted on token
* movements.
*
* Additionally, the `granularity` value is hard-coded to `1`, meaning that there
* are no special restrictions in the amount of tokens that created, moved, or
* destroyed. This makes integration with ERC20 applications seamless.
*/
contract ERC777 is IERC777, IERC20 {
using SafeMath for uint256;
using Address for address;
IERC1820Registry private _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
mapping(address => uint256) private _balances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
// We inline the result of the following hashes because Solidity doesn't resolve them at compile time.
// See https://github.com/ethereum/solidity/issues/4024.
// keccak256("ERC777TokensSender")
bytes32 constant private TOKENS_SENDER_INTERFACE_HASH =
0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895;
// keccak256("ERC777TokensRecipient")
bytes32 constant private TOKENS_RECIPIENT_INTERFACE_HASH =
0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;
// This isn't ever read from - it's only used to respond to the defaultOperators query.
address[] private _defaultOperatorsArray;
// Immutable, but accounts may revoke them (tracked in __revokedDefaultOperators).
mapping(address => bool) private _defaultOperators;
// For each account, a mapping of its operators and revoked default operators.
mapping(address => mapping(address => bool)) private _operators;
mapping(address => mapping(address => bool)) private _revokedDefaultOperators;
// ERC20-allowances
mapping (address => mapping (address => uint256)) private _allowances;
/**
* @dev `defaultOperators` may be an empty array.
*/
constructor(
string memory name,
string memory symbol,
address[] memory defaultOperators
) public {
_name = name;
_symbol = symbol;
_defaultOperatorsArray = defaultOperators;
for (uint256 i = 0; i < _defaultOperatorsArray.length; i++) {
_defaultOperators[_defaultOperatorsArray[i]] = true;
}
// register interfaces
//_erc1820.setInterfaceImplementer(address(this), keccak256("ERC777Token"), address(this));
//_erc1820.setInterfaceImplementer(address(this), keccak256("ERC20Token"), address(this));
}
/**
* @dev See `IERC777.name`.
*/
function name() public view returns (string memory) {
return _name;
}
/**
* @dev See `IERC777.symbol`.
*/
function symbol() public view returns (string memory) {
return _symbol;
}
/**
* @dev See `ERC20Detailed.decimals`.
*
* Always returns 18, as per the
* [ERC777 EIP](https://eips.ethereum.org/EIPS/eip-777#backward-compatibility).
*/
function decimals() public pure returns (uint8) {
return 18;
}
/**
* @dev See `IERC777.granularity`.
*
* This implementation always returns `1`.
*/
function granularity() public view returns (uint256) {
return 1;
}
/**
* @dev See `IERC777.totalSupply`.
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev Returns the amount of tokens owned by an account (`tokenHolder`).
*/
function balanceOf(address tokenHolder) public view returns (uint256) {
return _balances[tokenHolder];
}
/**
* @dev See `IERC777.send`.
*
* Also emits a `Transfer` event for ERC20 compatibility.
*/
function send(address recipient, uint256 amount, bytes calldata data) external {
_send(msg.sender, msg.sender, recipient, amount, data, "", true);
}
/**
* @dev See `IERC20.transfer`.
*
* Unlike `send`, `recipient` is _not_ required to implement the `tokensReceived`
* interface if it is a contract.
*
* Also emits a `Sent` event.
*/
function transfer(address recipient, uint256 amount) external returns (bool) {
require(recipient != address(0), "ERC777: transfer to the zero address");
address from = msg.sender;
_callTokensToSend(from, from, recipient, amount, "", "");
_move(from, from, recipient, amount, "", "");
_callTokensReceived(from, from, recipient, amount, "", "", false);
return true;
}
/**
* @dev See `IERC777.burn`.
*
* Also emits a `Transfer` event for ERC20 compatibility.
*/
function burn(uint256 amount, bytes calldata data) external {
_burn(msg.sender, msg.sender, amount, data, "");
}
/**
* @dev See `IERC777.isOperatorFor`.
*/
function isOperatorFor(
address operator,
address tokenHolder
) public view returns (bool) {
return operator == tokenHolder ||
(_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) ||
_operators[tokenHolder][operator];
}
/**
* @dev See `IERC777.authorizeOperator`.
*/
function authorizeOperator(address operator) external {
require(msg.sender != operator, "ERC777: authorizing self as operator");
if (_defaultOperators[operator]) {
delete _revokedDefaultOperators[msg.sender][operator];
} else {
_operators[msg.sender][operator] = true;
}
emit AuthorizedOperator(operator, msg.sender);
}
/**
* @dev See `IERC777.revokeOperator`.
*/
function revokeOperator(address operator) external {
require(operator != msg.sender, "ERC777: revoking self as operator");
if (_defaultOperators[operator]) {
_revokedDefaultOperators[msg.sender][operator] = true;
} else {
delete _operators[msg.sender][operator];
}
emit RevokedOperator(operator, msg.sender);
}
/**
* @dev See `IERC777.defaultOperators`.
*/
function defaultOperators() public view returns (address[] memory) {
return _defaultOperatorsArray;
}
/**
* @dev See `IERC777.operatorSend`.
*
* Emits `Sent` and `Transfer` events.
*/
function operatorSend(
address sender,
address recipient,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
)
external
{
require(isOperatorFor(msg.sender, sender), "ERC777: caller is not an operator for holder");
_send(msg.sender, sender, recipient, amount, data, operatorData, true);
}
/**
* @dev See `IERC777.operatorBurn`.
*
* Emits `Sent` and `Transfer` events.
*/
function operatorBurn(address account, uint256 amount, bytes calldata data, bytes calldata operatorData) external {
require(isOperatorFor(msg.sender, account), "ERC777: caller is not an operator for holder");
_burn(msg.sender, account, amount, data, operatorData);
}
/**
* @dev See `IERC20.allowance`.
*
* Note that operator and allowance concepts are orthogonal: operators may
* not have allowance, and accounts with allowance may not be operators
* themselves.
*/
function allowance(address holder, address spender) public view returns (uint256) {
return _allowances[holder][spender];
}
/**
* @dev See `IERC20.approve`.
*
* Note that accounts cannot have allowance issued by their operators.
*/
function approve(address spender, uint256 value) external returns (bool) {
address holder = msg.sender;
_approve(holder, spender, value);
return true;
}
/**
* @dev See `IERC20.transferFrom`.
*
* Note that operator and allowance concepts are orthogonal: operators cannot
* call `transferFrom` (unless they have allowance), and accounts with
* allowance cannot call `operatorSend` (unless they are operators).
*
* Emits `Sent` and `Transfer` events.
*/
function transferFrom(address holder, address recipient, uint256 amount) external returns (bool) {
require(recipient != address(0), "ERC777: transfer to the zero address");
require(holder != address(0), "ERC777: transfer from the zero address");
address spender = msg.sender;
_callTokensToSend(spender, holder, recipient, amount, "", "");
_move(spender, holder, recipient, amount, "", "");
_approve(holder, spender, _allowances[holder][spender].sub(amount));
_callTokensReceived(spender, holder, recipient, amount, "", "", false);
return true;
}
/**
* @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* If a send hook is registered for `raccount`, the corresponding function
* will be called with `operator`, `data` and `operatorData`.
*
* See `IERC777Sender` and `IERC777Recipient`.
*
* Emits `Sent` and `Transfer` events.
*
* Requirements
*
* - `account` cannot be the zero address.
* - if `account` is a contract, it must implement the `tokensReceived`
* interface.
*/
function _mint(
address operator,
address account,
uint256 amount,
bytes memory userData,
bytes memory operatorData
)
internal
{
require(account != address(0), "ERC777: mint to the zero address");
// Update state variables
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
_callTokensReceived(operator, address(0), account, amount, userData, operatorData, true);
emit Minted(operator, account, amount, userData, operatorData);
emit Transfer(address(0), account, amount);
}
/**
* @dev Send tokens
* @param operator address operator requesting the transfer
* @param from address token holder address
* @param to address recipient address
* @param amount uint256 amount of tokens to transfer
* @param userData bytes extra information provided by the token holder (if any)
* @param operatorData bytes extra information provided by the operator (if any)
* @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient
*/
function _send(
address operator,
address from,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData,
bool requireReceptionAck
)
private
{
require(from != address(0), "ERC777: send from the zero address");
require(to != address(0), "ERC777: send to the zero address");
_callTokensToSend(operator, from, to, amount, userData, operatorData);
_move(operator, from, to, amount, userData, operatorData);
_callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck);
}
/**
* @dev Burn tokens
* @param operator address operator requesting the operation
* @param from address token holder address
* @param amount uint256 amount of tokens to burn
* @param data bytes extra information provided by the token holder
* @param operatorData bytes extra information provided by the operator (if any)
*/
function _burn(
address operator,
address from,
uint256 amount,
bytes memory data,
bytes memory operatorData
)
private
{
require(from != address(0), "ERC777: burn from the zero address");
_callTokensToSend(operator, from, address(0), amount, data, operatorData);
// Update state variables
_totalSupply = _totalSupply.sub(amount);
_balances[from] = _balances[from].sub(amount);
emit Burned(operator, from, amount, data, operatorData);
emit Transfer(from, address(0), amount);
}
function _move(
address operator,
address from,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData
)
private
{
_balances[from] = _balances[from].sub(amount);
_balances[to] = _balances[to].add(amount);
emit Sent(operator, from, to, amount, userData, operatorData);
emit Transfer(from, to, amount);
}
function _approve(address holder, address spender, uint256 value) private {
// TODO: restore this require statement if this function becomes internal, or is called at a new callsite. It is
// currently unnecessary.
//require(holder != address(0), "ERC777: approve from the zero address");
require(spender != address(0), "ERC777: approve to the zero address");
_allowances[holder][spender] = value;
emit Approval(holder, spender, value);
}
/**
* @dev Call from.tokensToSend() if the interface is registered
* @param operator address operator requesting the transfer
* @param from address token holder address
* @param to address recipient address
* @param amount uint256 amount of tokens to transfer
* @param userData bytes extra information provided by the token holder (if any)
* @param operatorData bytes extra information provided by the operator (if any)
*/
function _callTokensToSend(
address operator,
address from,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData
)
private
{
address implementer = _erc1820.getInterfaceImplementer(from, TOKENS_SENDER_INTERFACE_HASH);
if (implementer != address(0)) {
IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData);
}
}
/**
* @dev Call to.tokensReceived() if the interface is registered. Reverts if the recipient is a contract but
* tokensReceived() was not registered for the recipient
* @param operator address operator requesting the transfer
* @param from address token holder address
* @param to address recipient address
* @param amount uint256 amount of tokens to transfer
* @param userData bytes extra information provided by the token holder (if any)
* @param operatorData bytes extra information provided by the operator (if any)
* @param requireReceptionAck if true, contract recipients are required to implement ERC777TokensRecipient
*/
function _callTokensReceived(
address operator,
address from,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData,
bool requireReceptionAck
)
private
{
address implementer = _erc1820.getInterfaceImplementer(to, TOKENS_RECIPIENT_INTERFACE_HASH);
if (implementer != address(0)) {
IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData);
} else if (requireReceptionAck) {
require(!to.isContract(), "ERC777: token recipient contract has no implementer for ERC777TokensRecipient");
}
}
}
// File: @openzeppelin/contracts/access/Roles.sol
pragma solidity ^0.5.0;
/**
* @title Roles
* @dev Library for managing addresses assigned to a Role.
*/
library Roles {
struct Role {
mapping (address => bool) bearer;
}
/**
* @dev Give an account access to this role.
*/
function add(Role storage role, address account) internal {
require(!has(role, account), "Roles: account already has role");
role.bearer[account] = true;
}
/**
* @dev Remove an account's access to this role.
*/
function remove(Role storage role, address account) internal {
require(has(role, account), "Roles: account does not have role");
role.bearer[account] = false;
}
/**
* @dev Check if an account has this role.
* @return bool
*/
function has(Role storage role, address account) internal view returns (bool) {
require(account != address(0), "Roles: account is the zero address");
return role.bearer[account];
}
}
// File: @openzeppelin/contracts/access/roles/MinterRole.sol
pragma solidity ^0.5.0;
contract MinterRole {
using Roles for Roles.Role;
event MinterAdded(address indexed account);
event MinterRemoved(address indexed account);
Roles.Role private _minters;
constructor () internal {
_addMinter(msg.sender);
}
modifier onlyMinter() {
require(isMinter(msg.sender), "MinterRole: caller does not have the Minter role");
_;
}
function isMinter(address account) public view returns (bool) {
return _minters.has(account);
}
function addMinter(address account) public onlyMinter {
_addMinter(account);
}
function renounceMinter() public {
_removeMinter(msg.sender);
}
function _addMinter(address account) internal {
_minters.add(account);
emit MinterAdded(account);
}
function _removeMinter(address account) internal {
_minters.remove(account);
emit MinterRemoved(account);
}
}
// File: contracts/TaurusToken.sol
pragma solidity ^0.5.0;
contract TaurusToken is ERC777, MinterRole {
uint256 private _cap;
constructor (uint256 cap, address[] memory defaultOperators) ERC777("Taurus Token", "TAU", defaultOperators) public {
require(cap > 0, "TaurusToken: cap is 0");
_cap = cap;
}
function mint(address account, uint256 value) onlyMinter external {
require(totalSupply().add(value) <= _cap, "TaurusToken: cap exceeded");
super._mint(msg.sender, account, value, "", "");
}
}
/**
*Submitted for verification at Etherscan.io on 2017-11-28
*/
pragma solidity >=0.4.21 <0.6.0;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
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;
}
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;
}
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;
}
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
if (newOwner != address(0)) {
owner = newOwner;
}
}
}
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20Basic {
uint public _totalSupply;
function totalSupply() public view returns (uint);
function balanceOf(address who) public view returns (uint);
function transfer(address to, uint value) public;
event Transfer(address indexed from, address indexed to, uint 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 (uint);
function transferFrom(address from, address to, uint value) public;
function approve(address spender, uint value) public;
event Approval(address indexed owner, address indexed spender, uint value);
}
/**
* @title Basic token
* @dev Basic version of StandardToken, with no allowances.
*/
contract BasicToken is Ownable, ERC20Basic {
using SafeMath for uint;
mapping(address => uint) public balances;
// additional variables for use if transaction fees ever became necessary
uint public basisPointsRate = 0;
uint public maximumFee = 0;
/**
* @dev Fix for the ERC20 short address attack.
*/
modifier onlyPayloadSize(uint size) {
require(!(msg.data.length < size + 4));
_;
}
/**
* @dev transfer token for a specified address
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
*/
function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
uint fee = (_value.mul(basisPointsRate)).div(10000);
if (fee > maximumFee) {
fee = maximumFee;
}
uint sendAmount = _value.sub(fee);
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(sendAmount);
if (fee > 0) {
balances[owner] = balances[owner].add(fee);
emit Transfer(msg.sender, owner, fee);
}
emit Transfer(msg.sender, _to, sendAmount);
}
/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the the balance of.
* @return An uint representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public view returns (uint balance) {
return balances[_owner];
}
}
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* @dev https://github.com/ethereum/EIPs/issues/20
* @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken is BasicToken, ERC20 {
mapping (address => mapping (address => uint)) public allowed;
uint public constant MAX_UINT = 2**256 - 1;
/**
* @dev Transfer tokens from one address to another
* @param _from address The address which you want to send tokens from
* @param _to address The address which you want to transfer to
* @param _value uint the amount of tokens to be transferred
*/
function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
uint256 _allowance = allowed[_from][msg.sender];
// Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
// if (_value > _allowance) throw;
uint fee = (_value.mul(basisPointsRate)).div(10000);
if (fee > maximumFee) {
fee = maximumFee;
}
if (_allowance < MAX_UINT) {
allowed[_from][msg.sender] = _allowance.sub(_value);
}
uint sendAmount = _value.sub(fee);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(sendAmount);
if (fee > 0) {
balances[owner] = balances[owner].add(fee);
emit Transfer(_from, owner, fee);
}
emit Transfer(_from, _to, sendAmount);
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
// 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) && (allowed[msg.sender][_spender] != 0)));
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
}
/**
* @dev Function to check the amount of tokens than an owner allowed to a spender.
* @param _owner address The address which owns the funds.
* @param _spender address The address which will spend the funds.
* @return A uint specifying the amount of tokens still available for the spender.
*/
function allowance(address _owner, address _spender) public view returns (uint remaining) {
return allowed[_owner][_spender];
}
}
/**
* @title Pausable
* @dev Base contract which allows children to implement an emergency stop mechanism.
*/
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!paused);
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*/
modifier whenPaused() {
require(paused);
_;
}
/**
* @dev called by the owner to pause, triggers stopped state
*/
function pause() onlyOwner whenNotPaused public {
paused = true;
emit Pause();
}
/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() onlyOwner whenPaused public {
paused = false;
emit Unpause();
}
}
contract BlackList is Ownable, BasicToken {
/////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) ///////
function getBlackListStatus(address _maker) external view returns (bool) {
return isBlackListed[_maker];
}
function getOwner() external view returns (address) {
return owner;
}
mapping (address => bool) public isBlackListed;
function addBlackList (address _evilUser) public onlyOwner {
isBlackListed[_evilUser] = true;
emit AddedBlackList(_evilUser);
}
function removeBlackList (address _clearedUser) public onlyOwner {
isBlackListed[_clearedUser] = false;
emit RemovedBlackList(_clearedUser);
}
function destroyBlackFunds (address _blackListedUser) public onlyOwner {
require(isBlackListed[_blackListedUser]);
uint dirtyFunds = balanceOf(_blackListedUser);
balances[_blackListedUser] = 0;
_totalSupply -= dirtyFunds;
emit DestroyedBlackFunds(_blackListedUser, dirtyFunds);
}
event DestroyedBlackFunds(address _blackListedUser, uint _balance);
event AddedBlackList(address _user);
event RemovedBlackList(address _user);
}
contract UpgradedStandardToken is StandardToken{
// those methods are called by the legacy contract
// and they must ensure msg.sender to be the contract address
function transferByLegacy(address from, address to, uint value) public;
function transferFromByLegacy(address sender, address from, address spender, uint value) public;
function approveByLegacy(address from, address spender, uint value) public;
}
contract TetherToken is Pausable, StandardToken, BlackList {
string public name;
string public symbol;
uint public decimals;
address public upgradedAddress;
bool public deprecated;
// The contract can be initialized with a number of tokens
// All the tokens are deposited to the owner address
//
// @param _balance Initial supply of the contract
// @param _name Token Name
// @param _symbol Token symbol
// @param _decimals Token decimals
constructor(uint _initialSupply, string memory _name, string memory _symbol, uint _decimals) public {
_totalSupply = _initialSupply;
name = _name;
symbol = _symbol;
decimals = _decimals;
balances[owner] = _initialSupply;
deprecated = false;
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function transfer(address _to, uint _value) public whenNotPaused {
require(!isBlackListed[msg.sender]);
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
} else {
return super.transfer(_to, _value);
}
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function transferFrom(address _from, address _to, uint _value) public whenNotPaused {
require(!isBlackListed[_from]);
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value);
} else {
return super.transferFrom(_from, _to, _value);
}
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function balanceOf(address who) public view returns (uint) {
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).balanceOf(who);
} else {
return super.balanceOf(who);
}
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value);
} else {
return super.approve(_spender, _value);
}
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function allowance(address _owner, address _spender) public view returns (uint remaining) {
if (deprecated) {
return StandardToken(upgradedAddress).allowance(_owner, _spender);
} else {
return super.allowance(_owner, _spender);
}
}
// deprecate current contract in favour of a new one
function deprecate(address _upgradedAddress) public onlyOwner {
deprecated = true;
upgradedAddress = _upgradedAddress;
emit Deprecate(_upgradedAddress);
}
// deprecate current contract if favour of a new one
function totalSupply() public view returns (uint) {
if (deprecated) {
return StandardToken(upgradedAddress).totalSupply();
} else {
return _totalSupply;
}
}
// Issue a new amount of tokens
// these tokens are deposited into the owner address
//
// @param _amount Number of tokens to be issued
function issue(uint amount) public onlyOwner {
require(_totalSupply + amount > _totalSupply);
require(balances[owner] + amount > balances[owner]);
balances[owner] += amount;
_totalSupply += amount;
emit Issue(amount);
}
// Redeem tokens.
// These tokens are withdrawn from the owner address
// if the balance must be enough to cover the redeem
// or the call will fail.
// @param _amount Number of tokens to be issued
function redeem(uint amount) public onlyOwner {
require(_totalSupply >= amount);
require(balances[owner] >= amount);
_totalSupply -= amount;
balances[owner] -= amount;
emit Redeem(amount);
}
function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner {
// Ensure transparency by hardcoding limit beyond which fees can never be added
require(newBasisPoints < 20);
require(newMaxFee < 50);
basisPointsRate = newBasisPoints;
maximumFee = newMaxFee.mul(10**decimals);
emit Params(basisPointsRate, maximumFee);
}
// Called when new token are issued
event Issue(uint amount);
// Called when tokens are redeemed
event Redeem(uint amount);
// Called when contract is deprecated
event Deprecate(address newAddress);
// Called if contract ever adds fees
event Params(uint feeBasisPoints, uint maxFee);
}
// File: contracts/TetherToken.sol
pragma solidity >=0.4.21 <0.6.0;
contract TetherToken {
uint public _totalSupply;
function totalSupply() public view returns (uint);
function balanceOf(address who) public view returns (uint);
function transfer(address to, uint value) public;
function allowance(address owner, address spender) public view returns (uint);
function transferFrom(address from, address to, uint value) public;
function approve(address spender, uint value) public;
}
// File: contracts/SafeMath.sol
pragma solidity ^0.5.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
// File: contracts/TetherWallet.sol
pragma solidity >=0.4.21 <0.6.0;
/**
* @dev 任何人都可以将自己的 ERC20 USDT 转入转出
*/
contract TetherWallet {
using SafeMath for uint256;
TetherToken public usdtToken;
mapping(address => uint256) private balances;
event Received(address indexed sender, uint256 amount);
event Withdrawn(address indexed to, uint256 value);
constructor(address tetherToken) public {
usdtToken = TetherToken(tetherToken);
}
/**
* @dev 存入一定数量的 ERC20 USDT
* 在调用此方法钱,请先调用 ERC20 USDT 合约的 approve 方法进行授权
*/
function deposit(uint256 usdtWeiAmount) public {
require(usdtToken.allowance(msg.sender, address(this)) >= usdtWeiAmount);
usdtToken.transferFrom(msg.sender, address(this), usdtWeiAmount);
balances[msg.sender] = balances[msg.sender].add(usdtWeiAmount);
emit Received(msg.sender, usdtWeiAmount);
}
/**
* @dev 将一定数量的 ERC20 USDT 转出到指定地址
*/
function withdraw(address to, uint256 usdtWeiAmount) public {
require(balances[msg.sender] > usdtWeiAmount);
balances[msg.sender] = balances[msg.sender].sub(usdtWeiAmount);
usdtToken.transfer(to, usdtWeiAmount);
emit Withdrawn(to, usdtWeiAmount);
}
/**
* @dev 将一定数量的 ERC20 USDT 提现到自己钱包
*/
function withdraw(uint256 usdtWeiAmount) public {
require(balances[msg.sender] > usdtWeiAmount);
withdraw(msg.sender, usdtWeiAmount);
}
function balanceOf(address account) public view returns (uint256) {
return balances[account];
}
}
pragma solidity ^0.6.0;
contract Token {
function balanceOf(address _owner) public returns (uint balance) {}
function transfer(address _to, uint _value) public returns (bool success) {}
function transferFrom(address _from, address _to, uint _value) public returns (bool success) {}
function approve(address _spender, uint _value) public returns (bool success) {}
function allowance(address _owner, address _spender) public returns (uint remaining) {}
event Transfer(address indexed _from, address indexed _to, uint _value);
event Approval(address indexed _owner, address indexed _spender, uint _value);
}
contract TokenTransfer {
Token public token;
struct User {
uint256 id;
}
constructor(address tokenAddress) public {
token = Token(tokenAddress);
}
function transferOut(address receipt, uint256 amount) public {
token.transfer(receipt, amount);
User memory user = User(2020001);
}
}
/**
*Submitted for verification at Etherscan.io on 2017-11-28
*/
pragma solidity ^0.4.17;
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
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;
}
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;
}
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;
}
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
if (newOwner != address(0)) {
owner = newOwner;
}
}
}
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20Basic {
uint public _totalSupply;
function totalSupply() public constant returns (uint);
function balanceOf(address who) public constant returns (uint);
function transfer(address to, uint value) public;
event Transfer(address indexed from, address indexed to, uint value);
}
/**
* @title ERC20 interface
* @dev see https://github.com/ethereum/EIPs/issues/20
*/
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public constant returns (uint);
function transferFrom(address from, address to, uint value) public;
function approve(address spender, uint value) public;
event Approval(address indexed owner, address indexed spender, uint value);
}
/**
* @title Basic token
* @dev Basic version of StandardToken, with no allowances.
*/
contract BasicToken is Ownable, ERC20Basic {
using SafeMath for uint;
mapping(address => uint) public balances;
// additional variables for use if transaction fees ever became necessary
uint public basisPointsRate = 0;
uint public maximumFee = 0;
/**
* @dev Fix for the ERC20 short address attack.
*/
modifier onlyPayloadSize(uint size) {
require(!(msg.data.length < size + 4));
_;
}
/**
* @dev transfer token for a specified address
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
*/
function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
uint fee = (_value.mul(basisPointsRate)).div(10000);
if (fee > maximumFee) {
fee = maximumFee;
}
uint sendAmount = _value.sub(fee);
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(sendAmount);
if (fee > 0) {
balances[owner] = balances[owner].add(fee);
Transfer(msg.sender, owner, fee);
}
Transfer(msg.sender, _to, sendAmount);
}
/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the the balance of.
* @return An uint representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public constant returns (uint balance) {
return balances[_owner];
}
}
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* @dev https://github.com/ethereum/EIPs/issues/20
* @dev Based oncode by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken is BasicToken, ERC20 {
mapping (address => mapping (address => uint)) public allowed;
uint public constant MAX_UINT = 2**256 - 1;
/**
* @dev Transfer tokens from one address to another
* @param _from address The address which you want to send tokens from
* @param _to address The address which you want to transfer to
* @param _value uint the amount of tokens to be transferred
*/
function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
var _allowance = allowed[_from][msg.sender];
// Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
// if (_value > _allowance) throw;
uint fee = (_value.mul(basisPointsRate)).div(10000);
if (fee > maximumFee) {
fee = maximumFee;
}
if (_allowance < MAX_UINT) {
allowed[_from][msg.sender] = _allowance.sub(_value);
}
uint sendAmount = _value.sub(fee);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(sendAmount);
if (fee > 0) {
balances[owner] = balances[owner].add(fee);
Transfer(_from, owner, fee);
}
Transfer(_from, _to, sendAmount);
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
// 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) && (allowed[msg.sender][_spender] != 0)));
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
}
/**
* @dev Function to check the amount of tokens than an owner allowed to a spender.
* @param _owner address The address which owns the funds.
* @param _spender address The address which will spend the funds.
* @return A uint specifying the amount of tokens still available for the spender.
*/
function allowance(address _owner, address _spender) public constant returns (uint remaining) {
return allowed[_owner][_spender];
}
}
/**
* @title Pausable
* @dev Base contract which allows children to implement an emergency stop mechanism.
*/
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!paused);
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*/
modifier whenPaused() {
require(paused);
_;
}
/**
* @dev called by the owner to pause, triggers stopped state
*/
function pause() onlyOwner whenNotPaused public {
paused = true;
Pause();
}
/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() onlyOwner whenPaused public {
paused = false;
Unpause();
}
}
contract BlackList is Ownable, BasicToken {
/////// Getters to allow the same blacklist to be used also by other contracts (including upgraded Tether) ///////
function getBlackListStatus(address _maker) external constant returns (bool) {
return isBlackListed[_maker];
}
function getOwner() external constant returns (address) {
return owner;
}
mapping (address => bool) public isBlackListed;
function addBlackList (address _evilUser) public onlyOwner {
isBlackListed[_evilUser] = true;
AddedBlackList(_evilUser);
}
function removeBlackList (address _clearedUser) public onlyOwner {
isBlackListed[_clearedUser] = false;
RemovedBlackList(_clearedUser);
}
function destroyBlackFunds (address _blackListedUser) public onlyOwner {
require(isBlackListed[_blackListedUser]);
uint dirtyFunds = balanceOf(_blackListedUser);
balances[_blackListedUser] = 0;
_totalSupply -= dirtyFunds;
DestroyedBlackFunds(_blackListedUser, dirtyFunds);
}
event DestroyedBlackFunds(address _blackListedUser, uint _balance);
event AddedBlackList(address _user);
event RemovedBlackList(address _user);
}
contract UpgradedStandardToken is StandardToken{
// those methods are called by the legacy contract
// and they must ensure msg.sender to be the contract address
function transferByLegacy(address from, address to, uint value) public;
function transferFromByLegacy(address sender, address from, address spender, uint value) public;
function approveByLegacy(address from, address spender, uint value) public;
}
contract TetherToken is Pausable, StandardToken, BlackList {
string public name;
string public symbol;
uint public decimals;
address public upgradedAddress;
bool public deprecated;
// The contract can be initialized with a number of tokens
// All the tokens are deposited to the owner address
//
// @param _balance Initial supply of the contract
// @param _name Token Name
// @param _symbol Token symbol
// @param _decimals Token decimals
function TetherToken(uint _initialSupply, string _name, string _symbol, uint _decimals) public {
_totalSupply = _initialSupply;
name = _name;
symbol = _symbol;
decimals = _decimals;
balances[owner] = _initialSupply;
deprecated = false;
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function transfer(address _to, uint _value) public whenNotPaused {
require(!isBlackListed[msg.sender]);
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
} else {
return super.transfer(_to, _value);
}
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function transferFrom(address _from, address _to, uint _value) public whenNotPaused {
require(!isBlackListed[_from]);
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value);
} else {
return super.transferFrom(_from, _to, _value);
}
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function balanceOf(address who) public constant returns (uint) {
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).balanceOf(who);
} else {
return super.balanceOf(who);
}
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
if (deprecated) {
return UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value);
} else {
return super.approve(_spender, _value);
}
}
// Forward ERC20 methods to upgraded contract if this one is deprecated
function allowance(address _owner, address _spender) public constant returns (uint remaining) {
if (deprecated) {
return StandardToken(upgradedAddress).allowance(_owner, _spender);
} else {
return super.allowance(_owner, _spender);
}
}
// deprecate current contract in favour of a new one
function deprecate(address _upgradedAddress) public onlyOwner {
deprecated = true;
upgradedAddress = _upgradedAddress;
Deprecate(_upgradedAddress);
}
// deprecate current contract if favour of a new one
function totalSupply() public constant returns (uint) {
if (deprecated) {
return StandardToken(upgradedAddress).totalSupply();
} else {
return _totalSupply;
}
}
// Issue a new amount of tokens
// these tokens are deposited into the owner address
//
// @param _amount Number of tokens to be issued
function issue(uint amount) public onlyOwner {
require(_totalSupply + amount > _totalSupply);
require(balances[owner] + amount > balances[owner]);
balances[owner] += amount;
_totalSupply += amount;
Issue(amount);
}
// Redeem tokens.
// These tokens are withdrawn from the owner address
// if the balance must be enough to cover the redeem
// or the call will fail.
// @param _amount Number of tokens to be issued
function redeem(uint amount) public onlyOwner {
require(_totalSupply >= amount);
require(balances[owner] >= amount);
_totalSupply -= amount;
balances[owner] -= amount;
Redeem(amount);
}
function setParams(uint newBasisPoints, uint newMaxFee) public onlyOwner {
// Ensure transparency by hardcoding limit beyond which fees can never be added
require(newBasisPoints < 20);
require(newMaxFee < 50);
basisPointsRate = newBasisPoints;
maximumFee = newMaxFee.mul(10**decimals);
Params(basisPointsRate, maximumFee);
}
// Called when new token are issued
event Issue(uint amount);
// Called when tokens are redeemed
event Redeem(uint amount);
// Called when contract is deprecated
event Deprecate(address newAddress);
// Called if contract ever adds fees
event Params(uint feeBasisPoints, uint maxFee);
}
pragma solidity ^0.6.1;
import "./CloneFactory.sol";
contract WalletFactory is CloneFactory {
address Template = 0x692a70D2e424a56D2C6C27aA97D1a86395877b3A;
function createWallet() external returns (address newWallet) {
newWallet = createClone(Template);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment