/**
* Source Code first verified at https://etherscan.io on Wednesday, May 8, 2019
(UTC) */
// File: openzeppelin-solidity/contracts/ownership/Ownable.sol
pragma solidity ^0.5.2;
/**
* @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 private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
* @notice 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 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 {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// File: contracts/utils/Utils.sol
/**
* @title Utilities Contract
* @author Validity Labs AG <[email protected]>
*/
pragma solidity ^0.5.7;
contract Utils {
/** MODIFIERS **/
/**
* @notice Check if the address is not zero
*/
modifier onlyValidAddress(address _address) {
require(_address != address(0), "Invalid address");
_;
}
/**
* @notice Check if the address is not the sender's address
*/
modifier isSenderNot(address _address) {
require(_address != msg.sender, "Address is the same as the sender");
_;
}
/**
* @notice Check if the address is the sender's address
*/
modifier isSender(address _address) {
require(_address == msg.sender, "Address is different from the sender");
_;
}
/**
* @notice Controle if a boolean attribute (false by default) was updated to true.
* @dev This attribute is designed specifically for recording an action.
* @param criterion The boolean attribute that records if an action has taken place
*/
modifier onlyOnce(bool criterion) {
require(criterion == false, "Already been set");
_;
criterion = true;
}
}
// File: contracts/utils/Managed.sol
pragma solidity ^0.5.7;
contract Managed is Utils, Ownable {
// managers can be set and altered by owner, multiple manager accounts are possible
mapping(address => bool) public isManager;
/** EVENTS **/
event ChangedManager(address indexed manager, bool active);
/*** MODIFIERS ***/
modifier onlyManager() {
require(isManager[msg.sender], "not manager");
_;
}
/**
* @dev Set / alter manager / whitelister "account". This can be done from owner only
* @param manager address address of the manager to create/alter
* @param active bool flag that shows if the manager account is active
*/
function setManager(address manager, bool active) public onlyOwner onlyValidAddress(manager) {
isManager[manager] = active;
emit ChangedManager(manager, active);
}
}
// File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol
pragma solidity ^0.5.2;
/**
* @title ERC20 interface
* @dev see https://eips.ethereum.org/EIPS/eip-20
*/
interface IERC20 {
function transfer(address to, uint256 value) external returns (bool);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
function totalSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol
pragma solidity ^0.5.2;
/**
* @title ERC20Detailed token
* @dev The decimals are only for visualization purposes.
* All the operations are done using the smallest and indivisible token unit,
* just as on Ethereum all the operations are done in wei.
*/
contract ERC20Detailed is IERC20 {
string private _name;
string private _symbol;
uint8 private _decimals;
constructor (string memory name, string memory symbol, uint8 decimals) public {
_name = name;
_symbol = symbol;
_decimals = decimals;
}
/**
* @return the name of the token.
*/
function name() public view returns (string memory) {
return _name;
}
/**
* @return the symbol of the token.
*/
function symbol() public view returns (string memory) {
return _symbol;
}
/**
* @return the number of decimals of the token.
*/
function decimals() public view returns (uint8) {
return _decimals;
}
}
// File: openzeppelin-solidity/contracts/math/SafeMath.sol
pragma solidity ^0.5.2;
/**
* @title SafeMath
* @dev Unsigned math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two unsigned integers, reverts on 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);
return c;
}
/**
* @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two unsigned integers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
// File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
pragma solidity ^0.5.2;
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* https://eips.ethereum.org/EIPS/eip-20
* Originally based on code by FirstBlood:
* https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*
* This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for
* all accounts just by listening to said events. Note that this isn't required by the specification, and other
* compliant implementations may not do it.
*/
contract ERC20 is IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowed;
uint256 private _totalSupply;
/**
* @dev Total number of tokens in existence
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev Gets the balance of the specified address.
* @param owner The address to query the balance of.
* @return A uint256 representing the amount owned by the passed address.
*/
function balanceOf(address owner) public view returns (uint256) {
return _balances[owner];
}
/**
* @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 returns (uint256) {
return _allowed[owner][spender];
}
/**
* @dev Transfer token to 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) {
_transfer(msg.sender, 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 returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
/**
* @dev Transfer tokens from one address to another.
* Note that while this function emits an Approval event, this is not required as per the specification,
* and other compliant implementations may not emit the event.
* @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) {
_transfer(from, to, value);
_approve(from, msg.sender, _allowed[from][msg.sender].sub(value));
return true;
}
/**
* @dev Increase the amount of tokens that an owner allowed to a spender.
* approve should be called when _allowed[msg.sender][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
* Emits an Approval event.
* @param spender The address which will spend the funds.
* @param addedValue The amount of tokens to increase the allowance by.
*/
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(msg.sender, spender, _allowed[msg.sender][spender].add(addedValue));
return true;
}
/**
* @dev Decrease the amount of tokens that an owner allowed to a spender.
* approve should be called when _allowed[msg.sender][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
* Emits an Approval event.
* @param spender The address which will spend the funds.
* @param subtractedValue The amount of tokens to decrease the allowance by.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(msg.sender, spender, _allowed[msg.sender][spender].sub(subtractedValue));
return true;
}
/**
* @dev Transfer token for a specified addresses
* @param from The address to transfer from.
* @param to The address to transfer to.
* @param value The amount to be transferred.
*/
function _transfer(address from, address to, uint256 value) internal {
require(to != address(0));
_balances[from] = _balances[from].sub(value);
_balances[to] = _balances[to].add(value);
emit Transfer(from, to, value);
}
/**
* @dev Internal function that mints an amount of the token and assigns it to
* an account. This encapsulates the modification of balances such that the
* proper events are emitted.
* @param account The account that will receive the created tokens.
* @param value The amount that will be created.
*/
function _mint(address account, uint256 value) internal {
require(account != address(0));
_totalSupply = _totalSupply.add(value);
_balances[account] = _balances[account].add(value);
emit Transfer(address(0), account, value);
}
/**
* @dev Internal function that burns an amount of the token of a given
* account.
* @param account The account whose tokens will be burnt.
* @param value The amount that will be burnt.
*/
function _burn(address account, uint256 value) internal {
require(account != address(0));
_totalSupply = _totalSupply.sub(value);
_balances[account] = _balances[account].sub(value);
emit Transfer(account, address(0), value);
}
/**
* @dev Approve an address to spend another addresses' tokens.
* @param owner The address that owns the tokens.
* @param spender The address that will spend the tokens.
* @param value The number of tokens that can be spent.
*/
function _approve(address owner, address spender, uint256 value) internal {
require(spender != address(0));
require(owner != address(0));
_allowed[owner][spender] = value;
emit Approval(owner, spender, value);
}
/**
* @dev Internal function that burns an amount of the token of a given
* account, deducting from the sender's allowance for said account. Uses the
* internal burn function.
* Emits an Approval event (reflecting the reduced allowance).
* @param account The account whose tokens will be burnt.
* @param value The amount that will be burnt.
*/
function _burnFrom(address account, uint256 value) internal {
_burn(account, value);
_approve(account, msg.sender, _allowed[account][msg.sender].sub(value));
}
}
// File: openzeppelin-solidity/contracts/token/ERC20/ERC20Burnable.sol
pragma solidity ^0.5.2;
/**
* @title Burnable Token
* @dev Token that can be irreversibly burned (destroyed).
*/
contract ERC20Burnable is ERC20 {
/**
* @dev Burns a specific amount of tokens.
* @param value The amount of token to be burned.
*/
function burn(uint256 value) public {
_burn(msg.sender, value);
}
/**
* @dev Burns a specific amount of tokens from the target address and decrements allowance
* @param from address The account whose tokens will be burned.
* @param value uint256 The amount of token to be burned.
*/
function burnFrom(address from, uint256 value) public {
_burnFrom(from, value);
}
}
// File: openzeppelin-solidity/contracts/access/Roles.sol
pragma solidity ^0.5.2;
/**
* @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(account != address(0));
require(!has(role, account));
role.bearer[account] = true;
}
/**
* @dev remove an account's access to this role
*/
function remove(Role storage role, address account) internal {
require(account != address(0));
require(has(role, account));
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));
return role.bearer[account];
}
}
// File: openzeppelin-solidity/contracts/access/roles/PauserRole.sol
pragma solidity ^0.5.2;
contract PauserRole {
using Roles for Roles.Role;
event PauserAdded(address indexed account);
event PauserRemoved(address indexed account);
Roles.Role private _pausers;
constructor () internal {
_addPauser(msg.sender);
}
modifier onlyPauser() {
require(isPauser(msg.sender));
_;
}
function isPauser(address account) public view returns (bool) {
return _pausers.has(account);
}
function addPauser(address account) public onlyPauser {
_addPauser(account);
}
function renouncePauser() public {
_removePauser(msg.sender);
}
function _addPauser(address account) internal {
_pausers.add(account);
emit PauserAdded(account);
}
function _removePauser(address account) internal {
_pausers.remove(account);
emit PauserRemoved(account);
}
}
// File: openzeppelin-solidity/contracts/lifecycle/Pausable.sol
pragma solidity ^0.5.2;
/**
* @title Pausable
* @dev Base contract which allows children to implement an emergency stop mechanism.
*/
contract Pausable is PauserRole {
event Paused(address account);
event Unpaused(address account);
bool private _paused;
constructor () internal {
_paused = false;
}
/**
* @return true if the contract is paused, false otherwise.
*/
function paused() public view returns (bool) {
return _paused;
}
/**
* @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() public onlyPauser whenNotPaused {
_paused = true;
emit Paused(msg.sender);
}
/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() public onlyPauser whenPaused {
_paused = false;
emit Unpaused(msg.sender);
}
}
// File: openzeppelin-solidity/contracts/token/ERC20/ERC20Pausable.sol
pragma solidity ^0.5.2;
/**
* @title Pausable token
* @dev ERC20 modified with pausable transfers.
*/
contract ERC20Pausable is ERC20, Pausable {
function transfer(address to, uint256 value) public whenNotPaused returns (bool) {
return super.transfer(to, value);
}
function transferFrom(address from, address to, uint256 value) public whenNotPaused returns (bool) {
return super.transferFrom(from, to, value);
}
function approve(address spender, uint256 value) public whenNotPaused returns (bool) {
return super.approve(spender, value);
}
function increaseAllowance(address spender, uint addedValue) public whenNotPaused returns (bool success) {
return super.increaseAllowance(spender, addedValue);
}
function decreaseAllowance(address spender, uint subtractedValue) public whenNotPaused returns (bool success) {
return super.decreaseAllowance(spender, subtractedValue);
}
}
// File: openzeppelin-solidity/contracts/access/roles/MinterRole.sol
pragma solidity ^0.5.2;
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));
_;
}
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: openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol
pragma solidity ^0.5.2;
/**
* @title ERC20Mintable
* @dev ERC20 minting logic
*/
contract ERC20Mintable is ERC20, MinterRole {
/**
* @dev Function to mint tokens
* @param to The address that will receive the minted tokens.
* @param value The amount of tokens to mint.
* @return A boolean that indicates if the operation was successful.
*/
function mint(address to, uint256 value) public onlyMinter returns (bool) {
_mint(to, value);
return true;
}
}
// File: openzeppelin-solidity/contracts/utils/Address.sol
pragma solidity ^0.5.2;
/**
* Utility library of inline functions on addresses
*/
library Address {
/**
* Returns whether the target address is a contract
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param account address of the account to check
* @return whether the target address is a contract
*/
function isContract(address account) internal view returns (bool) {
uint256 size;
// XXX Currently there is no better way to check if there is a contract in an address
// than to check the size of the code at that address.
// See https://ethereum.stackexchange.com/a/14016/36603
// for more details about how this works.
// TODO Check this again before the Serenity release, because all addresses will be
// contracts then.
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
}
// File: openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol
pragma solidity ^0.5.2;
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// 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) || (token.allowance(address(this), spender) == 0));
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must equal true).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves.
// A Solidity high level call has three parts:
// 1. The target address is checked to verify it contains contract code
// 2. The call itself is made, and success asserted
// 3. The return value is decoded, which in turn checks the size of the returned data.
require(address(token).isContract());
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = address(token).call(data);
require(success);
if (returndata.length > 0) { // Return data is optional
require(abi.decode(returndata, (bool)));
}
}
}
// File: contracts/utils/Reclaimable.sol
/**
* @title Reclaimable
* @dev This contract gives owner right to recover any ERC20 tokens accidentally sent to
* the token contract. The recovered token will be sent to the owner of token.
* @author Validity Labs AG <[email protected]>
*/
// solhint-disable-next-line compiler-fixed, compiler-gt-0_5
pragma solidity ^0.5.7;
contract Reclaimable is Ownable {
using SafeERC20 for IERC20;
/**
* @notice Let the owner to retrieve other tokens accidentally sent to this contract.
* @dev This function is suitable when no token of any kind shall be stored under
* the address of the inherited contract.
* @param tokenToBeRecovered address of the token to be recovered.
*/
function reclaimToken(IERC20 tokenToBeRecovered) external onlyOwner {
uint256 balance = tokenToBeRecovered.balanceOf(address(this));
tokenToBeRecovered.safeTransfer(msg.sender, balance);
}
}
// File: openzeppelin-solidity/contracts/access/roles/WhitelistAdminRole.sol
pragma solidity ^0.5.2;
/**
* @title WhitelistAdminRole
* @dev WhitelistAdmins are responsible for assigning and removing Whitelisted accounts.
*/
contract WhitelistAdminRole {
using Roles for Roles.Role;
event WhitelistAdminAdded(address indexed account);
event WhitelistAdminRemoved(address indexed account);
Roles.Role private _whitelistAdmins;
constructor () internal {
_addWhitelistAdmin(msg.sender);
}
modifier onlyWhitelistAdmin() {
require(isWhitelistAdmin(msg.sender));
_;
}
function isWhitelistAdmin(address account) public view returns (bool) {
return _whitelistAdmins.has(account);
}
function addWhitelistAdmin(address account) public onlyWhitelistAdmin {
_addWhitelistAdmin(account);
}
function renounceWhitelistAdmin() public {
_removeWhitelistAdmin(msg.sender);
}
function _addWhitelistAdmin(address account) internal {
_whitelistAdmins.add(account);
emit WhitelistAdminAdded(account);
}
function _removeWhitelistAdmin(address account) internal {
_whitelistAdmins.remove(account);
emit WhitelistAdminRemoved(account);
}
}
// File: openzeppelin-solidity/contracts/access/roles/WhitelistedRole.sol
pragma solidity ^0.5.2;
/**
* @title WhitelistedRole
* @dev Whitelisted accounts have been approved by a WhitelistAdmin to perform certain actions (e.g. participate in a
* crowdsale). This role is special in that the only accounts that can add it are WhitelistAdmins (who can also remove
* it), and not Whitelisteds themselves.
*/
contract WhitelistedRole is WhitelistAdminRole {
using Roles for Roles.Role;
event WhitelistedAdded(address indexed account);
event WhitelistedRemoved(address indexed account);
Roles.Role private _whitelisteds;
modifier onlyWhitelisted() {
require(isWhitelisted(msg.sender));
_;
}
function isWhitelisted(address account) public view returns (bool) {
return _whitelisteds.has(account);
}
function addWhitelisted(address account) public onlyWhitelistAdmin {
_addWhitelisted(account);
}
function removeWhitelisted(address account) public onlyWhitelistAdmin {
_removeWhitelisted(account);
}
function renounceWhitelisted() public {
_removeWhitelisted(msg.sender);
}
function _addWhitelisted(address account) internal {
_whitelisteds.add(account);
emit WhitelistedAdded(account);
}
function _removeWhitelisted(address account) internal {
_whitelisteds.remove(account);
emit WhitelistedRemoved(account);
}
}
// File: openzeppelin-solidity/contracts/math/Math.sol
pragma solidity ^0.5.2;
/**
* @title Math
* @dev Assorted math operations
*/
library Math {
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Calculates the average of two numbers. Since these are integers,
* averages of an even and odd number cannot be represented, and will be
* rounded down.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow, so we distribute
return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
}
}
// File: contracts/token/ERC20/library/Snapshots.sol
/**
* @title Snapshot
* @dev Utility library of the Snapshot structure, including getting value.
* @author Validity Labs AG <[email protected]tylabs.org>
*/
pragma solidity ^0.5.7;
library Snapshots {
using Math for uint256;
using SafeMath for uint256;
/**
* @notice This structure stores the historical value associate at a particular timestamp
* @param timestamp The timestamp of the creation of the snapshot
* @param value The value to be recorded
*/
struct Snapshot {
uint256 timestamp;
uint256 value;
}
struct SnapshotList {
Snapshot[] history;
}
/** TODO: within 1 block: transfer w/ snapshot, then dividend distrubtion, transfer w/ snapshot
*
* @notice This function creates snapshots for certain value...
* @dev To avoid having two Snapshots with the same block.timestamp, we check if the last
* existing one is the current block.timestamp, we update the last Snapshot
* @param item The SnapshotList to be operated
* @param _value The value associated the the item that is going to have a snapshot
*/
function createSnapshot(SnapshotList storage item, uint256 _value) internal {
uint256 length = item.history.length;
if (length == 0 || (item.history[length.sub(1)].timestamp < block.timestamp)) {
item.history.push(Snapshot(block.timestamp, _value));
} else {
// When the last existing snapshot is ready to be updated
item.history[length.sub(1)].value = _value;
}
}
/**
* @notice Find the index of the item in the SnapshotList that contains information
* corresponding to the timestamp. (FindLowerBond of the array)
* @dev The binary search logic is inspired by the Arrays.sol from Openzeppelin
* @param item The list of Snapshots to be queried
* @param timestamp The timestamp of the queried moment
* @return The index of the Snapshot array
*/
function findBlockIndex(
SnapshotList storage item,
uint256 timestamp
)
internal
view
returns (uint256)
{
// Find lower bound of the array
uint256 length = item.history.length;
// Return value for extreme cases: If no snapshot exists and/or the last snapshot
if (item.history[length.sub(1)].timestamp <= timestamp) {
return length.sub(1);
} else {
// Need binary search for the value
uint256 low = 0;
uint256 high = length.sub(1);
while (low < high.sub(1)) {
uint256 mid = Math.average(low, high);
// mid will always be strictly less than high and it rounds down
if (item.history[mid].timestamp <= timestamp) {
low = mid;
} else {
high = mid;
}
}
return low;
}
}
/**
* @notice This function returns the value of the corresponding Snapshot
* @param item The list of Snapshots to be queried
* @param timestamp The timestamp of the queried moment
* @return The value of the queried moment
*/
function getValueAt(
SnapshotList storage item,
uint256 timestamp
)
internal
view
returns (uint256)
{
if (item.history.length == 0 || timestamp < item.history[0].timestamp) {
return 0;
} else {
uint256 index = findBlockIndex(item, timestamp);
return item.history[index].value;
}
}
}
// File: contracts/token/ERC20/ERC20Snapshot.sol
/**
* @title ERC20 Snapshot Token
* @dev This is an ERC20 compatible token that takes snapshots of account balances.
* @author Validity Labs AG <[email protected]>
*/
pragma solidity ^0.5.7;
contract ERC20Snapshot is ERC20 {
using Snapshots for Snapshots.SnapshotList;
mapping(address => Snapshots.SnapshotList) private _snapshotBalances;
Snapshots.SnapshotList private _snapshotTotalSupply;
event CreatedAccountSnapshot(address indexed account, uint256 indexed timestamp, uint256 value);
event CreatedTotalSupplySnapshot(uint256 indexed timestamp, uint256 value);
/**
* @notice Return the historical supply of the token at a certain time
* @param timestamp The block number of the moment when token supply is queried
* @return The total supply at "timestamp"
*/
function totalSupplyAt(uint256 timestamp) public view returns (uint256) {
return _snapshotTotalSupply.getValueAt(timestamp);
}
/**
* @notice Return the historical balance of an account at a certain time
* @param owner The address of the token holder
* @param timestamp The block number of the moment when token supply is queried
* @return The balance of the queried token holder at "timestamp"
*/
function balanceOfAt(address owner, uint256 timestamp)
public
view
returns (uint256) {
return _snapshotBalances[owner].getValueAt(timestamp);
}
/** OVERRIDE
* @notice Transfer tokens between two accounts while enforcing the update of Snapshots
* @param from The address to transfer from
* @param to The address to transfer to
* @param value The amount to be transferred
*/
function _transfer(address from, address to, uint256 value) internal {
super._transfer(from, to, value); // ERC20 transfer
_createAccountSnapshot(from, balanceOf(from));
_createAccountSnapshot(to, balanceOf(to));
}
/** OVERRIDE
* @notice Mint tokens to one account while enforcing the update of Snapshots
* @param account The address that receives tokens
* @param value The amount of tokens to be created
*/
function _mint(address account, uint256 value) internal {
super._mint(account, value);
_createAccountSnapshot(account, balanceOf(account));
_createTotalSupplySnapshot(account, totalSupplyAt(block.timestamp).add(value));
}
/** OVERRIDE
* @notice Burn tokens of one account
* @param account The address whose tokens will be burnt
* @param value The amount of tokens to be burnt
*/
function _burn(address account, uint256 value) internal {
super._burn(account, value);
_createAccountSnapshot(account, balanceOf(account));
_createTotalSupplySnapshot(account, totalSupplyAt(block.timestamp).sub(value));
}
/**
* @notice creates a total supply snapshot & emits event
* @param amount uint256
* @param account address
*/
function _createTotalSupplySnapshot(address account, uint256 amount) internal {
_snapshotTotalSupply.createSnapshot(amount);
emit CreatedTotalSupplySnapshot(block.timestamp, amount);
}
/**
* @notice creates an account snapshot & emits event
* @param amount uint256
* @param account address
*/
function _createAccountSnapshot(address account, uint256 amount) internal {
_snapshotBalances[account].createSnapshot(amount);
emit CreatedAccountSnapshot(account, block.timestamp, amount);
}
function _precheckSnapshot() internal {
// FILL LATER TODO: comment on how this is utilized
// Why it's not being abstract
}
}
// File: contracts/STO/token/WhitelistedSnapshot.sol
/**
* @title Whitelisted Snapshot Token
* @author Validity Labs AG <[email protected]>
*/
pragma solidity ^0.5.7;
/**
* Whitelisted Snapshot repurposes the following 2 variables inherited from ERC20Snapshot:
* _snapshotBalances: only whitelisted accounts get snapshots
* _snapshotTotalSupply: only the total sum of whitelisted
*/
contract WhitelistedSnapshot is ERC20Snapshot, WhitelistedRole {
/** OVERRIDE
* @notice add account to whitelist & create a snapshot of current balance
* @param account address
*/
function addWhitelisted(address account) public {
super.addWhitelisted(account);
uint256 balance = balanceOf(account);
_createAccountSnapshot(account, balance);
uint256 newSupplyValue = totalSupplyAt(now).add(balance);
_createTotalSupplySnapshot(account, newSupplyValue);
}
/** OVERRIDE
* @notice remove account from white & create a snapshot of 0 balance
* @param account address
*/
function removeWhitelisted(address account) public {
super.removeWhitelisted(account);
_createAccountSnapshot(account, 0);
uint256 balance = balanceOf(account);
uint256 newSupplyValue = totalSupplyAt(now).sub(balance);
_createTotalSupplySnapshot(account, newSupplyValue);
}
/** OVERRIDE & call parent
* @notice Transfer tokens between two accounts while enforcing the update of Snapshots
* @dev the super._transfer call handles the snapshot of each account. See the internal functions
* below: _createTotalSupplySnapshot & _createAccountSnapshot
* @param from address The address to transfer from
* @param to address The address to transfer to
* @param value uint256 The amount to be transferred
*/
function _transfer(address from, address to, uint256 value) internal {
// if available will call the sibiling's inherited function before calling the parent's
super._transfer(from, to, value);
/**
* Possibilities:
* Homogeneous Transfers:
* 0: _whitelist to _whitelist: 0 total supply snapshot
* 1: nonwhitelist to nonwhitelist: 0 total supply snapshot
* Heterogeneous Transfers:
* 2: _whitelist to nonwhitelist: 1 whitelisted total supply snapshot
* 3: nonwhitelist to _whitelist: 1 whitelisted total supply snapshot
*/
// isWhitelistedHetero tells us to/from is a mix of whitelisted/not whitelisted accounts
// isAdding tell us whether or not to add or subtract from the whitelisted total supply value
(bool isWhitelistedHetero, bool isAdding) = _isWhitelistedHeterogeneousTransfer(from, to);
if (isWhitelistedHetero) { // one account is whitelisted, the other is not
uint256 newSupplyValue = totalSupplyAt(block.timestamp);
address account;
if (isAdding) {
newSupplyValue = newSupplyValue.add(value);
account = to;
} else {
newSupplyValue = newSupplyValue.sub(value);
account = from;
}
_createTotalSupplySnapshot(account, newSupplyValue);
}
}
/**
* @notice returns true (isHetero) for a mix-match of whitelisted & nonwhitelisted account transfers
* returns true (isAdding) if total supply is increasing or false for decreasing
* @param from address
* @param to address
* @return isHetero, isAdding. bool, bool
*/
function _isWhitelistedHeterogeneousTransfer(address from, address to)
internal
view
returns (bool isHetero, bool isAdding) {
bool _isToWhitelisted = isWhitelisted(to);
bool _isFromWhitelisted = isWhitelisted(from);
if (!_isFromWhitelisted && _isToWhitelisted) {
isHetero = true;
isAdding = true; // increase whitelisted total supply
} else if (_isFromWhitelisted && !_isToWhitelisted) {
isHetero = true;
}
}
/** OVERRIDE
* @notice creates a total supply snapshot & emits event
* @param amount uint256
* @param account address
*/
function _createTotalSupplySnapshot(address account, uint256 amount) internal {
if (isWhitelisted(account)) {
super._createTotalSupplySnapshot(account, amount);
}
}
/** OVERRIDE
* @notice only snapshot if account is whitelisted
* @param account address
* @param amount uint256
*/
function _createAccountSnapshot(address account, uint256 amount) internal {
if (isWhitelisted(account)) {
super._createAccountSnapshot(account, amount);
}
}
function _precheckSnapshot() internal onlyWhitelisted {}
}
// File: contracts/STO/BaseOptedIn.sol
/**
* @title Base Opt In
* @author Validity Labs AG <[email protected]>
* This allows accounts to "opt out" or "opt in"
* Defaults everyone to opted in
* Example: opt out from onchain dividend payments
*/
pragma solidity ^0.5.7;
contract BaseOptedIn {
// uint256 = timestamp. Default: 0 = opted in. > 0 = opted out
mapping(address => uint256) public optedOutAddresses; // whitelisters who've opted to receive offchain dividends
/** EVENTS **/
event OptedOut(address indexed account);
event OptedIn(address indexed account);
modifier onlyOptedBool(bool isIn) { // true for onlyOptedIn, false for onlyOptedOut
if (isIn) {
require(optedOutAddresses[msg.sender] > 0, "already opted in");
} else {
require(optedOutAddresses[msg.sender] == 0, "already opted out");
}
_;
}
/**
* @notice accounts who have opted out from onchain dividend payments
*/
function optOut() public onlyOptedBool(false) {
optedOutAddresses[msg.sender] = block.timestamp;
emit OptedOut(msg.sender);
}
/**
* @notice accounts who previously opted out, who opt back in
*/
function optIn() public onlyOptedBool(true) {
optedOutAddresses[msg.sender] = 0;
emit OptedIn(msg.sender);
}
/**
* @notice returns true if opted in
* @param account address
* @return optedIn bool
*/
function isOptedIn(address account) public view returns (bool optedIn) {
if (optedOutAddresses[account] == 0) {
optedIn = true;
}
}
}
// File: contracts/STO/token/OptedInSnapshot.sol
/**
* @title Opted In Snapshot
* @author Validity Labs AG <[email protected]>
*/
pragma solidity ^0.5.7;
/**
* Opted In Snapshot repurposes the following 2 variables inherited from ERC20Snapshot:
* _snapshotBalances: snapshots of opted in accounts
* _snapshotTotalSupply: only the total sum of opted in accounts
*/
contract OptedInSnapshot is ERC20Snapshot, BaseOptedIn {
/** OVERRIDE
* @notice accounts who previously opted out, who opt back in
*/
function optIn() public {
// protects against TODO: Fill later
super._precheckSnapshot();
super.optIn();
address account = msg.sender;
uint256 balance = balanceOf(account);
_createAccountSnapshot(account, balance);
_createTotalSupplySnapshot(account, totalSupplyAt(now).add(balance));
}
/** OVERRIDE
* @notice call parent f(x) &
* create new snapshot for account: setting to 0
* create new shapshot for total supply: oldTotalSupply.sub(balance)
*/
function optOut() public {
// protects against TODO: Fill later
super._precheckSnapshot();
super.optOut();
address account = msg.sender;
_createAccountSnapshot(account, 0);
_createTotalSupplySnapshot(account, totalSupplyAt(now).sub(balanceOf(account)));
}
/** OVERRIDE
* @notice Transfer tokens between two accounts while enforcing the update of Snapshots
* @param from The address to transfer from
* @param to The address to transfer to
* @param value The amount to be transferred
*/
function _transfer(address from, address to, uint256 value) internal {
// if available will call the sibiling's inherited function before calling the parent's
super._transfer(from, to, value);
/**
* Possibilities:
* Homogeneous Transfers:
* 0: opted in to opted in: 0 total supply snapshot
* 1: opted out to opted out: 0 total supply snapshot
* Heterogeneous Transfers:
* 2: opted out to opted in: 1 whitelisted total supply snapshot
* 3: opted in to opted out: 1 whitelisted total supply snapshot
*/
// isOptedHetero tells us to/from is a mix of opted in/out accounts
// isAdding tell us whether or not to add or subtract from the opted in total supply value
(bool isOptedHetero, bool isAdding) = _isOptedHeterogeneousTransfer(from, to);
if (isOptedHetero) { // one account is whitelisted, the other is not
uint256 newSupplyValue = totalSupplyAt(block.timestamp);
address account;
if (isAdding) {
newSupplyValue = newSupplyValue.add(value);
account = to;
} else {
newSupplyValue = newSupplyValue.sub(value);
account = from;
}
_createTotalSupplySnapshot(account, newSupplyValue);
}
}
/**
* @notice returns true for a mix-match of opted in & opted out transfers.
* if true, returns true/false for increasing either optedIn or opetedOut total supply balances
* @dev should only be calling if both to and from accounts are whitelisted
* @param from address
* @param to address
* @return isOptedHetero, isOptedInIncrease. bool, bool
*/
function _isOptedHeterogeneousTransfer(address from, address to)
internal
view
returns (bool isOptedHetero, bool isOptedInIncrease) {
bool _isToOptedIn = isOptedIn(to);
bool _isFromOptedIn = isOptedIn(from);
if (!_isFromOptedIn && _isToOptedIn) {
isOptedHetero = true;
isOptedInIncrease = true; // increase opted in total supply
} else if (_isFromOptedIn && !_isToOptedIn) {
isOptedHetero = true;
}
}
/** OVERRIDE
* @notice creates a total supply snapshot & emits event
* @param amount uint256
* @param account address
*/
function _createTotalSupplySnapshot(address account, uint256 amount) internal {
if (isOptedIn(account)) {
super._createTotalSupplySnapshot(account, amount);
}
}
/** OVERRIDE
* @notice only snapshot if opted in
* @param account address
* @param amount uint256
*/
function _createAccountSnapshot(address account, uint256 amount) internal {
if (isOptedIn(account)) {
super._createAccountSnapshot(account, amount);
}
}
}
// File: contracts/STO/token/ERC20ForceTransfer.sol
/**
* @title ERC20 ForceTransfer
* @author Validity Labs AG <[email protected]>
*/
pragma solidity ^0.5.7;
/**
* @dev inherit contract, create external/public function that calls these internal functions
* to activate the ability for one or both forceTransfer implementations
*/
contract ERC20ForceTransfer is Ownable, ERC20 {
event ForcedTransfer(address indexed confiscatee, uint256 amount, address indexed receiver);
/**
* @notice takes all funds from confiscatee and sends them to receiver
* @param confiscatee address who's funds are being confiscated
* @param receiver address who's receiving the funds
*/
function forceTransfer(address confiscatee, address receiver) external onlyOwner {
uint256 balance = balanceOf(confiscatee);
_transfer(confiscatee, receiver, balance);
emit ForcedTransfer(confiscatee, balance, receiver);
}
/**
* @notice takes an amount of funds from confiscatee and sends them to receiver
* @param confiscatee address who's funds are being confiscated
* @param receiver address who's receiving the funds
*/
function forceTransfer(address confiscatee, address receiver, uint256 amount) external onlyOwner {
_transfer(confiscatee, receiver, amount);
emit ForcedTransfer(confiscatee, amount, receiver);
}
}
// File: contracts/STO/BaseDocumentRegistry.sol
/**
* @title Base Document Registry Contract
* @author Validity Labs AG <[email protected]>
* inspired by Neufund's iAgreement smart contract
*/
pragma solidity ^0.5.7;
// solhint-disable not-rely-on-time
contract BaseDocumentRegistry is Ownable {
using SafeMath for uint256;
struct HashedDocument {
uint256 timestamp;
string documentUri;
}
HashedDocument[] private _documents;
event AddedLogDocumented(string documentUri, uint256 documentIndex);
/**
* @notice adds a document's uri from IPFS to the array
* @param documentUri string
*/
function addDocument(string calldata documentUri) external onlyOwner {
require(bytes(documentUri).length > 0, "invalid documentUri");
HashedDocument memory document = HashedDocument({
timestamp: block.timestamp,
documentUri: documentUri
});
_documents.push(document);
emit AddedLogDocumented(documentUri, _documents.length.sub(1));
}
/**
* @notice fetch the latest document on the array
* @return uint256, string, uint256
*/
function currentDocument()
public
view
returns (uint256 timestamp, string memory documentUri, uint256 index) {
require(_documents.length > 0, "no documents exist");
uint256 last = _documents.length.sub(1);
HashedDocument storage document = _documents[last];
return (document.timestamp, document.documentUri, last);
}
/**
* @notice adds a document's uri from IPFS to the array
* @param documentIndex uint256
* @return uint256, string, uint256
*/
function getDocument(uint256 documentIndex)
public
view
returns (uint256 timestamp, string memory documentUri, uint256 index) {
require(documentIndex < _documents.length, "invalid index");
HashedDocument storage document = _documents[documentIndex];
return (document.timestamp, document.documentUri, documentIndex);
}
/**
* @notice return the total amount of documents in the array
* @return uint256
*/
function documentCount() public view returns (uint256) {
return _documents.length;
}
}
// File: contracts/examples/ExampleSecurityToken.sol
/**
* @title Example Security Token
* @author Validity Labs AG <[email protected]>
*/
pragma solidity ^0.5.7;
contract ExampleSecurityToken is
Utils,
Reclaimable,
ERC20Detailed,
WhitelistedSnapshot,
OptedInSnapshot,
ERC20Mintable,
ERC20Burnable,
ERC20Pausable,
ERC20ForceTransfer,
BaseDocumentRegistry {
bool private _isSetup;
/**
* @notice contructor for the token contract
*/
constructor(string memory name, string memory symbol, address initialAccount, uint256 initialBalance)
public
ERC20Detailed(name, symbol, 0) {
// pause();
_mint(initialAccount, initialBalance);
roleSetup(initialAccount);
}
/**
* @notice setup roles and contract addresses for the new token
* @param board Address of the owner who is also a manager
*/
function roleSetup(address board) internal onlyOwner onlyOnce(_isSetup) {
addMinter(board);
addPauser(board);
_addWhitelistAdmin(board);
}
/** OVERRIDE - onlyOwner role (the board) can call
* @notice Burn tokens of one account
* @param account The address whose tokens will be burnt
* @param value The amount of tokens to be burnt
*/
function _burn(address account, uint256 value) internal onlyOwner {
super._burn(account, value);
}
}
// File: contracts/STO/dividends/Dividends.sol
/**
* @title Dividend contract for STO
* @author Validity Labs AG <[email protected]>
*/
pragma solidity ^0.5.7;
contract Dividends is Utils, Ownable {
using SafeMath for uint256;
using SafeERC20 for IERC20;
address public _wallet; // set at deploy time
struct Dividend {
uint256 recordDate; // timestamp of the record date
uint256 claimPeriod; // claim period, in seconds, of the claiming period
address payoutToken; // payout token, which could be different each time.
uint256 payoutAmount; // the total amount of tokens deposit
uint256 claimedAmount; // the total amount of tokens being claimed
uint256 totalSupply; // the total supply of sto token when deposit was made
bool reclaimed; // If the unclaimed deposit was reclaimed by the team
mapping(address => bool) claimed; // If investors have claimed their dividends.
}
address public _token;
Dividend[] public dividends;
// Record the balance of each ERC20 token deposited to this contract as dividends.
mapping(address => uint256) public totalBalance;
// EVENTS
event DepositedDividend(uint256 indexed dividendIndex, address indexed payoutToken, uint256 payoutAmount, uint256 recordDate, uint256 claimPeriod);
event ReclaimedDividend(uint256 indexed dividendIndex, address indexed claimer, uint256 claimedAmount);
event RecycledDividend(uint256 indexed dividendIndex, uint256 timestamp, uint256 recycledAmount);
/**
* @notice Check if the index is valid
*/
modifier validDividendIndex(uint256 _dividendIndex) {
require(_dividendIndex < dividends.length, "Such dividend does not exist");
_;
}
/**
* @notice initialize the Dividend contract with the STO Token contract and the new owner
* @param stoToken The token address, of which the holders could claim dividends.
* @param wallet the address of the wallet to receive the reclaimed funds
*/
/* solhint-disable */
constructor(address stoToken, address wallet) public onlyValidAddress(stoToken) onlyValidAddress(wallet) {
_token = stoToken;
_wallet = wallet;
transferOwnership(wallet);
}
/* solhint-enable */
/**
* @notice deposit payoutDividend tokens (ERC20) into this contract
* @param payoutToken ERC20 address of the token used for payout the current dividend
* @param amount uint256 total amount of the ERC20 tokens deposited to payout to all
* token holders as of previous block from when this function is included
* @dev The owner should first call approve(STODividendsContractAddress, amount)
* in the payoutToken contract
*/
function depositDividend(address payoutToken, uint256 recordDate, uint256 claimPeriod, uint256 amount)
public
onlyOwner
onlyValidAddress(payoutToken)
{
require(amount > 0, "invalid deposit amount");
require(recordDate > 0, "invalid recordDate");
require(claimPeriod > 0, "invalid claimPeriod");
IERC20(payoutToken).safeTransferFrom(msg.sender, address(this), amount); // transfer ERC20 to this contract
totalBalance[payoutToken] = totalBalance[payoutToken].add(amount); // update global balance of ERC20 token
dividends.push(
Dividend(
recordDate,
claimPeriod,
payoutToken,
amount,
0,
ERC20Snapshot(_token).totalSupplyAt(block.timestamp), //eligible supply
false
)
);
emit DepositedDividend((dividends.length).sub(1), payoutToken, amount, block.timestamp, claimPeriod);
}
/** TODO: check for "recycle" or "recycled" - replace with reclaimed
* @notice Token holder claim their dividends
* @param dividendIndex The index of the deposit dividend to be claimed.
*/
function claimDividend(uint256 dividendIndex)
public
validDividendIndex(dividendIndex)
{
Dividend storage dividend = dividends[dividendIndex];
require(dividend.claimed[msg.sender] == false, "Dividend already claimed");
require(dividend.reclaimed == false, "Dividend already reclaimed");
require((dividend.recordDate).add(dividend.claimPeriod) >= block.timestamp, "No longer claimable");
_claimDividend(dividendIndex, msg.sender);
}
/**
* @notice Claim dividends from a startingIndex to all possible dividends
* @param startingIndex The index from which the loop of claiming dividend starts
* @dev To claim all dividends from the beginning, set this value to 0.
* This parameter may help reducing the risk of running out-of-gas due to many loops
*/
function claimAllDividends(uint256 startingIndex)
public
validDividendIndex(startingIndex)
{
for (uint256 i = startingIndex; i < dividends.length; i++) {
Dividend storage dividend = dividends[i];
if (dividend.claimed[msg.sender] == false
&& (dividend.recordDate).add(dividend.claimPeriod) >= block.timestamp && dividend.reclaimed == false) {
_claimDividend(i, msg.sender);
}
}
}
/**
* @notice recycle the dividend. Transfer tokens back to the _wallet
* @param dividendIndex the storage index of the dividend in the pushed array.
*/
function reclaimDividend(uint256 dividendIndex)
public
onlyOwner
validDividendIndex(dividendIndex)
{
Dividend storage dividend = dividends[dividendIndex];
require(dividend.reclaimed == false, "Dividend already reclaimed");
require((dividend.recordDate).add(dividend.claimPeriod) < block.timestamp, "Still claimable");
dividend.reclaimed = true;
uint256 recycledAmount = (dividend.payoutAmount).sub(dividend.claimedAmount);
totalBalance[dividend.payoutToken] = totalBalance[dividend.payoutToken].sub(recycledAmount);
IERC20(dividend.payoutToken).safeTransfer(_wallet, recycledAmount);
emit RecycledDividend(dividendIndex, block.timestamp, recycledAmount);
}
/**
* @notice get dividend info at index
* @param dividendIndex the storage index of the dividend in the pushed array.
* @return recordDate (uint256) of the dividend
* @return claimPeriod (uint256) of the dividend
* @return payoutToken (address) of the dividend
* @return payoutAmount (uint256) of the dividend
* @return claimedAmount (uint256) of the dividend
* @return the total supply (uint256) of the dividend
* @return Whether this dividend was reclaimed (bool) of the dividend
*/
function getDividend(uint256 dividendIndex)
public
view
validDividendIndex(dividendIndex)
returns (uint256, uint256, address, uint256, uint256, uint256, bool)
{
Dividend memory result = dividends[dividendIndex];
return (
result.recordDate,
result.claimPeriod,
address(result.payoutToken),
result.payoutAmount,
result.claimedAmount,
result.totalSupply,
result.reclaimed);
}
/**
* @notice Internal function that claim the dividend
* @param dividendIndex the index of the dividend to be claimed
* @param account address of the account to receive dividend
*/
function _claimDividend(uint256 dividendIndex, address account) internal {
Dividend storage dividend = dividends[dividendIndex];
uint256 claimAmount = _calcClaim(dividendIndex, account);
dividend.claimed[account] = true;
dividend.claimedAmount = (dividend.claimedAmount).add(claimAmount);
totalBalance[dividend.payoutToken] = totalBalance[dividend.payoutToken].sub(claimAmount);
IERC20(dividend.payoutToken).safeTransfer(account, claimAmount);
emit ReclaimedDividend(dividendIndex, account, claimAmount);
}
/**
* @notice calculate dividend claim amount
*/
function _calcClaim(uint256 dividendIndex, address account) internal view returns (uint256) {
Dividend memory dividend = dividends[dividendIndex];
uint256 balance = ERC20Snapshot(_token).balanceOfAt(account, dividend.recordDate);
return balance.mul(dividend.payoutAmount).div(dividend.totalSupply);
}
}
// File: contracts/examples/ExampleTokenFactory.sol
/**
* @title Example Token Factory Contract
* @author Validity Labs AG <[email protected]>
*/
pragma solidity 0.5.7;
/* solhint-disable max-line-length */
/* solhint-disable separate-by-one-line-in-contract */
contract ExampleTokenFactory is Managed {
mapping(address => address) public tokenToDividend;
/*** EVENTS ***/
event DeployedToken(address indexed contractAddress, string name, string symbol, address indexed clientOwner);
event DeployedDividend(address indexed contractAddress);
/*** FUNCTIONS ***/
function newToken(string calldata _name, string calldata _symbol, address _clientOwner, uint256 _initialAmount) external onlyOwner {
address tokenAddress = _deployToken(_name, _symbol, _clientOwner, _initialAmount);
}
function newTokenAndDividend(string calldata _name, string calldata _symbol, address _clientOwner, uint256 _initialAmount) external onlyOwner {
address tokenAddress = _deployToken(_name, _symbol, _clientOwner, _initialAmount);
address dividendAddress = _deployDividend(tokenAddress, _clientOwner);
tokenToDividend[tokenAddress] = dividendAddress;
}
/** MANGER FUNCTIONS **/
/**
* @notice Prospectus and Quarterly Reports
* @dev string null check is done at the token level - see ERC20DocumentRegistry
* @param _est address of the targeted EST
* @param _documentUri string IPFS URI to the document
*/
function addDocument(address _est, string calldata _documentUri) external onlyValidAddress(_est) onlyManager {
ExampleSecurityToken(_est).addDocument(_documentUri);
}
/**
* @notice pause or unpause individual EST
* @param _est address of the targeted EST
*/
function togglePauseEST(address _est) public onlyValidAddress(_est) onlyManager {
ExampleSecurityToken est = ExampleSecurityToken(_est);
bool result = est.paused();
result ? est.unpause() : est.pause();
}
/**
* @notice force the transfer of tokens from _confiscatee to _receiver
* @param _est address of the targeted EST
* @param _confiscatee address to confiscate tokens from
* @param _receiver address to receive the balance of tokens
* @param _amount uint256 amount to take away from _confiscatee
*/
function forceTransferEST(address _est, address _confiscatee, address _receiver, uint256 _amount)
public
onlyValidAddress(_est)
onlyValidAddress(_confiscatee)
onlyValidAddress(_receiver)
onlyManager {
require(_amount > 0, "invalid amount");
ExampleSecurityToken est = ExampleSecurityToken(_est);
est.forceTransfer(_confiscatee, _receiver, _amount);
}
function _deployToken(string memory _name, string memory _symbol, address _clientOwner, uint256 _initialAmount) internal returns (address) {
require(bytes(_name).length > 0, "name cannot be blank");
require(bytes(_symbol).length > 0, "symbol cannot be blank");
ExampleSecurityToken tokenContract = new ExampleSecurityToken(_name, _symbol, _clientOwner, _initialAmount);
emit DeployedToken(address(tokenContract), _name, _symbol, _clientOwner);
return address(tokenContract);
}
function _deployDividend(address tokenAddress, address wallet) internal returns (address) {
Dividends dividendContract = new Dividends(tokenAddress, wallet);
emit DeployedDividend(address(dividendContract));
return address(dividendContract);
}
}