Transaction Hash:
Block:
11057467 at Oct-15-2020 01:42:09 AM +UTC
Transaction Fee:
0.010114573 ETH
$19.68
Gas Used:
190,841 Gas / 53 Gwei
Emitted Events:
| 59 |
ReversibleICOToken.Sent( operator=[Sender] 0x5bf00a6e363e196129ee892d10c91ca50c344628, from=[Sender] 0x5bf00a6e363e196129ee892d10c91ca50c344628, to=ReversibleICO, amount=8500000000000000000000, data=0x, operatorData=0x )
|
| 60 |
ReversibleICOToken.Transfer( from=[Sender] 0x5bf00a6e363e196129ee892d10c91ca50c344628, to=ReversibleICO, value=8500000000000000000000 )
|
| 61 |
ReversibleICO.ParticipantWithdraw( participantAddress=[Sender] 0x5bf00a6e363e196129ee892d10c91ca50c344628, ethAmount=15657951988639325667, tokenAmount=8500000000000000000000, withdrawCount=4 )
|
| 62 |
ReversibleICO.TransferEvent( typeId=4, relatedAddress=[Sender] 0x5bf00a6e363e196129ee892d10c91ca50c344628, value=15657951988639325667 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
|
0x04668Ec2...D451c8F7F
Miner
| (zhizhu.top) | 930.139650401178588253 Eth | 930.149764974178588253 Eth | 0.010114573 | |
| 0x5Bf00A6e...50c344628 |
5.149168218097420707 Eth
Nonce: 326
|
20.797005633736746374 Eth
Nonce: 327
| 15.647837415639325667 | ||
| 0xA8b91968...5aec0be6D | |||||
| 0xE417b912...191b2CdF6 | (LUKSO: Reversible ICO) | 3,651.586453370367016047 Eth | 3,635.92850138172769038 Eth | 15.657951988639325667 |
Execution Trace
ReversibleICOToken.transfer( recipient=0xE417b912F6cB6592ec2D71dbF6F2B48191b2CdF6, amount=8500000000000000000000 ) => ( True )
-
ERC1820Registry.getInterfaceImplementer( _addr=0x5Bf00A6e363e196129Ee892D10c91ca50c344628, _interfaceHash=29DDB589B1FB5FC7CF394961C1ADF5F8C6454761ADF795E67FE149F658ABE895 ) => ( 0x0000000000000000000000000000000000000000 ) -
ERC1820Registry.getInterfaceImplementer( _addr=0xE417b912F6cB6592ec2D71dbF6F2B48191b2CdF6, _interfaceHash=B281FC8C12954D22544DB45DE3159A39272895B169A852B314F9CC762E44C53B ) => ( 0xE417b912F6cB6592ec2D71dbF6F2B48191b2CdF6 ) ReversibleICO.tokensReceived( 0x5Bf00A6e363e196129Ee892D10c91ca50c344628, _from=0x5Bf00A6e363e196129Ee892D10c91ca50c344628, 0xE417b912F6cB6592ec2D71dbF6F2B48191b2CdF6, _amount=8500000000000000000000, 0x, 0x )- ETH 15.657951988639325667
0x5bf00a6e363e196129ee892d10c91ca50c344628.CALL( )
- ETH 15.657951988639325667
transfer[ERC777 (ln:343)]
_callTokensToSend[ERC777 (ln:348)]getInterfaceImplementer[ERC777 (ln:556)]tokensToSend[ERC777 (ln:558)]
_move[ERC777 (ln:350)]sub[ERC777 (ln:528)]add[ERC777 (ln:529)]Sent[ERC777 (ln:531)]Transfer[ERC777 (ln:532)]
_callTokensReceived[ERC777 (ln:352)]getInterfaceImplementer[ERC777 (ln:574)]tokensReceived[ERC777 (ln:576)]isContract[ERC777 (ln:578)]
File 1 of 3: ReversibleICOToken
File 2 of 3: ReversibleICO
File 3 of 3: ERC1820Registry
/**
*Submitted for verification at Etherscan.io on 2020-05-13
*/
/*
* source https://github.com/lukso-network/rICO-smart-contracts
* @name LUKSO Token
* @author Micky Socaci <micky@binarzone.com>, Fabian Vogelsteller <@frozeman>
* @license Apachae 2.0
*/
/**
* @dev Implementation of the `IERC777` interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using `_mint`.
*
* Support for ERC20 is included in this contract, as specified by the EIP: both
* the ERC777 and ERC20 interfaces can be safely used when interacting with it.
* Both `IERC777.Sent` and `IERC20.Transfer` events are emitted on token
* movements.
*
* Additionally, the `granularity` value is hard-coded to `1`, meaning that there
* are no special restrictions in the amount of tokens that created, moved, or
* destroyed. This makes integration with ERC20 applications seamless.
*/
pragma solidity ^0.5.0;
interface IERC777 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function granularity() external view returns (uint256);
function totalSupply() external view returns (uint256);
function balanceOf(address owner) external view returns (uint256);
function send(address recipient, uint256 amount, bytes calldata data) external;
function burn(uint256 amount, bytes calldata data) external;
function isOperatorFor(address operator, address tokenHolder) external view returns (bool);
function authorizeOperator(address operator) external;
function revokeOperator(address operator) external;
function defaultOperators() external view returns (address[] memory);
function operatorSend(
address sender,
address recipient,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
) external;
function operatorBurn(
address account,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
) external;
event Sent(
address indexed operator,
address indexed from,
address indexed to,
uint256 amount,
bytes data,
bytes operatorData
);
event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);
event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
event RevokedOperator(address indexed operator, address indexed tokenHolder);
}
interface IERC777Recipient {
function tokensReceived(
address operator,
address from,
address to,
uint256 amount,
bytes calldata userData,
bytes calldata operatorData
) external;
}
interface IERC777Sender {
function tokensToSend(
address operator,
address from,
address to,
uint256 amount,
bytes calldata userData,
bytes calldata operatorData
) external;
}
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
library Address {
function isContract(address account) internal view returns (bool) {
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
assembly { codehash := extcodehash(account) }
return (codehash != 0x0 && codehash != accountHash);
}
function toPayable(address account) internal pure returns (address payable) {
return address(uint160(account));
}
}
interface IERC1820Registry {
function setManager(address account, address newManager) external;
function getManager(address account) external view returns (address);
function setInterfaceImplementer(address account, bytes32 interfaceHash, address implementer) external;
function getInterfaceImplementer(address account, bytes32 interfaceHash) external view returns (address);
function interfaceHash(string calldata interfaceName) external pure returns (bytes32);
function updateERC165Cache(address account, bytes4 interfaceId) external;
function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool);
function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);
event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);
event ManagerChanged(address indexed account, address indexed newManager);
}
contract ERC777 is IERC777, IERC20 {
using SafeMath for uint256;
using Address for address;
IERC1820Registry private _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
mapping(address => uint256) private _balances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
bytes32 constant private TOKENS_SENDER_INTERFACE_HASH =
0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895;
bytes32 constant private TOKENS_RECIPIENT_INTERFACE_HASH =
0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b;
address[] private _defaultOperatorsArray;
mapping(address => bool) private _defaultOperators;
mapping(address => mapping(address => bool)) private _operators;
mapping(address => mapping(address => bool)) private _revokedDefaultOperators;
mapping (address => mapping (address => uint256)) private _allowances;
constructor(
string memory name,
string memory symbol,
address[] memory defaultOperators
) public {
_name = name;
_symbol = symbol;
_defaultOperatorsArray = defaultOperators;
for (uint256 i = 0; i < _defaultOperatorsArray.length; i++) {
_defaultOperators[_defaultOperatorsArray[i]] = true;
}
_erc1820.setInterfaceImplementer(address(this), keccak256("ERC777Token"), address(this));
_erc1820.setInterfaceImplementer(address(this), keccak256("ERC20Token"), address(this));
}
function name() public view returns (string memory) {
return _name;
}
function symbol() public view returns (string memory) {
return _symbol;
}
function decimals() public pure returns (uint8) {
return 18;
}
function granularity() public view returns (uint256) {
return 1;
}
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
function balanceOf(address tokenHolder) public view returns (uint256) {
return _balances[tokenHolder];
}
function send(address recipient, uint256 amount, bytes calldata data) external {
_send(msg.sender, msg.sender, recipient, amount, data, "", true);
}
function transfer(address recipient, uint256 amount) external returns (bool) {
require(recipient != address(0), "ERC777: transfer to the zero address");
address from = msg.sender;
_callTokensToSend(from, from, recipient, amount, "", "");
_move(from, from, recipient, amount, "", "");
_callTokensReceived(from, from, recipient, amount, "", "", false);
return true;
}
function burn(uint256 amount, bytes calldata data) external {
_burn(msg.sender, msg.sender, amount, data, "");
}
function isOperatorFor(
address operator,
address tokenHolder
) public view returns (bool) {
return operator == tokenHolder ||
(_defaultOperators[operator] && !_revokedDefaultOperators[tokenHolder][operator]) ||
_operators[tokenHolder][operator];
}
function authorizeOperator(address operator) external {
require(msg.sender != operator, "ERC777: authorizing self as operator");
if (_defaultOperators[operator]) {
delete _revokedDefaultOperators[msg.sender][operator];
} else {
_operators[msg.sender][operator] = true;
}
emit AuthorizedOperator(operator, msg.sender);
}
function revokeOperator(address operator) external {
require(operator != msg.sender, "ERC777: revoking self as operator");
if (_defaultOperators[operator]) {
_revokedDefaultOperators[msg.sender][operator] = true;
} else {
delete _operators[msg.sender][operator];
}
emit RevokedOperator(operator, msg.sender);
}
function defaultOperators() public view returns (address[] memory) {
return _defaultOperatorsArray;
}
function operatorSend(
address sender,
address recipient,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
)
external
{
require(isOperatorFor(msg.sender, sender), "ERC777: caller is not an operator for holder");
_send(msg.sender, sender, recipient, amount, data, operatorData, true);
}
function operatorBurn(address account, uint256 amount, bytes calldata data, bytes calldata operatorData) external {
require(isOperatorFor(msg.sender, account), "ERC777: caller is not an operator for holder");
_burn(msg.sender, account, amount, data, operatorData);
}
function allowance(address holder, address spender) public view returns (uint256) {
return _allowances[holder][spender];
}
function approve(address spender, uint256 value) external returns (bool) {
address holder = msg.sender;
_approve(holder, spender, value);
return true;
}
function transferFrom(address holder, address recipient, uint256 amount) external returns (bool) {
require(recipient != address(0), "ERC777: transfer to the zero address");
require(holder != address(0), "ERC777: transfer from the zero address");
address spender = msg.sender;
_callTokensToSend(spender, holder, recipient, amount, "", "");
_move(spender, holder, recipient, amount, "", "");
_approve(holder, spender, _allowances[holder][spender].sub(amount));
_callTokensReceived(spender, holder, recipient, amount, "", "", false);
return true;
}
function _mint(
address operator,
address account,
uint256 amount,
bytes memory userData,
bytes memory operatorData
)
internal
{
require(account != address(0), "ERC777: mint to the zero address");
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
_callTokensReceived(operator, address(0), account, amount, userData, operatorData, true);
emit Minted(operator, account, amount, userData, operatorData);
emit Transfer(address(0), account, amount);
}
function _send(
address operator,
address from,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData,
bool requireReceptionAck
)
private
{
require(from != address(0), "ERC777: send from the zero address");
require(to != address(0), "ERC777: send to the zero address");
_callTokensToSend(operator, from, to, amount, userData, operatorData);
_move(operator, from, to, amount, userData, operatorData);
_callTokensReceived(operator, from, to, amount, userData, operatorData, requireReceptionAck);
}
function _burn(
address operator,
address from,
uint256 amount,
bytes memory data,
bytes memory operatorData
)
internal
{
require(from != address(0), "ERC777: burn from the zero address");
_callTokensToSend(operator, from, address(0), amount, data, operatorData);
_totalSupply = _totalSupply.sub(amount);
_balances[from] = _balances[from].sub(amount);
emit Burned(operator, from, amount, data, operatorData);
emit Transfer(from, address(0), amount);
}
function _move(
address operator,
address from,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData
)
internal
{
_balances[from] = _balances[from].sub(amount);
_balances[to] = _balances[to].add(amount);
emit Sent(operator, from, to, amount, userData, operatorData);
emit Transfer(from, to, amount);
}
function _approve(address holder, address spender, uint256 value) private {
require(spender != address(0), "ERC777: approve to the zero address");
_allowances[holder][spender] = value;
emit Approval(holder, spender, value);
}
function _callTokensToSend(
address operator,
address from,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData
)
private
{
address implementer = _erc1820.getInterfaceImplementer(from, TOKENS_SENDER_INTERFACE_HASH);
if (implementer != address(0)) {
IERC777Sender(implementer).tokensToSend(operator, from, to, amount, userData, operatorData);
}
}
function _callTokensReceived(
address operator,
address from,
address to,
uint256 amount,
bytes memory userData,
bytes memory operatorData,
bool requireReceptionAck
)
private
{
address implementer = _erc1820.getInterfaceImplementer(to, TOKENS_RECIPIENT_INTERFACE_HASH);
if (implementer != address(0)) {
IERC777Recipient(implementer).tokensReceived(operator, from, to, amount, userData, operatorData);
} else if (requireReceptionAck) {
require(!to.isContract(), "ERC777: token recipient contract has no implementer for ERC777TokensRecipient");
}
}
}
interface ReversibleICO {
function getParticipantReservedTokens(address) external view returns (uint256);
}
contract ReversibleICOToken is ERC777 {
ReversibleICO public rICO;
bool public frozen;
bool public initialized;
address public deployingAddress;
address public tokenGenesisAddress;
address public migrationAddress;
address public freezerAddress;
address public rescuerAddress;
event SetRICOaddress(address indexed rICOAddress);
event SetMigrationAddress(address indexed migrationAddress);
event Frozen(address indexed freezerAddress);
event Unfrozen(address indexed freezerAddress);
event RemovedFreezer(address indexed freezerAddress);
event ChangedRICO(address indexed rICOAddress, address indexed rescuerAddress);
constructor(
string memory name,
string memory symbol,
address[] memory _defaultOperators
)
ERC777(name, symbol, _defaultOperators)
public
{
deployingAddress = msg.sender;
}
function init(
address _ricoAddress,
address _freezerAddress,
address _rescuerAddress,
address _tokenGenesisAddress,
uint256 _initialSupply
)
public
isNotInitialized
onlyDeployingAddress
{
require(_freezerAddress != address(0), "_freezerAddress cannot be 0x");
require(_rescuerAddress != address(0), "_rescuerAddress cannot be 0x");
require(_tokenGenesisAddress != address(0), "_tokenGenesisAddress cannot be 0x");
tokenGenesisAddress = _tokenGenesisAddress;
freezerAddress = _freezerAddress;
rescuerAddress = _rescuerAddress;
_mint(_tokenGenesisAddress, _tokenGenesisAddress, _initialSupply, "", "");
if(_ricoAddress != address(0)) {
rICO = ReversibleICO(_ricoAddress);
emit SetRICOaddress(_ricoAddress);
}
initialized = true;
}
function setRICOaddress(address _ricoAddress)
public
onlyTokenGenesisAddress
{
require(address(rICO) == address(0), "rICO address already set!");
require(_ricoAddress != address(0), "rICO address cannot be 0x.");
rICO = ReversibleICO(_ricoAddress);
emit SetRICOaddress(_ricoAddress);
}
function setMigrationAddress(address _migrationAddress)
public
onlyTokenGenesisAddress
{
migrationAddress = _migrationAddress;
emit SetMigrationAddress(migrationAddress);
}
function removeFreezer()
public
onlyFreezerAddress
isNotFrozen
{
freezerAddress = address(0);
emit RemovedFreezer(freezerAddress);
}
function freeze() public onlyFreezerAddress {
frozen = true;
emit Frozen(freezerAddress);
}
function unfreeze() public onlyFreezerAddress {
frozen = false;
emit Unfrozen(freezerAddress);
}
function changeRICO(address _newRicoAddress)
public
onlyRescuerAddress
isFrozen
{
rICO = ReversibleICO(_newRicoAddress);
emit ChangedRICO(_newRicoAddress, rescuerAddress);
}
function getLockedBalance(address _owner) public view returns(uint256) {
if(address(rICO) != address(0)) {
return rICO.getParticipantReservedTokens(_owner);
} else {
return 0;
}
}
function getUnlockedBalance(address _owner) public view returns(uint256) {
uint256 balance = balanceOf(_owner);
if(address(rICO) != address(0)) {
uint256 locked = rICO.getParticipantReservedTokens(_owner);
if(balance > 0 && locked > 0) {
if(balance >= locked) {
return balance.sub(locked);
} else {
return 0;
}
}
}
return balance;
}
function _move(
address _operator,
address _from,
address _to,
uint256 _amount,
bytes memory _userData,
bytes memory _operatorData
)
internal
isNotFrozen
isInitialized
{
if(
_to == address(rICO) ||
_to == migrationAddress
) {
require(_amount <= balanceOf(_from), "Sending failed: Insufficient funds");
} else {
require(_amount <= getUnlockedBalance(_from), "Sending failed: Insufficient funds");
}
ERC777._move(_operator, _from, _to, _amount, _userData, _operatorData);
}
function _burn(
address _operator,
address _from,
uint256 _amount,
bytes memory _data,
bytes memory _operatorData
)
internal
isNotFrozen
isInitialized
{
require(_amount <= getUnlockedBalance(_from), "Burning failed: Insufficient funds");
ERC777._burn(_operator, _from, _amount, _data, _operatorData);
}
modifier onlyDeployingAddress() {
require(msg.sender == deployingAddress, "Only the deployer can call this method.");
_;
}
modifier onlyTokenGenesisAddress() {
require(msg.sender == tokenGenesisAddress, "Only the tokenGenesisAddress can call this method.");
_;
}
modifier onlyFreezerAddress() {
require(msg.sender == freezerAddress, "Only the freezer address can call this method.");
_;
}
modifier onlyRescuerAddress() {
require(msg.sender == rescuerAddress, "Only the rescuer address can call this method.");
_;
}
modifier isInitialized() {
require(initialized == true, "Contract must be initialized.");
_;
}
modifier isNotInitialized() {
require(initialized == false, "Contract is already initialized.");
_;
}
modifier isFrozen() {
require(frozen == true, "Token contract not frozen.");
_;
}
modifier isNotFrozen() {
require(frozen == false, "Token contract is frozen!");
_;
}
}File 2 of 3: ReversibleICO
/*
* Submitted for verification at etherscan.io on 2020-06-10
*
* ________ ____ _ __ __ ______________
* /_ __/ /_ ___ / __ \___ _ _____ __________(_) /_ / /__ / _/ ____/ __ \
* / / / __ \/ _ \ / /_/ / _ \ | / / _ \/ ___/ ___/ / __ \/ / _ \ / // / / / / /
* / / / / / / __/ / _, _/ __/ |/ / __/ / (__ ) / /_/ / / __/ _/ // /___/ /_/ /
* /_/ /_/ /_/\___/ /_/ |_|\___/|___/\___/_/ /____/_/_.___/_/\___/ /___/\____/\____/
*
*
* source https://github.com/lukso-network/rICO-smart-contracts
* @name Reversible ICO
* @author Fabian Vogelsteller <@frozeman>, Micky Socaci <micky@binarzone.com>, Marjorie Hernandez <marjorie@lukso.io>
* @license Apache 2.0
*
* Readme more about it here https://medium.com/lukso/rico-the-reversible-ico-5392bf64318b
*/
pragma solidity ^0.5.0;
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}
interface IERC777 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function granularity() external view returns (uint256);
function totalSupply() external view returns (uint256);
function balanceOf(address owner) external view returns (uint256);
function send(address recipient, uint256 amount, bytes calldata data) external;
function burn(uint256 amount, bytes calldata data) external;
function isOperatorFor(address operator, address tokenHolder) external view returns (bool);
function authorizeOperator(address operator) external;
function revokeOperator(address operator) external;
function defaultOperators() external view returns (address[] memory);
function operatorSend(
address sender,
address recipient,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
) external;
function operatorBurn(
address account,
uint256 amount,
bytes calldata data,
bytes calldata operatorData
) external;
event Sent(
address indexed operator,
address indexed from,
address indexed to,
uint256 amount,
bytes data,
bytes operatorData
);
event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);
event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
event RevokedOperator(address indexed operator, address indexed tokenHolder);
}
interface IERC777Recipient {
function tokensReceived(
address operator,
address from,
address to,
uint256 amount,
bytes calldata userData,
bytes calldata operatorData
) external;
}
interface IERC1820Registry {
function setManager(address account, address newManager) external;
function getManager(address account) external view returns (address);
function setInterfaceImplementer(address account, bytes32 interfaceHash, address implementer) external;
function getInterfaceImplementer(address account, bytes32 interfaceHash) external view returns (address);
function interfaceHash(string calldata interfaceName) external pure returns (bytes32);
function updateERC165Cache(address account, bytes4 interfaceId) external;
function implementsERC165Interface(address account, bytes4 interfaceId) external view returns (bool);
function implementsERC165InterfaceNoCache(address account, bytes4 interfaceId) external view returns (bool);
event InterfaceImplementerSet(address indexed account, bytes32 indexed interfaceHash, address indexed implementer);
event ManagerChanged(address indexed account, address indexed newManager);
}
contract ReversibleICO is IERC777Recipient {
using SafeMath for uint256;
IERC1820Registry private ERC1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
bytes32 constant private TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient");
bool public initialized;
bool public frozen;
uint256 public frozenPeriod;
uint256 public freezeStart;
address public deployingAddress;
address public tokenAddress;
address public projectAddress;
address public whitelistingAddress;
address public freezerAddress;
address public rescuerAddress;
uint256 public initialTokenSupply;
uint256 public tokenSupply;
uint256 public committedETH;
uint256 public pendingETH;
uint256 public canceledETH;
uint256 public withdrawnETH;
uint256 public projectWithdrawCount;
uint256 public projectWithdrawnETH;
uint256 public minContribution = 0.1 ether;
uint256 public maxContribution = 4000 ether;
mapping(uint8 => Stage) public stages;
uint8 public stageCount;
mapping(address => Participant) public participants;
mapping(uint256 => address) public participantsById;
uint256 public participantCount;
uint256 public commitPhasePrice;
uint256 public commitPhaseStartBlock;
uint256 public commitPhaseEndBlock;
uint256 public commitPhaseBlockCount;
uint256 public buyPhaseStartBlock;
uint256 public buyPhaseEndBlock;
uint256 public buyPhaseBlockCount;
uint256 internal _projectCurrentlyReservedETH;
uint256 internal _projectUnlockedETH;
uint256 internal _projectLastBlock;
struct Stage {
uint256 tokenLimit;
uint256 tokenPrice;
}
struct Participant {
bool whitelisted;
uint32 contributions;
uint32 withdraws;
uint256 firstContributionBlock;
uint256 reservedTokens;
uint256 committedETH;
uint256 pendingETH;
uint256 _currentReservedTokens;
uint256 _unlockedTokens;
uint256 _lastBlock;
mapping(uint8 => ParticipantStageDetails) stages;
}
struct ParticipantStageDetails {
uint256 pendingETH;
}
event PendingContributionAdded(address indexed participantAddress, uint256 indexed amount, uint32 indexed contributionId, uint8 stageId);
event PendingContributionsCanceled(address indexed participantAddress, uint256 indexed amount, uint32 indexed contributionId);
event WhitelistApproved(address indexed participantAddress, uint256 indexed pendingETH, uint32 indexed contributions);
event WhitelistRejected(address indexed participantAddress, uint256 indexed pendingETH, uint32 indexed contributions);
event ContributionsAccepted(address indexed participantAddress, uint256 indexed ethAmount, uint256 indexed tokenAmount, uint8 stageId);
event ProjectWithdraw(address indexed projectAddress, uint256 indexed amount, uint32 indexed withdrawCount);
event ParticipantWithdraw(address indexed participantAddress, uint256 indexed ethAmount, uint256 indexed tokenAmount, uint32 withdrawCount);
event StageChanged(uint8 indexed stageId, uint256 indexed tokenLimit, uint256 indexed tokenPrice, uint256 effectiveBlockNumber);
event WhitelistingAddressChanged(address indexed whitelistingAddress, uint8 indexed stageId, uint256 indexed effectiveBlockNumber);
event FreezerAddressChanged(address indexed freezerAddress, uint8 indexed stageId, uint256 indexed effectiveBlockNumber);
event SecurityFreeze(address indexed freezerAddress, uint8 indexed stageId, uint256 indexed effectiveBlockNumber);
event SecurityUnfreeze(address indexed freezerAddress, uint8 indexed stageId, uint256 indexed effectiveBlockNumber);
event SecurityDisableEscapeHatch(address indexed freezerAddress, uint8 indexed stageId, uint256 indexed effectiveBlockNumber);
event SecurityEscapeHatch(address indexed rescuerAddress, address indexed to, uint8 indexed stageId, uint256 effectiveBlockNumber);
event TransferEvent (
uint8 indexed typeId,
address indexed relatedAddress,
uint256 indexed value
);
enum TransferTypes {
NOT_SET,
WHITELIST_REJECTED,
CONTRIBUTION_CANCELED,
CONTRIBUTION_ACCEPTED_OVERFLOW,
PARTICIPANT_WITHDRAW,
PARTICIPANT_WITHDRAW_OVERFLOW,
PROJECT_WITHDRAWN,
FROZEN_ESCAPEHATCH_TOKEN,
FROZEN_ESCAPEHATCH_ETH
}
constructor() public {
deployingAddress = msg.sender;
ERC1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this));
}
function init(
address _tokenAddress,
address _whitelistingAddress,
address _freezerAddress,
address _rescuerAddress,
address _projectAddress,
uint256 _commitPhaseStartBlock,
uint256 _buyPhaseStartBlock,
uint256 _buyPhaseEndBlock,
uint256 _initialPrice,
uint8 _stageCount,
uint256 _stageTokenLimitIncrease,
uint256 _stagePriceIncrease
)
public
onlyDeployingAddress
isNotInitialized
{
require(_tokenAddress != address(0), "_tokenAddress cannot be 0x");
require(_whitelistingAddress != address(0), "_whitelistingAddress cannot be 0x");
require(_freezerAddress != address(0), "_freezerAddress cannot be 0x");
require(_rescuerAddress != address(0), "_rescuerAddress cannot be 0x");
require(_projectAddress != address(0), "_projectAddress cannot be 0x");
tokenAddress = _tokenAddress;
whitelistingAddress = _whitelistingAddress;
freezerAddress = _freezerAddress;
rescuerAddress = _rescuerAddress;
projectAddress = _projectAddress;
commitPhaseStartBlock = _commitPhaseStartBlock;
commitPhaseEndBlock = _buyPhaseStartBlock.sub(1);
commitPhaseBlockCount = commitPhaseEndBlock.sub(commitPhaseStartBlock).add(1);
commitPhasePrice = _initialPrice;
stageCount = _stageCount;
Stage storage commitPhase = stages[0];
commitPhase.tokenLimit = _stageTokenLimitIncrease;
commitPhase.tokenPrice = _initialPrice;
uint256 previousStageTokenLimit = _stageTokenLimitIncrease;
for (uint8 i = 1; i <= _stageCount; i++) {
Stage storage byStage = stages[i];
byStage.tokenLimit = previousStageTokenLimit.add(_stageTokenLimitIncrease);
previousStageTokenLimit = byStage.tokenLimit;
byStage.tokenPrice = _initialPrice.add(_stagePriceIncrease.mul(i));
}
buyPhaseStartBlock = _buyPhaseStartBlock;
buyPhaseEndBlock = _buyPhaseEndBlock;
buyPhaseBlockCount = buyPhaseEndBlock.sub(buyPhaseStartBlock).add(1);
initialized = true;
}
function()
external
payable
isInitialized
isNotFrozen
{
Participant storage participantStats = participants[msg.sender];
if (participantStats.whitelisted == true && participantStats.contributions > 0) {
commit();
} else {
require(msg.value < minContribution, 'To contribute call commit() [0x3c7a3aff] and send ETH along.');
cancelPendingContributions(msg.sender, msg.value);
}
}
function tokensReceived(
address,
address _from,
address,
uint256 _amount,
bytes calldata,
bytes calldata
)
external
isInitialized
isNotFrozen
{
require(msg.sender == tokenAddress, "Unknown token contract sent tokens.");
if (_from == projectAddress) {
tokenSupply = tokenSupply.add(_amount);
initialTokenSupply = initialTokenSupply.add(_amount);
} else {
withdraw(_from, _amount);
}
}
function commit()
public
payable
isInitialized
isNotFrozen
isRunning
{
require(msg.value >= minContribution, "Value sent is less than the minimum contribution.");
uint8 currentStage = getCurrentStage();
Participant storage participantStats = participants[msg.sender];
ParticipantStageDetails storage byStage = participantStats.stages[currentStage];
require(participantStats.committedETH.add(msg.value) <= maxContribution, "Value sent is larger than the maximum contribution.");
if (participantStats.contributions == 0) {
participantsById[participantCount] = msg.sender;
participantCount++;
}
participantStats.contributions++;
participantStats.pendingETH = participantStats.pendingETH.add(msg.value);
byStage.pendingETH = byStage.pendingETH.add(msg.value);
pendingETH = pendingETH.add(msg.value);
emit PendingContributionAdded(
msg.sender,
msg.value,
uint32(participantStats.contributions),
currentStage
);
if (participantStats.whitelisted == true) {
acceptContributions(msg.sender);
}
}
function cancel()
external
payable
isInitialized
isNotFrozen
{
cancelPendingContributions(msg.sender, msg.value);
}
function whitelist(address[] calldata _addresses, bool _approve)
external
onlyWhitelistingAddress
isInitialized
isNotFrozen
isRunning
{
require(_addresses.length > 0, "No addresses given to whitelist.");
for (uint256 i = 0; i < _addresses.length; i++) {
address participantAddress = _addresses[i];
Participant storage participantStats = participants[participantAddress];
if (_approve) {
if (participantStats.whitelisted == false) {
participantStats.whitelisted = true;
emit WhitelistApproved(participantAddress, participantStats.pendingETH, uint32(participantStats.contributions));
}
acceptContributions(participantAddress);
} else {
participantStats.whitelisted = false;
emit WhitelistRejected(participantAddress, participantStats.pendingETH, uint32(participantStats.contributions));
cancelPendingContributions(participantAddress, 0);
}
}
}
function projectTokenWithdraw(uint256 _tokenAmount)
external
onlyProjectAddress
isInitialized
{
require(_tokenAmount <= tokenSupply, "Requested amount too high, not enough tokens available.");
tokenSupply = tokenSupply.sub(_tokenAmount);
initialTokenSupply = initialTokenSupply.sub(_tokenAmount);
IERC777(tokenAddress).send(projectAddress, _tokenAmount, "");
}
function projectWithdraw(uint256 _ethAmount)
external
onlyProjectAddress
isInitialized
isNotFrozen
{
calcProjectAllocation();
uint256 availableForWithdraw = _projectUnlockedETH.sub(projectWithdrawnETH);
require(_ethAmount <= availableForWithdraw, "Requested amount too high, not enough ETH unlocked.");
projectWithdrawCount++;
projectWithdrawnETH = projectWithdrawnETH.add(_ethAmount);
emit ProjectWithdraw(
projectAddress,
_ethAmount,
uint32(projectWithdrawCount)
);
emit TransferEvent(
uint8(TransferTypes.PROJECT_WITHDRAWN),
projectAddress,
_ethAmount
);
address(uint160(projectAddress)).transfer(_ethAmount);
}
function changeStage(uint8 _stageId, uint256 _tokenLimit, uint256 _tokenPrice)
external
onlyProjectAddress
isInitialized
{
stages[_stageId].tokenLimit = _tokenLimit;
stages[_stageId].tokenPrice = _tokenPrice;
if(_stageId > stageCount) {
stageCount = _stageId;
}
emit StageChanged(_stageId, _tokenLimit, _tokenPrice, getCurrentEffectiveBlockNumber());
}
function changeWhitelistingAddress(address _newAddress)
external
onlyProjectAddress
isInitialized
{
whitelistingAddress = _newAddress;
emit WhitelistingAddressChanged(whitelistingAddress, getCurrentStage(), getCurrentEffectiveBlockNumber());
}
function changeFreezerAddress(address _newAddress)
external
onlyProjectAddress
isInitialized
{
freezerAddress = _newAddress;
emit FreezerAddressChanged(freezerAddress, getCurrentStage(), getCurrentEffectiveBlockNumber());
}
function freeze()
external
onlyFreezerAddress
isNotFrozen
{
frozen = true;
freezeStart = getCurrentEffectiveBlockNumber();
emit SecurityFreeze(freezerAddress, getCurrentStage(), freezeStart);
}
function unfreeze()
external
onlyFreezerAddress
isFrozen
{
uint256 currentBlock = getCurrentEffectiveBlockNumber();
frozen = false;
frozenPeriod = frozenPeriod.add(
currentBlock.sub(freezeStart)
);
emit SecurityUnfreeze(freezerAddress, getCurrentStage(), currentBlock);
}
function disableEscapeHatch()
external
onlyFreezerAddress
isNotFrozen
{
freezerAddress = address(0);
rescuerAddress = address(0);
emit SecurityDisableEscapeHatch(freezerAddress, getCurrentStage(), getCurrentEffectiveBlockNumber());
}
function escapeHatch(address _to)
external
onlyRescuerAddress
isFrozen
{
require(getCurrentEffectiveBlockNumber() == freezeStart.add(18000), 'Let it cool.. Wait at least ~3 days (18000 blk) before moving anything.');
uint256 tokenBalance = IERC777(tokenAddress).balanceOf(address(this));
uint256 ethBalance = address(this).balance;
IERC777(tokenAddress).send(_to, tokenBalance, "");
address(uint160(_to)).transfer(ethBalance);
emit SecurityEscapeHatch(rescuerAddress, _to, getCurrentStage(), getCurrentEffectiveBlockNumber());
emit TransferEvent(uint8(TransferTypes.FROZEN_ESCAPEHATCH_TOKEN), _to, tokenBalance);
emit TransferEvent(uint8(TransferTypes.FROZEN_ESCAPEHATCH_ETH), _to, ethBalance);
}
function getUnlockedProjectETH() public view returns (uint256) {
uint256 newlyUnlockedEth = calcUnlockedAmount(_projectCurrentlyReservedETH, _projectLastBlock);
return _projectUnlockedETH
.add(newlyUnlockedEth);
}
function getAvailableProjectETH() public view returns (uint256) {
return getUnlockedProjectETH()
.sub(projectWithdrawnETH);
}
function getParticipantReservedTokens(address _participantAddress) public view returns (uint256) {
Participant storage participantStats = participants[_participantAddress];
if(participantStats._currentReservedTokens == 0) {
return 0;
}
return participantStats._currentReservedTokens.sub(
calcUnlockedAmount(participantStats._currentReservedTokens, participantStats._lastBlock)
);
}
function getParticipantUnlockedTokens(address _participantAddress) public view returns (uint256) {
Participant storage participantStats = participants[_participantAddress];
return participantStats._unlockedTokens.add(
calcUnlockedAmount(participantStats._currentReservedTokens, participantStats._lastBlock)
);
}
function getAvailableTokenAtCurrentStage() public view returns (uint256) {
return stages[getCurrentStage()].tokenLimit.sub(
initialTokenSupply.sub(tokenSupply)
);
}
function getCurrentStage() public view returns (uint8) {
return getStageByTokenLimit(
initialTokenSupply.sub(tokenSupply)
);
}
function getCurrentPrice() public view returns (uint256) {
return getPriceAtStage(getCurrentStage());
}
function getPriceAtStage(uint8 _stageId) public view returns (uint256) {
if (_stageId <= stageCount) {
return stages[_stageId].tokenPrice;
}
return stages[stageCount].tokenPrice;
}
function getPriceForTokenLimit(uint256 _tokenLimit) public view returns (uint256) {
return getPriceAtStage(getStageByTokenLimit(_tokenLimit));
}
function getStageByTokenLimit(uint256 _tokenLimit) public view returns (uint8) {
for (uint8 stageId = 0; stageId <= stageCount; stageId++) {
if(_tokenLimit <= stages[stageId].tokenLimit) {
return stageId;
}
}
return stageCount;
}
function committableEthAtStage(uint8 _stageId, uint8 _currentStage) public view returns (uint256) {
uint256 supply;
if(_stageId < _currentStage) {
return 0;
} else if(_stageId >= stageCount) {
supply = tokenSupply;
} else if(_stageId == _currentStage) {
supply = stages[_currentStage].tokenLimit.sub(
initialTokenSupply.sub(tokenSupply)
);
} else if(_stageId > _currentStage) {
supply = stages[_stageId].tokenLimit.sub(stages[_stageId - 1].tokenLimit);
}
return getEthAmountForTokensAtStage(
supply
, _stageId);
}
function getEthAmountForTokensAtStage(uint256 _tokenAmount, uint8 _stageId) public view returns (uint256) {
return _tokenAmount
.mul(stages[_stageId].tokenPrice)
.div(10 ** 18);
}
function getTokenAmountForEthAtStage(uint256 _ethAmount, uint8 _stageId) public view returns (uint256) {
return _ethAmount
.mul(10 ** 18)
.div(stages[_stageId].tokenPrice);
}
function getCurrentBlockNumber() public view returns (uint256) {
return uint256(block.number);
}
function getCurrentEffectiveBlockNumber() public view returns (uint256) {
return uint256(block.number)
.sub(frozenPeriod);
}
function calcUnlockedAmount(uint256 _amount, uint256 _lastBlock) public view returns (uint256) {
uint256 currentBlock = getCurrentEffectiveBlockNumber();
if(_amount == 0) {
return 0;
}
if (currentBlock >= buyPhaseStartBlock && currentBlock < buyPhaseEndBlock) {
uint256 lastBlock = _lastBlock;
if(lastBlock < buyPhaseStartBlock) {
lastBlock = buyPhaseStartBlock.sub(1);
}
uint256 passedBlocks = currentBlock.sub(lastBlock);
uint256 totalBlockCount = buyPhaseEndBlock.sub(lastBlock);
return _amount.mul(
passedBlocks.mul(10 ** 20)
.div(totalBlockCount)
).div(10 ** 20);
} else if (currentBlock >= buyPhaseEndBlock) {
return _amount;
}
return 0;
}
function sanityCheckProject() internal view {
require(
committedETH == _projectCurrentlyReservedETH.add(_projectUnlockedETH),
'Project Sanity check failed! Reserved + Unlock must equal committedETH'
);
require(
address(this).balance == _projectUnlockedETH.add(_projectCurrentlyReservedETH).add(pendingETH).sub(projectWithdrawnETH),
'Project sanity check failed! balance = Unlock + Reserved - Withdrawn'
);
}
function sanityCheckParticipant(address _participantAddress) internal view {
Participant storage participantStats = participants[_participantAddress];
require(
participantStats.reservedTokens == participantStats._currentReservedTokens.add(participantStats._unlockedTokens),
'Participant Sanity check failed! Reser. + Unlock must equal totalReser'
);
}
function calcProjectAllocation() internal {
uint256 newlyUnlockedEth = calcUnlockedAmount(_projectCurrentlyReservedETH, _projectLastBlock);
_projectCurrentlyReservedETH = _projectCurrentlyReservedETH.sub(newlyUnlockedEth);
_projectUnlockedETH = _projectUnlockedETH.add(newlyUnlockedEth);
_projectLastBlock = getCurrentEffectiveBlockNumber();
sanityCheckProject();
}
function calcParticipantAllocation(address _participantAddress) internal {
Participant storage participantStats = participants[_participantAddress];
participantStats._unlockedTokens = getParticipantUnlockedTokens(_participantAddress);
participantStats._currentReservedTokens = getParticipantReservedTokens(_participantAddress);
participantStats._lastBlock = getCurrentEffectiveBlockNumber();
calcProjectAllocation();
}
function cancelPendingContributions(address _participantAddress, uint256 _sentValue)
internal
isInitialized
isNotFrozen
{
Participant storage participantStats = participants[_participantAddress];
uint256 participantPendingEth = participantStats.pendingETH;
if(participantPendingEth == 0) {
if(_sentValue > 0) {
address(uint160(_participantAddress)).transfer(_sentValue);
}
return;
}
for (uint8 stageId = 0; stageId <= stageCount; stageId++) {
participantStats.stages[stageId].pendingETH = 0;
}
participantStats.pendingETH = 0;
canceledETH = canceledETH.add(participantPendingEth);
pendingETH = pendingETH.sub(participantPendingEth);
emit PendingContributionsCanceled(_participantAddress, participantPendingEth, uint32(participantStats.contributions));
emit TransferEvent(
uint8(TransferTypes.CONTRIBUTION_CANCELED),
_participantAddress,
participantPendingEth
);
address(uint160(_participantAddress)).transfer(participantPendingEth.add(_sentValue));
sanityCheckParticipant(_participantAddress);
sanityCheckProject();
}
function acceptContributions(address _participantAddress)
internal
isInitialized
isNotFrozen
isRunning
{
Participant storage participantStats = participants[_participantAddress];
if (participantStats.pendingETH == 0) {
return;
}
uint8 currentStage = getCurrentStage();
uint256 totalRefundedETH;
uint256 totalNewReservedTokens;
calcParticipantAllocation(_participantAddress);
if(participantStats.committedETH == 0) {
participantStats.firstContributionBlock = participantStats._lastBlock;
}
for (uint8 stageId = 0; stageId <= stageCount; stageId++) {
ParticipantStageDetails storage byStage = participantStats.stages[stageId];
if (byStage.pendingETH == 0) {
continue;
}
if(stageId < currentStage) {
participantStats.stages[currentStage].pendingETH = participantStats.stages[currentStage].pendingETH.add(byStage.pendingETH);
byStage.pendingETH = 0;
continue;
}
uint256 maxCommittableEth = committableEthAtStage(stageId, currentStage);
uint256 newlyCommittableEth = byStage.pendingETH;
uint256 returnEth = 0;
uint256 overflowEth = 0;
if (newlyCommittableEth > maxCommittableEth) {
overflowEth = newlyCommittableEth.sub(maxCommittableEth);
newlyCommittableEth = maxCommittableEth;
if (stageId == stageCount) {
returnEth = overflowEth;
totalRefundedETH = totalRefundedETH.add(returnEth);
} else {
participantStats.stages[stageId + 1].pendingETH = participantStats.stages[stageId + 1].pendingETH.add(overflowEth);
byStage.pendingETH = byStage.pendingETH.sub(overflowEth);
}
}
uint256 newTokenAmount = getTokenAmountForEthAtStage(
newlyCommittableEth, stageId
);
totalNewReservedTokens = totalNewReservedTokens.add(newTokenAmount);
participantStats._currentReservedTokens = participantStats._currentReservedTokens.add(newTokenAmount);
participantStats.reservedTokens = participantStats.reservedTokens.add(newTokenAmount);
participantStats.committedETH = participantStats.committedETH.add(newlyCommittableEth);
participantStats.pendingETH = participantStats.pendingETH.sub(newlyCommittableEth).sub(returnEth);
byStage.pendingETH = byStage.pendingETH.sub(newlyCommittableEth).sub(returnEth);
tokenSupply = tokenSupply.sub(newTokenAmount);
pendingETH = pendingETH.sub(newlyCommittableEth).sub(returnEth);
committedETH = committedETH.add(newlyCommittableEth);
_projectCurrentlyReservedETH = _projectCurrentlyReservedETH.add(newlyCommittableEth);
emit ContributionsAccepted(_participantAddress, newlyCommittableEth, newTokenAmount, stageId);
}
if (totalRefundedETH > 0) {
emit TransferEvent(uint8(TransferTypes.CONTRIBUTION_ACCEPTED_OVERFLOW), _participantAddress, totalRefundedETH);
address(uint160(_participantAddress)).transfer(totalRefundedETH);
}
IERC777(tokenAddress).send(_participantAddress, totalNewReservedTokens, "");
sanityCheckParticipant(_participantAddress);
sanityCheckProject();
}
function withdraw(address _participantAddress, uint256 _returnedTokenAmount)
internal
isInitialized
isNotFrozen
isRunning
{
Participant storage participantStats = participants[_participantAddress];
calcParticipantAllocation(_participantAddress);
require(_returnedTokenAmount > 0, 'You can not withdraw without sending tokens.');
require(participantStats._currentReservedTokens > 0 && participantStats.reservedTokens > 0, 'You can not withdraw, you have no locked tokens.');
uint256 returnedTokenAmount = _returnedTokenAmount;
uint256 overflowingTokenAmount;
uint256 returnEthAmount;
if (returnedTokenAmount > participantStats._currentReservedTokens) {
overflowingTokenAmount = returnedTokenAmount.sub(participantStats._currentReservedTokens);
returnedTokenAmount = participantStats._currentReservedTokens;
}
returnEthAmount = participantStats.committedETH.mul(
returnedTokenAmount.sub(1).mul(10 ** 20)
.div(participantStats.reservedTokens)
).div(10 ** 20);
participantStats.withdraws++;
participantStats._currentReservedTokens = participantStats._currentReservedTokens.sub(returnedTokenAmount);
participantStats.reservedTokens = participantStats.reservedTokens.sub(returnedTokenAmount);
participantStats.committedETH = participantStats.committedETH.sub(returnEthAmount);
tokenSupply = tokenSupply.add(returnedTokenAmount);
withdrawnETH = withdrawnETH.add(returnEthAmount);
committedETH = committedETH.sub(returnEthAmount);
_projectCurrentlyReservedETH = _projectCurrentlyReservedETH.sub(returnEthAmount);
if (overflowingTokenAmount > 0) {
bytes memory data;
emit TransferEvent(uint8(TransferTypes.PARTICIPANT_WITHDRAW_OVERFLOW), _participantAddress, overflowingTokenAmount);
IERC777(tokenAddress).send(_participantAddress, overflowingTokenAmount, data);
}
emit ParticipantWithdraw(_participantAddress, returnEthAmount, returnedTokenAmount, uint32(participantStats.withdraws));
emit TransferEvent(uint8(TransferTypes.PARTICIPANT_WITHDRAW), _participantAddress, returnEthAmount);
address(uint160(_participantAddress)).transfer(returnEthAmount);
sanityCheckParticipant(_participantAddress);
sanityCheckProject();
}
modifier onlyProjectAddress() {
require(msg.sender == projectAddress, "Only the project can call this method.");
_;
}
modifier onlyDeployingAddress() {
require(msg.sender == deployingAddress, "Only the deployer can call this method.");
_;
}
modifier onlyWhitelistingAddress() {
require(msg.sender == whitelistingAddress, "Only the whitelist controller can call this method.");
_;
}
modifier onlyFreezerAddress() {
require(msg.sender == freezerAddress, "Only the freezer address can call this method.");
_;
}
modifier onlyRescuerAddress() {
require(msg.sender == rescuerAddress, "Only the rescuer address can call this method.");
_;
}
modifier isInitialized() {
require(initialized == true, "Contract must be initialized.");
_;
}
modifier isNotInitialized() {
require(initialized == false, "Contract can not be initialized.");
_;
}
modifier isFrozen() {
require(frozen == true, "rICO has to be frozen!");
_;
}
modifier isNotFrozen() {
require(frozen == false, "rICO is frozen!");
_;
}
modifier isRunning() {
uint256 blockNumber = getCurrentEffectiveBlockNumber();
require(blockNumber >= commitPhaseStartBlock && blockNumber <= buyPhaseEndBlock, "Current block is outside the rICO period.");
_;
}
}File 3 of 3: ERC1820Registry
/* ERC1820 Pseudo-introspection Registry Contract
* This standard defines a universal registry smart contract where any address (contract or regular account) can
* register which interface it supports and which smart contract is responsible for its implementation.
*
* Written in 2019 by Jordi Baylina and Jacques Dafflon
*
* To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to
* this software to the public domain worldwide. This software is distributed without any warranty.
*
* You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see
* <http://creativecommons.org/publicdomain/zero/1.0/>.
*
* ███████╗██████╗ ██████╗ ██╗ █████╗ ██████╗ ██████╗
* ██╔════╝██╔══██╗██╔════╝███║██╔══██╗╚════██╗██╔═████╗
* █████╗ ██████╔╝██║ ╚██║╚█████╔╝ █████╔╝██║██╔██║
* ██╔══╝ ██╔══██╗██║ ██║██╔══██╗██╔═══╝ ████╔╝██║
* ███████╗██║ ██║╚██████╗ ██║╚█████╔╝███████╗╚██████╔╝
* ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚════╝ ╚══════╝ ╚═════╝
*
* ██████╗ ███████╗ ██████╗ ██╗███████╗████████╗██████╗ ██╗ ██╗
* ██╔══██╗██╔════╝██╔════╝ ██║██╔════╝╚══██╔══╝██╔══██╗╚██╗ ██╔╝
* ██████╔╝█████╗ ██║ ███╗██║███████╗ ██║ ██████╔╝ ╚████╔╝
* ██╔══██╗██╔══╝ ██║ ██║██║╚════██║ ██║ ██╔══██╗ ╚██╔╝
* ██║ ██║███████╗╚██████╔╝██║███████║ ██║ ██║ ██║ ██║
* ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
*
*/
pragma solidity 0.5.3;
// IV is value needed to have a vanity address starting with '0x1820'.
// IV: 53759
/// @dev The interface a contract MUST implement if it is the implementer of
/// some (other) interface for any address other than itself.
interface ERC1820ImplementerInterface {
/// @notice Indicates whether the contract implements the interface 'interfaceHash' for the address 'addr' or not.
/// @param interfaceHash keccak256 hash of the name of the interface
/// @param addr Address for which the contract will implement the interface
/// @return ERC1820_ACCEPT_MAGIC only if the contract implements 'interfaceHash' for the address 'addr'.
function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32);
}
/// @title ERC1820 Pseudo-introspection Registry Contract
/// @author Jordi Baylina and Jacques Dafflon
/// @notice This contract is the official implementation of the ERC1820 Registry.
/// @notice For more details, see https://eips.ethereum.org/EIPS/eip-1820
contract ERC1820Registry {
/// @notice ERC165 Invalid ID.
bytes4 constant internal INVALID_ID = 0xffffffff;
/// @notice Method ID for the ERC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`).
bytes4 constant internal ERC165ID = 0x01ffc9a7;
/// @notice Magic value which is returned if a contract implements an interface on behalf of some other address.
bytes32 constant internal ERC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("ERC1820_ACCEPT_MAGIC"));
/// @notice mapping from addresses and interface hashes to their implementers.
mapping(address => mapping(bytes32 => address)) internal interfaces;
/// @notice mapping from addresses to their manager.
mapping(address => address) internal managers;
/// @notice flag for each address and erc165 interface to indicate if it is cached.
mapping(address => mapping(bytes4 => bool)) internal erc165Cached;
/// @notice Indicates a contract is the 'implementer' of 'interfaceHash' for 'addr'.
event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer);
/// @notice Indicates 'newManager' is the address of the new manager for 'addr'.
event ManagerChanged(address indexed addr, address indexed newManager);
/// @notice Query if an address implements an interface and through which contract.
/// @param _addr Address being queried for the implementer of an interface.
/// (If '_addr' is the zero address then 'msg.sender' is assumed.)
/// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
/// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface.
/// @return The address of the contract which implements the interface '_interfaceHash' for '_addr'
/// or '0' if '_addr' did not register an implementer for this interface.
function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) {
address addr = _addr == address(0) ? msg.sender : _addr;
if (isERC165Interface(_interfaceHash)) {
bytes4 erc165InterfaceHash = bytes4(_interfaceHash);
return implementsERC165Interface(addr, erc165InterfaceHash) ? addr : address(0);
}
return interfaces[addr][_interfaceHash];
}
/// @notice Sets the contract which implements a specific interface for an address.
/// Only the manager defined for that address can set it.
/// (Each address is the manager for itself until it sets a new manager.)
/// @param _addr Address for which to set the interface.
/// (If '_addr' is the zero address then 'msg.sender' is assumed.)
/// @param _interfaceHash Keccak256 hash of the name of the interface as a string.
/// E.g., 'web3.utils.keccak256("ERC777TokensRecipient")' for the 'ERC777TokensRecipient' interface.
/// @param _implementer Contract address implementing '_interfaceHash' for '_addr'.
function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external {
address addr = _addr == address(0) ? msg.sender : _addr;
require(getManager(addr) == msg.sender, "Not the manager");
require(!isERC165Interface(_interfaceHash), "Must not be an ERC165 hash");
if (_implementer != address(0) && _implementer != msg.sender) {
require(
ERC1820ImplementerInterface(_implementer)
.canImplementInterfaceForAddress(_interfaceHash, addr) == ERC1820_ACCEPT_MAGIC,
"Does not implement the interface"
);
}
interfaces[addr][_interfaceHash] = _implementer;
emit InterfaceImplementerSet(addr, _interfaceHash, _implementer);
}
/// @notice Sets '_newManager' as manager for '_addr'.
/// The new manager will be able to call 'setInterfaceImplementer' for '_addr'.
/// @param _addr Address for which to set the new manager.
/// @param _newManager Address of the new manager for 'addr'. (Pass '0x0' to reset the manager to '_addr'.)
function setManager(address _addr, address _newManager) external {
require(getManager(_addr) == msg.sender, "Not the manager");
managers[_addr] = _newManager == _addr ? address(0) : _newManager;
emit ManagerChanged(_addr, _newManager);
}
/// @notice Get the manager of an address.
/// @param _addr Address for which to return the manager.
/// @return Address of the manager for a given address.
function getManager(address _addr) public view returns(address) {
// By default the manager of an address is the same address
if (managers[_addr] == address(0)) {
return _addr;
} else {
return managers[_addr];
}
}
/// @notice Compute the keccak256 hash of an interface given its name.
/// @param _interfaceName Name of the interface.
/// @return The keccak256 hash of an interface name.
function interfaceHash(string calldata _interfaceName) external pure returns(bytes32) {
return keccak256(abi.encodePacked(_interfaceName));
}
/* --- ERC165 Related Functions --- */
/* --- Developed in collaboration with William Entriken. --- */
/// @notice Updates the cache with whether the contract implements an ERC165 interface or not.
/// @param _contract Address of the contract for which to update the cache.
/// @param _interfaceId ERC165 interface for which to update the cache.
function updateERC165Cache(address _contract, bytes4 _interfaceId) external {
interfaces[_contract][_interfaceId] = implementsERC165InterfaceNoCache(
_contract, _interfaceId) ? _contract : address(0);
erc165Cached[_contract][_interfaceId] = true;
}
/// @notice Checks whether a contract implements an ERC165 interface or not.
// If the result is not cached a direct lookup on the contract address is performed.
// If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling
// 'updateERC165Cache' with the contract address.
/// @param _contract Address of the contract to check.
/// @param _interfaceId ERC165 interface to check.
/// @return True if '_contract' implements '_interfaceId', false otherwise.
function implementsERC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) {
if (!erc165Cached[_contract][_interfaceId]) {
return implementsERC165InterfaceNoCache(_contract, _interfaceId);
}
return interfaces[_contract][_interfaceId] == _contract;
}
/// @notice Checks whether a contract implements an ERC165 interface or not without using nor updating the cache.
/// @param _contract Address of the contract to check.
/// @param _interfaceId ERC165 interface to check.
/// @return True if '_contract' implements '_interfaceId', false otherwise.
function implementsERC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) {
uint256 success;
uint256 result;
(success, result) = noThrowCall(_contract, ERC165ID);
if (success == 0 || result == 0) {
return false;
}
(success, result) = noThrowCall(_contract, INVALID_ID);
if (success == 0 || result != 0) {
return false;
}
(success, result) = noThrowCall(_contract, _interfaceId);
if (success == 1 && result == 1) {
return true;
}
return false;
}
/// @notice Checks whether the hash is a ERC165 interface (ending with 28 zeroes) or not.
/// @param _interfaceHash The hash to check.
/// @return True if '_interfaceHash' is an ERC165 interface (ending with 28 zeroes), false otherwise.
function isERC165Interface(bytes32 _interfaceHash) internal pure returns (bool) {
return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0;
}
/// @dev Make a call on a contract without throwing if the function does not exist.
function noThrowCall(address _contract, bytes4 _interfaceId)
internal view returns (uint256 success, uint256 result)
{
bytes4 erc165ID = ERC165ID;
assembly {
let x := mload(0x40) // Find empty storage location using "free memory pointer"
mstore(x, erc165ID) // Place signature at beginning of empty storage
mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature
success := staticcall(
30000, // 30k gas
_contract, // To addr
x, // Inputs are stored at location x
0x24, // Inputs are 36 (4 + 32) bytes long
x, // Store output over input (saves space)
0x20 // Outputs are 32 bytes long
)
result := mload(x) // Load the result
}
}
}