pragma solidity ^0.5.8;
/// @title ERC20 Token Interface
/// @author Hoard Team
/// @notice See https://github.com/ethereum/EIPs/issues/20
contract ERC20Token {
// PUBLIC INTERFACE
// /// @dev Returns total amount of tokens
// /// @notice params -> (uint256 totalSupply)
// It's implamented as a variable which doesn't override this method. Commented to prevent compilation error.
// function totalSupply () constant public returns (uint256);
/// @dev Returns balance of specified account
/// @notice params -> (address _owner)
function balanceOf (address) view public returns (uint256);
/// @dev Transfers tokens from msg.sender to a specified address
/// @notice params -> (address _to, uint256 _value)
function transfer (address, uint256) public returns (bool);
/// @dev Allowance mechanism - delegated transfer
/// @notice params -> (address _from, address _to, uint256 _value)
function transferFrom (address, address, uint256) public returns (bool);
/// @dev Allowance mechanism - approve delegated transfer
/// @notice params -> (address _spender, uint256 _value)
function approve (address, uint256) public returns (bool);
/// @dev Allowance mechanism - set allowance for specified address
/// @notice params -> (address _owner, address _spender)
function allowance (address, address) public view returns (uint256);
// EVENTS
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}
/// @title Safe Math
/// @author Open Zeppelin
/// @notice implementation from - https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/math/SafeMath.sol
library SafeMath {
function safeMul(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a * b;
assert(a == 0 || c / a == b);
return c;
}
function safeDiv(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 safeSub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function safeAdd(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
function max256(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
function min256(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
}
/// @title Standard ERC20 compliant token
/// @author Hoard Team
/// @notice Original taken from https://github.com/ethereum/EIPs/issues/20
/// @notice SafeMath used as specified by OpenZeppelin
/// @notice Comments and additional approval code from https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token
contract StandardToken is ERC20Token {
using SafeMath for uint256;
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
uint256 public 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) public returns (bool) {
balances[msg.sender] = balances[msg.sender].safeSub(_value);
balances[_to] = balances[_to].safeAdd(_value);
emit Transfer(msg.sender, _to, _value);
return true;
}
/// @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 returns (bool) {
uint256 _allowance = allowed[_from][msg.sender];
// Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
// require (_value <= _allowance); if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]) {
balances[_to] = balances[_to].safeAdd(_value);
balances[_from] = balances[_from].safeSub(_value);
allowed[_from][msg.sender] = _allowance.safeSub(_value);
emit Transfer(_from, _to, _value);
return true;
}
/// @dev Gets the balance of the specified address.
/// @param _owner The address to query the the balance of.
/// @return An uint256 representing the amount owned by the passed address.
function balanceOf(address _owner) view public returns (uint256) {
return balances[_owner];
}
/// @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, uint256 _value) public returns (bool) {
// 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);
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) view public returns (uint256) {
return allowed[_owner][_spender];
}
/// @notice approve should be called when allowed[_spender] == 0. To increment
/// allowed value it is better to use this function to avoid 2 calls (and wait until
/// the first transaction is mined)
function increaseApproval (address _spender, uint256 _addedValue) public returns (bool) {
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].safeAdd(_addedValue);
emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
/// @notice approve should be called when allowed[_spender] == 0. To decrement
/// allowed value it is better to use this function to avoid 2 calls (and wait until
/// the first transaction is mined)
function decreaseApproval (address _spender, uint256 _subtractedValue) public returns (bool) {
uint256 oldValue = allowed[msg.sender][_spender];
if (_subtractedValue > oldValue) {
allowed[msg.sender][_spender] = 0;
} else {
allowed[msg.sender][_spender] = oldValue - _subtractedValue;
}
emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
}
/// @title Migration Agent interface
/// @author Hoard Team
/// @notice Based on GNT implementation - https://github.com/golemfactory/golem-crowdfunding/blob/master/contracts/Token.sol
contract MigrationAgent {
/// @dev migrates tokens or other "assets" from one contract to another (not yet specified)
/// @notice parameters -> (address _from, uint _value)
function migrateFrom(address, uint256) public;
}
/// @title Mintable token interface
/// @author Hoard Team
contract Mintable {
/// @dev Mint new tokens
/// @notice params -> (address _recipient, uint256 _amount)
function mintTokens (address, uint256) public;
}
/// @title Migratable entity interface
/// @author Hoard Team
contract Migratable {
/// @dev Migrates tokens for msg.sender
/// @notice params -> (uint256 _value)
function migrate (uint256) public;
// EVENTS
event Migrate (address indexed _from, address indexed _to, uint256 _value);
}
/// @title Standard ERC20 compliant token
/// @author Hoard Team
contract ExtendedStandardToken is StandardToken, Migratable, Mintable {
address public migrationAgent;
uint256 public totalMigrated;
// MODIFIERS
modifier migrationAgentSet {
require(migrationAgent != address(0));
_;
}
modifier migrationAgentNotSet {
require(migrationAgent == address(0));
_;
}
/// @dev Internal constructor to prevent bare instances of this contract
constructor () internal {
}
// MIGRATION LOGIC
/// @dev Migrates tokens for msg.sender and burns them
/// @param _value amount of tokens to migrate
function migrate (uint256 _value) public {
// Validate input value
require(_value > 0);
//require(_value <= balances[msg.sender]);
//not necessary as safeSub throws in case the above condition does not hold
balances[msg.sender] = balances[msg.sender].safeSub(_value);
totalSupply = totalSupply.safeSub(_value);
totalMigrated = totalMigrated.safeAdd(_value);
MigrationAgent(migrationAgent).migrateFrom(msg.sender, _value);
emit Migrate(msg.sender, migrationAgent, _value);
}
// MINTING LOGIC
/// @dev Mints additional tokens
/// @param _recipient owner of new tokens
/// @param _amount amount of tokens to mint
function mintTokens (address _recipient, uint256 _amount) public {
require(_amount > 0);
balances[_recipient] = balances[_recipient].safeAdd(_amount);
totalSupply = totalSupply.safeAdd(_amount);
// Log token creation event
emit Transfer(address(0), msg.sender, _amount);
}
// CONTROL LOGIC
/// @dev Sets address of a new migration agent
/// @param _address address of new migration agent
function setMigrationAgent (address _address) public {
migrationAgent = _address;
}
}
/// @title Hoard Token (HRD) - crowdfunding code for Hoard token
/// @author Hoard Team
/// @notice Based on MLN implementation - https://github.com/melonproject/melon/blob/master/contracts/tokens/MelonToken.sol
/// @notice Based on GNT implementation - https://github.com/golemfactory/golem-crowdfunding/blob/master/contracts/Token.sol
contract HoardToken is ExtendedStandardToken {
// Token description fields
string public constant name = "Hoard Token";
string public constant symbol = "HRD";
uint256 public constant decimals = 18; // 18 decimal places, the same as ETH
// contract supervision variables
address public creator;
address public hoard;
address public migrationMaster;
// MODIFIERS
modifier onlyCreator {
require(msg.sender == creator);
_;
}
modifier onlyHoard {
require(msg.sender == hoard);
_;
}
modifier onlyMigrationMaster {
require(msg.sender == migrationMaster);
_;
}
// CONSTRUCTION
/// @param _hoard Hoard multisig contract
/// @param _migrationMaster migration master
constructor (address _hoard, address _migrationMaster) public {
require(_hoard != address(0));
require(_migrationMaster != address(0));
creator = msg.sender;
hoard = _hoard;
migrationMaster = _migrationMaster;
}
// BASE CLASS IMPLEMENTATION
/// @notice ExtendedStandardToken is StandardToken
function transfer (address _to, uint256 _value) public
returns (bool)
{
return super.transfer(_to, _value);
}
/// @notice ExtendedStandardToken is StandardToken
function transferFrom (address _from, address _to, uint256 _value) public
returns (bool)
{
return super.transferFrom(_from, _to, _value);
}
/// @notice ExtendedStandardToken is Migratable
function migrate (uint256 _value) public migrationAgentSet {
super.migrate(_value);
}
/// @notice ExtendedStandardToken
function setMigrationAgent (address _address) public onlyMigrationMaster migrationAgentNotSet {
require(_address != address(0));
super.setMigrationAgent(_address);
}
/// @notice ExtendedStandardToken is Mintable
function mintTokens (address _recipient, uint256 _amount) public onlyCreator {
super.mintTokens(_recipient, _amount);
}
// CONTROL LOGIC
/// @dev changes Hoard multisig address to another one
function changeHoardAddress (address _address) onlyHoard external { hoard = _address; }
/// @dev changes migration master address to another one
function changeMigrationMaster (address _address) onlyHoard external { migrationMaster = _address; }
}
{
"compilationTarget": {
"HoardToken.sol": "HoardToken"
},
"evmVersion": "petersburg",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[],"name":"creator","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_value","type":"uint256"}],"name":"migrate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"}],"name":"changeMigrationMaster","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"migrationMaster","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"}],"name":"setMigrationAgent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_address","type":"address"}],"name":"changeHoardAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"migrationAgent","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalMigrated","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","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":"hoard","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_recipient","type":"address"},{"name":"_amount","type":"uint256"}],"name":"mintTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_hoard","type":"address"},{"name":"_migrationMaster","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Migrate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_from","type":"address"},{"indexed":true,"name":"_to","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_owner","type":"address"},{"indexed":true,"name":"_spender","type":"address"},{"indexed":false,"name":"_value","type":"uint256"}],"name":"Approval","type":"event"}]