// File: contracts/ITransferRules.sol
pragma solidity 0.5.12;
contract ITransferRules {
/// @notice Detects if a transfer will be reverted and if so returns an appropriate reference code
/// @param from Sending address
/// @param to Receiving address
/// @param value Amount of tokens being transferred
/// @return Code by which to reference message for rejection reasoning
function detectTransferRestriction(
address token,
address from,
address to,
uint256 value
) external view returns (uint8);
/// @notice Returns a human-readable message for a given restriction code
/// @param restrictionCode Identifier for looking up a message
/// @return Text showing the restriction's reasoning
function messageForTransferRestriction(uint8 restrictionCode)
external
view
returns (string memory);
function checkSuccess(uint8 restrictionCode) external view returns (bool);
}
// 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/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/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: contracts/RestrictedToken.sol
pragma solidity 0.5.12;
/// @title Restricted Token
/// @author CoMakery, Inc.
/// @notice An ERC-20 token with ERC-1404 transfer restrictions for managing security tokens, etc.
contract RestrictedToken is ERC20 {
using SafeMath for uint256;
string public symbol;
string public name;
uint8 public decimals;
ITransferRules public transferRules;
using Roles for Roles.Role;
Roles.Role private _contractAdmins;
Roles.Role private _transferAdmins;
uint256 public maxTotalSupply;
uint256 public contractAdminCount;
// Transfer restriction "eternal storage" mappings that can be used by future TransferRules contract upgrades
// They are accessed through getter and setter methods
mapping(address => uint256) private _maxBalances;
mapping(address => uint256) private _lockUntil; // unix timestamp to lock funds until
mapping(address => uint256) private _transferGroups; // restricted groups like Reg D Accredited US, Reg CF Unaccredited US and Reg S Foreign
mapping(uint256 => mapping(uint256 => uint256)) private _allowGroupTransfers; // approve transfers between groups: from => to => TimeLockUntil
mapping(address => bool) private _frozenAddresses;
bool public isPaused = false;
uint256 public constant MAX_UINT256 = ((2 ** 255 - 1) * 2) + 1; // get max uint256 without overflow
event RoleChange(address indexed grantor, address indexed grantee, string role, bool indexed status);
event AddressMaxBalance(address indexed admin, address indexed addr, uint256 indexed value);
event AddressTimeLock(address indexed admin, address indexed addr, uint256 indexed value);
event AddressTransferGroup(address indexed admin, address indexed addr, uint256 indexed value);
event AddressFrozen(address indexed admin, address indexed addr, bool indexed status);
event AllowGroupTransfer(address indexed admin, uint256 indexed fromGroup, uint256 indexed toGroup, uint256 lockedUntil);
event Pause(address admin, bool status);
event Upgrade(address admin, address oldRules, address newRules);
constructor(
address transferRules_,
address contractAdmin_,
address tokenReserveAdmin_,
string memory symbol_,
string memory name_,
uint8 decimals_,
uint256 totalSupply_,
uint256 maxTotalSupply_
) public {
require(transferRules_ != address(0), "Transfer rules address cannot be 0x0");
require(contractAdmin_ != address(0), "Token owner address cannot be 0x0");
require(tokenReserveAdmin_ != address(0), "Token reserve admin address cannot be 0x0");
// Transfer rules can be swapped out for a new contract inheriting from the ITransferRules interface
// The "eternal storage" for rule data stays in this RestrictedToken contract for use by TransferRules contract upgrades
transferRules = ITransferRules(transferRules_);
symbol = symbol_;
name = name_;
decimals = decimals_;
maxTotalSupply = maxTotalSupply_;
_contractAdmins.add(contractAdmin_);
contractAdminCount = 1;
_mint(tokenReserveAdmin_, totalSupply_);
}
modifier onlyContractAdmin() {
require(_contractAdmins.has(msg.sender), "DOES NOT HAVE CONTRACT OWNER ROLE");
_;
}
modifier onlyTransferAdmin() {
require(_transferAdmins.has(msg.sender), "DOES NOT HAVE TRANSFER ADMIN ROLE");
_;
}
modifier onlyTransferAdminOrContractAdmin() {
require((_contractAdmins.has(msg.sender) || _transferAdmins.has(msg.sender)),
"DOES NOT HAVE TRANSFER ADMIN OR CONTRACT ADMIN ROLE");
_;
}
modifier validAddress(address addr) {
require(addr != address(0), "Address cannot be 0x0");
_;
}
/// @dev Authorizes an address holder to write transfer restriction rules
/// @param addr The address to grant transfer admin rights to
function grantTransferAdmin(address addr) external validAddress(addr) onlyContractAdmin {
_transferAdmins.add(addr);
emit RoleChange(msg.sender, addr, "TransferAdmin", true);
}
/// @dev Revokes authorization to write transfer restriction rules
/// @param addr The address to grant transfer admin rights to
function revokeTransferAdmin(address addr) external validAddress(addr) onlyContractAdmin {
_transferAdmins.remove(addr);
emit RoleChange(msg.sender, addr, "TransferAdmin", false);
}
/// @dev Checks if an address is an authorized transfer admin.
/// @param addr The address to check for transfer admin privileges.
/// @return hasPermission returns true if the address has transfer admin permission and false if not.
function checkTransferAdmin(address addr) external view returns(bool hasPermission) {
return _transferAdmins.has(addr);
}
/// @dev Authorizes an address holder to be a contract admin. Contract admins grant privileges to accounts.
/// Contract admins can mint/burn tokens and freeze accounts.
/// @param addr The address to grant transfer admin rights to.
function grantContractAdmin(address addr) external validAddress(addr) onlyContractAdmin {
_contractAdmins.add(addr);
contractAdminCount = contractAdminCount.add(1);
emit RoleChange(msg.sender, addr, "ContractAdmin", true);
}
/// @dev Revokes authorization as a contract admin.
/// The contract requires there is at least 1 Contract Admin to avoid locking the Contract Admin functionality.
/// @param addr The address to remove contract admin rights from
function revokeContractAdmin(address addr) external validAddress(addr) onlyContractAdmin {
require(contractAdminCount > 1, "Must have at least one contract admin");
_contractAdmins.remove(addr);
contractAdminCount = contractAdminCount.sub(1);
emit RoleChange(msg.sender, addr, "ContractAdmin", false);
}
/// @dev Checks if an address is an authorized contract admin.
/// @param addr The address to check for contract admin privileges.
/// @return hasPermission returns true if the address has contract admin permission and false if not.
function checkContractAdmin(address addr) external view returns(bool hasPermission) {
return _contractAdmins.has(addr);
}
/// @dev Enforces transfer restrictions managed using the ERC-1404 standard functions.
/// The TransferRules contract defines what the rules are. The data inputs to those rules remains in the RestrictedToken contract.
/// TransferRules is a separate contract so its logic can be upgraded.
/// @param from The address the tokens are transferred from
/// @param to The address the tokens would be transferred to
/// @param value the quantity of tokens to be transferred
function enforceTransferRestrictions(address from, address to, uint256 value) private view {
uint8 restrictionCode = detectTransferRestriction(from, to, value);
require(transferRules.checkSuccess(restrictionCode), messageForTransferRestriction(restrictionCode));
}
/// @dev Calls the TransferRules detectTransferRetriction function to determine if tokens can be transferred.
/// detectTransferRestriction returns a status code.
/// @param from The address the tokens are transferred from
/// @param to The address the tokens would be transferred to
/// @param value The quantity of tokens to be transferred
function detectTransferRestriction(address from, address to, uint256 value) public view returns(uint8) {
return transferRules.detectTransferRestriction(address(this), from, to, value);
}
/// @dev Calls TransferRules to lookup a human readable error message that goes with an error code.
/// @param restrictionCode is an error code to lookup an error code for
function messageForTransferRestriction(uint8 restrictionCode) public view returns(string memory) {
return transferRules.messageForTransferRestriction(restrictionCode);
}
/// @dev Sets the maximum number of tokens an address will be allowed to hold.
/// Addresses can hold 0 tokens by default.
/// @param addr The address to restrict
/// @param updatedValue the maximum number of tokens the address can hold
function setMaxBalance(address addr, uint256 updatedValue) public validAddress(addr) onlyTransferAdmin {
_maxBalances[addr] = updatedValue;
emit AddressMaxBalance(msg.sender, addr, updatedValue);
}
/// @dev Gets the maximum number of tokens an address is allowed to hold
/// @param addr The address to check restrictions for
function getMaxBalance(address addr) external view returns(uint256) {
return _maxBalances[addr];
}
/// @dev Lock tokens in the address from being transfered until the specified time
/// @param addr The address to restrict
/// @param timestamp The time the tokens will be locked until as a Unix timetsamp.
/// Unix timestamp is the number of seconds since the Unix epoch of 00:00:00 UTC on 1 January 1970.
function setLockUntil(address addr, uint256 timestamp) public validAddress(addr) onlyTransferAdmin {
_lockUntil[addr] = timestamp;
emit AddressTimeLock(msg.sender, addr, timestamp);
}
/// @dev A convenience method to remove an addresses timelock. It sets the lock date to 0 which corresponds to the
/// earliest possible timestamp in the past 00:00:00 UTC on 1 January 1970.
/// @param addr The address to remove the timelock for.
function removeLockUntil(address addr) external validAddress(addr) onlyTransferAdmin {
_lockUntil[addr] = 0;
emit AddressTimeLock(msg.sender, addr, 0);
}
/// @dev Check when the address will be locked for transfers until
/// @param addr The address to check
/// @return timestamp The time the address will be locked until.
/// The format is the number of seconds since the Unix epoch of 00:00:00 UTC on 1 January 1970.
function getLockUntil(address addr) external view returns(uint256 timestamp) {
return _lockUntil[addr];
}
/// @dev Set the one group that the address belongs to, such as a US Reg CF investor group.
/// @param addr The address to set the group for.
/// @param groupID The uint256 numeric ID of the group.
function setTransferGroup(address addr, uint256 groupID) public validAddress(addr) onlyTransferAdmin {
_transferGroups[addr] = groupID;
emit AddressTransferGroup(msg.sender, addr, groupID);
}
/// @dev Gets the transfer group the address belongs to. The default group is 0.
/// @param addr The address to check.
/// @return groupID The group id of the address.
function getTransferGroup(address addr) external view returns(uint256 groupID) {
return _transferGroups[addr];
}
/// @dev Freezes or unfreezes an address.
/// Tokens in a frozen address cannot be transferred from until the address is unfrozen.
/// @param addr The address to be frozen.
/// @param status The frozenAddress status of the address. True means frozen false means not frozen.
function freeze(address addr, bool status) public validAddress(addr) onlyTransferAdminOrContractAdmin {
_frozenAddresses[addr] = status;
emit AddressFrozen(msg.sender, addr, status);
}
/// @dev Checks the status of an address to see if its frozen
/// @param addr The address to check
/// @return status Returns true if the address is frozen and false if its not frozen.
function getFrozenStatus(address addr) external view returns(bool status) {
return _frozenAddresses[addr];
}
/// @dev A convenience method for updating the transfer group, lock until, max balance, and freeze status.
/// The convenience method also helps to reduce gas costs.
/// @param addr The address to set permissions for.
/// @param groupID The ID of the address
/// @param timeLockUntil The unix timestamp that the address should be locked until. Use 0 if it's not locked.
/// The format is the number of seconds since the Unix epoch of 00:00:00 UTC on 1 January 1970.
/// @param maxBalance Is the maximum number of tokens the account can hold.
/// @param status The frozenAddress status of the address. True means frozen false means not frozen.
function setAddressPermissions(address addr, uint256 groupID, uint256 timeLockUntil,
uint256 maxBalance, bool status) public validAddress(addr) onlyTransferAdmin {
setTransferGroup(addr, groupID);
setLockUntil(addr, timeLockUntil);
setMaxBalance(addr, maxBalance);
freeze(addr, status);
}
/// @dev Sets an allowed transfer from a group to another group beginning at a specific time.
/// There is only one definitive rule per from and to group.
/// @param from The group the transfer is coming from.
/// @param to The group the transfer is going to.
/// @param lockedUntil The unix timestamp that the transfer is locked until. 0 is a special number. 0 means the transfer is not allowed.
/// This is because in the smart contract mapping all pairs are implicitly defined with a default lockedUntil value of 0.
/// But no transfers should be authorized until explicitly allowed. Thus 0 must mean no transfer is allowed.
function setAllowGroupTransfer(uint256 from, uint256 to, uint256 lockedUntil) external onlyTransferAdmin {
_allowGroupTransfers[from][to] = lockedUntil;
emit AllowGroupTransfer(msg.sender, from, to, lockedUntil);
}
/// @dev Checks to see when a transfer between two addresses would be allowed.
/// @param from The address the transfer is coming from
/// @param to The address the transfer is going to
/// @return timestamp The Unix timestamp of the time the transfer would be allowed. A 0 means never.
/// The format is the number of seconds since the Unix epoch of 00:00:00 UTC on 1 January 1970.
function getAllowTransferTime(address from, address to) external view returns(uint timestamp) {
return _allowGroupTransfers[_transferGroups[from]][_transferGroups[to]];
}
/// @dev Checks to see when a transfer between two groups would be allowed.
/// @param from The group id the transfer is coming from
/// @param to The group id the transfer is going to
/// @return timestamp The Unix timestamp of the time the transfer would be allowed. A 0 means never.
/// The format is the number of seconds since the Unix epoch of 00:00:00 UTC on 1 January 1970.
function getAllowGroupTransferTime(uint from, uint to) external view returns(uint timestamp) {
return _allowGroupTransfers[from][to];
}
/// @dev Destroys tokens and removes them from the total supply. Can only be called by an address with a Contract Admin role.
/// @param from The address to destroy the tokens from.
/// @param value The number of tokens to destroy from the address.
function burn(address from, uint256 value) external validAddress(from) onlyContractAdmin {
require(value <= balanceOf(from), "Insufficent tokens to burn");
_burn(from, value);
}
/// @dev Allows the contract admin to create new tokens in a specified address.
/// The total number of tokens cannot exceed the maxTotalSupply (the "Hard Cap").
/// @param to The addres to mint tokens into.
/// @param value The number of tokens to mint.
function mint(address to, uint256 value) external validAddress(to) onlyContractAdmin {
require(SafeMath.add(totalSupply(), value) <= maxTotalSupply, "Cannot mint more than the max total supply");
_mint(to, value);
}
/// @dev Allows the contract admin to pause transfers.
function pause() external onlyContractAdmin() {
isPaused = true;
emit Pause(msg.sender, true);
}
/// @dev Allows the contract admin to unpause transfers.
function unpause() external onlyContractAdmin() {
isPaused = false;
emit Pause(msg.sender, false);
}
/// @dev Allows the contrac admin to upgrade the transfer rules.
/// The upgraded transfer rules must implement the ITransferRules interface which conforms to the ERC-1404 token standard.
/// @param newTransferRules The address of the deployed TransferRules contract.
function upgradeTransferRules(ITransferRules newTransferRules) external onlyContractAdmin {
require(address(newTransferRules) != address(0x0), "Address cannot be 0x0");
address oldRules = address(transferRules);
transferRules = newTransferRules;
emit Upgrade(msg.sender, oldRules, address(newTransferRules));
}
function transfer(address to, uint256 value) public validAddress(to) returns(bool success) {
require(value <= balanceOf(msg.sender), "Insufficent tokens");
enforceTransferRestrictions(msg.sender, to, value);
super.transfer(to, value);
return true;
}
function transferFrom(address from, address to, uint256 value) public validAddress(from) validAddress(to) returns(bool success) {
require(value <= allowance(from, to), "The approved allowance is lower than the transfer amount");
require(value <= balanceOf(from), "Insufficent tokens");
enforceTransferRestrictions(from, to, value);
super.transferFrom(from, to, value);
return true;
}
function safeApprove(address spender, uint256 value) public {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require((value == 0) || (allowance(address(msg.sender), spender) == 0),
"Cannot approve from non-zero to non-zero allowance"
);
approve(spender, value);
}
}
// File: contracts/TransferRules.sol
pragma solidity 0.5.12;
contract TransferRules is ITransferRules {
using SafeMath for uint256;
mapping(uint8 => string) internal errorMessage;
uint8 public constant SUCCESS = 0;
uint8 public constant GREATER_THAN_RECIPIENT_MAX_BALANCE = 1;
uint8 public constant SENDER_TOKENS_TIME_LOCKED = 2;
uint8 public constant DO_NOT_SEND_TO_TOKEN_CONTRACT = 3;
uint8 public constant DO_NOT_SEND_TO_EMPTY_ADDRESS = 4;
uint8 public constant SENDER_ADDRESS_FROZEN = 5;
uint8 public constant ALL_TRANSFERS_PAUSED = 6;
uint8 public constant TRANSFER_GROUP_NOT_APPROVED = 7;
uint8 public constant TRANSFER_GROUP_NOT_ALLOWED_UNTIL_LATER = 8;
constructor() public {
errorMessage[SUCCESS] = "SUCCESS";
errorMessage[GREATER_THAN_RECIPIENT_MAX_BALANCE] = "GREATER THAN RECIPIENT MAX BALANCE";
errorMessage[SENDER_TOKENS_TIME_LOCKED] = "SENDER TOKENS LOCKED";
errorMessage[DO_NOT_SEND_TO_TOKEN_CONTRACT] = "DO NOT SEND TO TOKEN CONTRACT";
errorMessage[DO_NOT_SEND_TO_EMPTY_ADDRESS] = "DO NOT SEND TO EMPTY ADDRESS";
errorMessage[SENDER_ADDRESS_FROZEN] = "SENDER ADDRESS IS FROZEN";
errorMessage[ALL_TRANSFERS_PAUSED] = "ALL TRANSFERS PAUSED";
errorMessage[TRANSFER_GROUP_NOT_APPROVED] = "TRANSFER GROUP NOT APPROVED";
errorMessage[TRANSFER_GROUP_NOT_ALLOWED_UNTIL_LATER] = "TRANSFER GROUP NOT ALLOWED UNTIL LATER";
}
/// @notice Detects if a transfer will be reverted and if so returns an appropriate reference code
/// @param from Sending address
/// @param to Receiving address
/// @param value Amount of tokens being transferred
/// @return Code by which to reference message for rejection reason
function detectTransferRestriction(address _token, address from, address to, uint256 value) external view returns(uint8) {
RestrictedToken token = RestrictedToken(_token);
if (token.isPaused()) return ALL_TRANSFERS_PAUSED;
if (to == address(0)) return DO_NOT_SEND_TO_EMPTY_ADDRESS;
if (to == address(token)) return DO_NOT_SEND_TO_TOKEN_CONTRACT;
if (token.balanceOf(to).add(value) > token.getMaxBalance(to)) return GREATER_THAN_RECIPIENT_MAX_BALANCE;
if (now < token.getLockUntil(from)) return SENDER_TOKENS_TIME_LOCKED;
if (token.getFrozenStatus(from)) return SENDER_ADDRESS_FROZEN;
uint256 lockedUntil = token.getAllowTransferTime(from, to);
if (0 == lockedUntil) return TRANSFER_GROUP_NOT_APPROVED;
if (now < lockedUntil) return TRANSFER_GROUP_NOT_ALLOWED_UNTIL_LATER;
return SUCCESS;
}
/// @notice Returns a human-readable message for a given restriction code
/// @param restrictionCode Identifier for looking up a message
/// @return Text showing the restriction's reasoning
function messageForTransferRestriction(uint8 restrictionCode) external view returns(string memory) {
return errorMessage[restrictionCode];
}
/// @notice a method for checking a response code to determine if a transfer was succesful.
/// Defining this separately from the token contract allows it to be upgraded.
/// For instance this method would need to be upgraded if the SUCCESS code was changed to 1
/// as specified in ERC-1066 instead of 0 as specified in ERC-1404.
/// @param restrictionCode The code to check.
/// @return isSuccess A boolean indicating if the code is the SUCCESS code.
function checkSuccess(uint8 restrictionCode) external view returns(bool isSuccess) {
return restrictionCode == SUCCESS;
}
}
{
"compilationTarget": {
"RestrictedToken.sol": "RestrictedToken"
},
"evmVersion": "petersburg",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"transferRules_","type":"address"},{"internalType":"address","name":"contractAdmin_","type":"address"},{"internalType":"address","name":"tokenReserveAdmin_","type":"address"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"string","name":"name_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"uint256","name":"totalSupply_","type":"uint256"},{"internalType":"uint256","name":"maxTotalSupply_","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":true,"internalType":"address","name":"addr","type":"address"},{"indexed":true,"internalType":"bool","name":"status","type":"bool"}],"name":"AddressFrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":true,"internalType":"address","name":"addr","type":"address"},{"indexed":true,"internalType":"uint256","name":"value","type":"uint256"}],"name":"AddressMaxBalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":true,"internalType":"address","name":"addr","type":"address"},{"indexed":true,"internalType":"uint256","name":"value","type":"uint256"}],"name":"AddressTimeLock","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":true,"internalType":"address","name":"addr","type":"address"},{"indexed":true,"internalType":"uint256","name":"value","type":"uint256"}],"name":"AddressTransferGroup","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"admin","type":"address"},{"indexed":true,"internalType":"uint256","name":"fromGroup","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"toGroup","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lockedUntil","type":"uint256"}],"name":"AllowGroupTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"Pause","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"grantor","type":"address"},{"indexed":true,"internalType":"address","name":"grantee","type":"address"},{"indexed":false,"internalType":"string","name":"role","type":"string"},{"indexed":true,"internalType":"bool","name":"status","type":"bool"}],"name":"RoleChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"admin","type":"address"},{"indexed":false,"internalType":"address","name":"oldRules","type":"address"},{"indexed":false,"internalType":"address","name":"newRules","type":"address"}],"name":"Upgrade","type":"event"},{"constant":true,"inputs":[],"name":"MAX_UINT256","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"burn","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"checkContractAdmin","outputs":[{"internalType":"bool","name":"hasPermission","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"checkTransferAdmin","outputs":[{"internalType":"bool","name":"hasPermission","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"contractAdminCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"detectTransferRestriction","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"bool","name":"status","type":"bool"}],"name":"freeze","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"from","type":"uint256"},{"internalType":"uint256","name":"to","type":"uint256"}],"name":"getAllowGroupTransferTime","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"getAllowTransferTime","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getFrozenStatus","outputs":[{"internalType":"bool","name":"status","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getLockUntil","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getMaxBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getTransferGroup","outputs":[{"internalType":"uint256","name":"groupID","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"grantContractAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"grantTransferAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"isPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maxTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint8","name":"restrictionCode","type":"uint8"}],"name":"messageForTransferRestriction","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"mint","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"pause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"removeLockUntil","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"revokeContractAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"revokeTransferAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"safeApprove","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"groupID","type":"uint256"},{"internalType":"uint256","name":"timeLockUntil","type":"uint256"},{"internalType":"uint256","name":"maxBalance","type":"uint256"},{"internalType":"bool","name":"status","type":"bool"}],"name":"setAddressPermissions","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"from","type":"uint256"},{"internalType":"uint256","name":"to","type":"uint256"},{"internalType":"uint256","name":"lockedUntil","type":"uint256"}],"name":"setAllowGroupTransfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"setLockUntil","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"updatedValue","type":"uint256"}],"name":"setMaxBalance","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"groupID","type":"uint256"}],"name":"setTransferGroup","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"transferRules","outputs":[{"internalType":"contract ITransferRules","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"unpause","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"contract ITransferRules","name":"newTransferRules","type":"address"}],"name":"upgradeTransferRules","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]