More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 1,496 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Commit | 20323394 | 40 days ago | IN | 0.01 ETH | 0.00017652 | ||||
Commit | 4880133 | 2421 days ago | IN | 1 ETH | 0.00069186 | ||||
Commit | 4880082 | 2421 days ago | IN | 1 ETH | 0.00138372 | ||||
Commit | 4880056 | 2421 days ago | IN | 1 ETH | 0.00117616 | ||||
Commit | 4879773 | 2421 days ago | IN | 1 ETH | 0.00117616 | ||||
Transfer | 4879763 | 2421 days ago | IN | 1 ETH | 0.00042086 | ||||
Commit | 4879747 | 2421 days ago | IN | 1 ETH | 0.00117616 | ||||
Commit | 4879710 | 2421 days ago | IN | 1 ETH | 0.00069186 | ||||
Commit | 4879699 | 2421 days ago | IN | 1 ETH | 0.00069186 | ||||
Commit | 4879673 | 2421 days ago | IN | 1 ETH | 0.00069186 | ||||
Commit | 4879655 | 2421 days ago | IN | 1 ETH | 0.00069186 | ||||
Commit | 4879329 | 2421 days ago | IN | 1 ETH | 0.00069186 | ||||
Commit | 4867993 | 2423 days ago | IN | 1 ETH | 0.00023062 | ||||
Commit | 4865463 | 2424 days ago | IN | 1 ETH | 0.00092248 | ||||
Commit | 4865427 | 2424 days ago | IN | 1 ETH | 0.00069186 | ||||
Commit | 4865413 | 2424 days ago | IN | 1 ETH | 0.00069186 | ||||
Commit | 4853525 | 2426 days ago | IN | 1 ETH | 0.00069186 | ||||
Commit | 4850747 | 2426 days ago | IN | 1 ETH | 0.0004843 | ||||
Commit | 4850723 | 2426 days ago | IN | 1.04 ETH | 0.00069186 | ||||
Commit | 4850700 | 2426 days ago | IN | 1.05 ETH | 0.00069186 | ||||
Commit | 4850686 | 2426 days ago | IN | 1.01 ETH | 0.00069186 | ||||
Commit | 4850669 | 2426 days ago | IN | 1 ETH | 0.00069186 | ||||
Commit | 4848552 | 2427 days ago | IN | 1 ETH | 0.00069186 | ||||
Commit | 4815001 | 2433 days ago | IN | 2.05 ETH | 0.00138602 | ||||
Commit | 4814985 | 2433 days ago | IN | 2.05 ETH | 0.00092478 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
4748122 | 2444 days ago | 1.3 ETH | ||||
4748105 | 2444 days ago | 1 ETH | ||||
4748104 | 2444 days ago | 1 ETH | ||||
4748094 | 2444 days ago | 1.03 ETH | ||||
4748090 | 2444 days ago | 1 ETH | ||||
4748080 | 2444 days ago | 1.02 ETH | ||||
4748070 | 2444 days ago | 1.77 ETH | ||||
4748070 | 2444 days ago | 1.1 ETH | ||||
4748060 | 2444 days ago | 1.03 ETH | ||||
4748053 | 2444 days ago | 1 ETH | ||||
4748042 | 2444 days ago | 1 ETH | ||||
4748035 | 2444 days ago | 2 ETH | ||||
4748019 | 2444 days ago | 1.3 ETH | ||||
4748010 | 2444 days ago | 1.3 ETH | ||||
4748007 | 2444 days ago | 1.5 ETH | ||||
4748004 | 2444 days ago | 1 ETH | ||||
4748003 | 2444 days ago | 1 ETH | ||||
4747964 | 2444 days ago | 1 ETH | ||||
4747963 | 2444 days ago | 1 ETH | ||||
4747948 | 2444 days ago | 2.31437967 ETH | ||||
4747931 | 2444 days ago | 10 ETH | ||||
4747921 | 2444 days ago | 1 ETH | ||||
4747916 | 2444 days ago | 1.0978964 ETH | ||||
4747913 | 2444 days ago | 1.1 ETH | ||||
4747905 | 2444 days ago | 10 ETH |
Loading...
Loading
Contract Name:
Commitment
Compiler Version
v0.4.15+commit.bbb8e64f
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity)
/** *Submitted for verification at Etherscan.io on 2017-11-08 */ pragma solidity 0.4.15; /// @title provides subject to role checking logic contract IAccessPolicy { //////////////////////// // Public functions //////////////////////// /// @notice We don't make this function constant to allow for state-updating access controls such as rate limiting. /// @dev checks if subject belongs to requested role for particular object /// @param subject address to be checked against role, typically msg.sender /// @param role identifier of required role /// @param object contract instance context for role checking, typically contract requesting the check /// @param verb additional data, in current AccessControll implementation msg.sig /// @return if subject belongs to a role function allowed( address subject, bytes32 role, address object, bytes4 verb ) public returns (bool); } /// @title enables access control in implementing contract /// @dev see AccessControlled for implementation contract IAccessControlled { //////////////////////// // Events //////////////////////// /// @dev must log on access policy change event LogAccessPolicyChanged( address controller, IAccessPolicy oldPolicy, IAccessPolicy newPolicy ); //////////////////////// // Public functions //////////////////////// /// @dev allows to change access control mechanism for this contract /// this method must be itself access controlled, see AccessControlled implementation and notice below /// @notice it is a huge issue for Solidity that modifiers are not part of function signature /// then interfaces could be used for example to control access semantics /// @param newPolicy new access policy to controll this contract /// @param newAccessController address of ROLE_ACCESS_CONTROLLER of new policy that can set access to this contract function setAccessPolicy(IAccessPolicy newPolicy, address newAccessController) public; function accessPolicy() public constant returns (IAccessPolicy); } contract StandardRoles { //////////////////////// // Constants //////////////////////// // @notice Soldity somehow doesn't evaluate this compile time // @dev role which has rights to change permissions and set new policy in contract, keccak256("AccessController") bytes32 internal constant ROLE_ACCESS_CONTROLLER = 0xac42f8beb17975ed062dcb80c63e6d203ef1c2c335ced149dc5664cc671cb7da; } /// @title Granular code execution permissions /// @notice Intended to replace existing Ownable pattern with more granular permissions set to execute smart contract functions /// for each function where 'only' modifier is applied, IAccessPolicy implementation is called to evaluate if msg.sender belongs to required role for contract being called. /// Access evaluation specific belong to IAccessPolicy implementation, see RoleBasedAccessPolicy for details. /// @dev Should be inherited by a contract requiring such permissions controll. IAccessPolicy must be provided in constructor. Access policy may be replaced to a different one /// by msg.sender with ROLE_ACCESS_CONTROLLER role contract AccessControlled is IAccessControlled, StandardRoles { //////////////////////// // Mutable state //////////////////////// IAccessPolicy private _accessPolicy; //////////////////////// // Modifiers //////////////////////// /// @dev limits function execution only to senders assigned to required 'role' modifier only(bytes32 role) { require(_accessPolicy.allowed(msg.sender, role, this, msg.sig)); _; } //////////////////////// // Constructor //////////////////////// function AccessControlled(IAccessPolicy policy) internal { require(address(policy) != 0x0); _accessPolicy = policy; } //////////////////////// // Public functions //////////////////////// // // Implements IAccessControlled // function setAccessPolicy(IAccessPolicy newPolicy, address newAccessController) public only(ROLE_ACCESS_CONTROLLER) { // ROLE_ACCESS_CONTROLLER must be present // under the new policy. This provides some // protection against locking yourself out. require(newPolicy.allowed(newAccessController, ROLE_ACCESS_CONTROLLER, this, msg.sig)); // We can now safely set the new policy without foot shooting. IAccessPolicy oldPolicy = _accessPolicy; _accessPolicy = newPolicy; // Log event LogAccessPolicyChanged(msg.sender, oldPolicy, newPolicy); } function accessPolicy() public constant returns (IAccessPolicy) { return _accessPolicy; } } contract AccessRoles { //////////////////////// // Constants //////////////////////// // NOTE: All roles are set to the keccak256 hash of the // CamelCased role name, i.e. // ROLE_LOCKED_ACCOUNT_ADMIN = keccak256("LockedAccountAdmin") // may setup LockedAccount, change disbursal mechanism and set migration bytes32 internal constant ROLE_LOCKED_ACCOUNT_ADMIN = 0x4675da546d2d92c5b86c4f726a9e61010dce91cccc2491ce6019e78b09d2572e; // may setup whitelists and abort whitelisting contract with curve rollback bytes32 internal constant ROLE_WHITELIST_ADMIN = 0xaef456e7c864418e1d2a40d996ca4febf3a7e317fe3af5a7ea4dda59033bbe5c; // May issue (generate) Neumarks bytes32 internal constant ROLE_NEUMARK_ISSUER = 0x921c3afa1f1fff707a785f953a1e197bd28c9c50e300424e015953cbf120c06c; // May burn Neumarks it owns bytes32 internal constant ROLE_NEUMARK_BURNER = 0x19ce331285f41739cd3362a3ec176edffe014311c0f8075834fdd19d6718e69f; // May create new snapshots on Neumark bytes32 internal constant ROLE_SNAPSHOT_CREATOR = 0x08c1785afc57f933523bc52583a72ce9e19b2241354e04dd86f41f887e3d8174; // May enable/disable transfers on Neumark bytes32 internal constant ROLE_TRANSFER_ADMIN = 0xb6527e944caca3d151b1f94e49ac5e223142694860743e66164720e034ec9b19; // may reclaim tokens/ether from contracts supporting IReclaimable interface bytes32 internal constant ROLE_RECLAIMER = 0x0542bbd0c672578966dcc525b30aa16723bb042675554ac5b0362f86b6e97dc5; // represents legally platform operator in case of forks and contracts with legal agreement attached. keccak256("PlatformOperatorRepresentative") bytes32 internal constant ROLE_PLATFORM_OPERATOR_REPRESENTATIVE = 0xb2b321377653f655206f71514ff9f150d0822d062a5abcf220d549e1da7999f0; // allows to deposit EUR-T and allow addresses to send and receive EUR-T. keccak256("EurtDepositManager") bytes32 internal constant ROLE_EURT_DEPOSIT_MANAGER = 0x7c8ecdcba80ce87848d16ad77ef57cc196c208fc95c5638e4a48c681a34d4fe7; } contract IEthereumForkArbiter { //////////////////////// // Events //////////////////////// event LogForkAnnounced( string name, string url, uint256 blockNumber ); event LogForkSigned( uint256 blockNumber, bytes32 blockHash ); //////////////////////// // Public functions //////////////////////// function nextForkName() public constant returns (string); function nextForkUrl() public constant returns (string); function nextForkBlockNumber() public constant returns (uint256); function lastSignedBlockNumber() public constant returns (uint256); function lastSignedBlockHash() public constant returns (bytes32); function lastSignedTimestamp() public constant returns (uint256); } /** * @title legally binding smart contract * @dev General approach to paring legal and smart contracts: * 1. All terms and agreement are between two parties: here between legal representation of platform operator representative and platform investor. * 2. Parties are represented by public Ethereum addresses. Platform investor is and address that holds and controls funds and receives and controls Neumark token * 3. Legal agreement has immutable part that corresponds to smart contract code and mutable part that may change for example due to changing regulations or other externalities that smart contract does not control. * 4. There should be a provision in legal document that future changes in mutable part cannot change terms of immutable part. * 5. Immutable part links to corresponding smart contract via its address. * 6. Additional provision should be added if smart contract supports it * a. Fork provision * b. Bugfixing provision (unilateral code update mechanism) * c. Migration provision (bilateral code update mechanism) * * Details on Agreement base class: * 1. We bind smart contract to legal contract by storing uri (preferably ipfs or hash) of the legal contract in the smart contract. It is however crucial that such binding is done by platform operator representation so transaction establishing the link must be signed by respective wallet ('amendAgreement') * 2. Mutable part of agreement may change. We should be able to amend the uri later. Previous amendments should not be lost and should be retrievable (`amendAgreement` and 'pastAgreement' functions). * 3. It is up to deriving contract to decide where to put 'acceptAgreement' modifier. However situation where there is no cryptographic proof that given address was really acting in the transaction should be avoided, simplest example being 'to' address in `transfer` function of ERC20. * **/ contract Agreement is AccessControlled, AccessRoles { //////////////////////// // Type declarations //////////////////////// /// @notice agreement with signature of the platform operator representative struct SignedAgreement { address platformOperatorRepresentative; uint256 signedBlockTimestamp; string agreementUri; } //////////////////////// // Immutable state //////////////////////// IEthereumForkArbiter private ETHEREUM_FORK_ARBITER; //////////////////////// // Mutable state //////////////////////// // stores all amendments to the agreement, first amendment is the original SignedAgreement[] private _amendments; // stores block numbers of all addresses that signed the agreement (signatory => block number) mapping(address => uint256) private _signatories; //////////////////////// // Events //////////////////////// event LogAgreementAccepted( address indexed accepter ); event LogAgreementAmended( address platformOperatorRepresentative, string agreementUri ); //////////////////////// // Modifiers //////////////////////// /// @notice logs that agreement was accepted by platform user /// @dev intended to be added to functions that if used make 'accepter' origin to enter legally binding agreement modifier acceptAgreement(address accepter) { if(_signatories[accepter] == 0) { require(_amendments.length > 0); _signatories[accepter] = block.number; LogAgreementAccepted(accepter); } _; } //////////////////////// // Constructor //////////////////////// function Agreement(IAccessPolicy accessPolicy, IEthereumForkArbiter forkArbiter) AccessControlled(accessPolicy) internal { require(forkArbiter != IEthereumForkArbiter(0x0)); ETHEREUM_FORK_ARBITER = forkArbiter; } //////////////////////// // Public functions //////////////////////// function amendAgreement(string agreementUri) public only(ROLE_PLATFORM_OPERATOR_REPRESENTATIVE) { SignedAgreement memory amendment = SignedAgreement({ platformOperatorRepresentative: msg.sender, signedBlockTimestamp: block.timestamp, agreementUri: agreementUri }); _amendments.push(amendment); LogAgreementAmended(msg.sender, agreementUri); } function ethereumForkArbiter() public constant returns (IEthereumForkArbiter) { return ETHEREUM_FORK_ARBITER; } function currentAgreement() public constant returns ( address platformOperatorRepresentative, uint256 signedBlockTimestamp, string agreementUri, uint256 index ) { require(_amendments.length > 0); uint256 last = _amendments.length - 1; SignedAgreement storage amendment = _amendments[last]; return ( amendment.platformOperatorRepresentative, amendment.signedBlockTimestamp, amendment.agreementUri, last ); } function pastAgreement(uint256 amendmentIndex) public constant returns ( address platformOperatorRepresentative, uint256 signedBlockTimestamp, string agreementUri, uint256 index ) { SignedAgreement storage amendment = _amendments[amendmentIndex]; return ( amendment.platformOperatorRepresentative, amendment.signedBlockTimestamp, amendment.agreementUri, amendmentIndex ); } function agreementSignedAtBlock(address signatory) public constant returns (uint256) { return _signatories[signatory]; } } contract IsContract { //////////////////////// // Internal functions //////////////////////// function isContract(address addr) internal constant returns (bool) { uint256 size; // takes 700 gas assembly { size := extcodesize(addr) } return size > 0; } } contract IBasicToken { //////////////////////// // Events //////////////////////// event Transfer( address indexed from, address indexed to, uint256 amount); //////////////////////// // Public functions //////////////////////// /// @dev This function makes it easy to get the total number of tokens /// @return The total number of tokens function totalSupply() public constant returns (uint256); /// @param owner The address that's balance is being requested /// @return The balance of `owner` at the current block function balanceOf(address owner) public constant returns (uint256 balance); /// @notice Send `amount` tokens to `to` from `msg.sender` /// @param to The address of the recipient /// @param amount The amount of tokens to be transferred /// @return Whether the transfer was successful or not function transfer(address to, uint256 amount) public returns (bool success); } /// @title allows deriving contract to recover any token or ether that it has balance of /// @notice note that this opens your contracts to claims from various people saying they lost tokens and they want them back /// be ready to handle such claims /// @dev use with care! /// 1. ROLE_RECLAIMER is allowed to claim tokens, it's not returning tokens to original owner /// 2. in derived contract that holds any token by design you must override `reclaim` and block such possibility. /// see LockedAccount as an example contract Reclaimable is AccessControlled, AccessRoles { //////////////////////// // Constants //////////////////////// IBasicToken constant internal RECLAIM_ETHER = IBasicToken(0x0); //////////////////////// // Public functions //////////////////////// function reclaim(IBasicToken token) public only(ROLE_RECLAIMER) { address reclaimer = msg.sender; if(token == RECLAIM_ETHER) { reclaimer.transfer(this.balance); } else { uint256 balance = token.balanceOf(this); require(token.transfer(reclaimer, balance)); } } } contract ITokenMetadata { //////////////////////// // Public functions //////////////////////// function symbol() public constant returns (string); function name() public constant returns (string); function decimals() public constant returns (uint8); } /// @title adds token metadata to token contract /// @dev see Neumark for example implementation contract TokenMetadata is ITokenMetadata { //////////////////////// // Immutable state //////////////////////// // The Token's name: e.g. DigixDAO Tokens string private NAME; // An identifier: e.g. REP string private SYMBOL; // Number of decimals of the smallest unit uint8 private DECIMALS; // An arbitrary versioning scheme string private VERSION; //////////////////////// // Constructor //////////////////////// /// @notice Constructor to set metadata /// @param tokenName Name of the new token /// @param decimalUnits Number of decimals of the new token /// @param tokenSymbol Token Symbol for the new token /// @param version Token version ie. when cloning is used function TokenMetadata( string tokenName, uint8 decimalUnits, string tokenSymbol, string version ) public { NAME = tokenName; // Set the name SYMBOL = tokenSymbol; // Set the symbol DECIMALS = decimalUnits; // Set the decimals VERSION = version; } //////////////////////// // Public functions //////////////////////// function name() public constant returns (string) { return NAME; } function symbol() public constant returns (string) { return SYMBOL; } function decimals() public constant returns (uint8) { return DECIMALS; } function version() public constant returns (string) { return VERSION; } } contract IERC223Callback { //////////////////////// // Public functions //////////////////////// function onTokenTransfer( address from, uint256 amount, bytes data ) public; } contract IERC223Token is IBasicToken { /// @dev Departure: We do not log data, it has no advantage over a standard /// log event. By sticking to the standard log event we /// stay compatible with constracts that expect and ERC20 token. // event Transfer( // address indexed from, // address indexed to, // uint256 amount, // bytes data); /// @dev Departure: We do not use the callback on regular transfer calls to /// stay compatible with constracts that expect and ERC20 token. // function transfer(address to, uint256 amount) // public // returns (bool); //////////////////////// // Public functions //////////////////////// function transfer(address to, uint256 amount, bytes data) public returns (bool); } contract IERC20Allowance { //////////////////////// // Events //////////////////////// event Approval( address indexed owner, address indexed spender, uint256 amount); //////////////////////// // Public functions //////////////////////// /// @dev This function makes it easy to read the `allowed[]` map /// @param owner The address of the account that owns the token /// @param spender The address of the account able to transfer the tokens /// @return Amount of remaining tokens of owner that spender is allowed /// to spend function allowance(address owner, address spender) public constant returns (uint256 remaining); /// @notice `msg.sender` approves `spender` to spend `amount` tokens on /// its behalf. This is a modified version of the ERC20 approve function /// to be a little bit safer /// @param spender The address of the account able to transfer the tokens /// @param amount The amount of tokens to be approved for transfer /// @return True if the approval was successful function approve(address spender, uint256 amount) public returns (bool success); /// @notice Send `amount` tokens to `to` from `from` on the condition it /// is approved by `from` /// @param from The address holding the tokens being transferred /// @param to The address of the recipient /// @param amount The amount of tokens to be transferred /// @return True if the transfer was successful function transferFrom(address from, address to, uint256 amount) public returns (bool success); } contract IERC20Token is IBasicToken, IERC20Allowance { } contract IERC677Callback { //////////////////////// // Public functions //////////////////////// // NOTE: This call can be initiated by anyone. You need to make sure that // it is send by the token (`require(msg.sender == token)`) or make sure // amount is valid (`require(token.allowance(this) >= amount)`). function receiveApproval( address from, uint256 amount, address token, // IERC667Token bytes data ) public returns (bool success); } contract IERC677Allowance is IERC20Allowance { //////////////////////// // Public functions //////////////////////// /// @notice `msg.sender` approves `spender` to send `amount` tokens on /// its behalf, and then a function is triggered in the contract that is /// being approved, `spender`. This allows users to use their tokens to /// interact with contracts in one function call instead of two /// @param spender The address of the contract able to transfer the tokens /// @param amount The amount of tokens to be approved for transfer /// @return True if the function call was successful function approveAndCall(address spender, uint256 amount, bytes extraData) public returns (bool success); } contract IERC677Token is IERC20Token, IERC677Allowance { } contract Math { //////////////////////// // Internal functions //////////////////////// // absolute difference: |v1 - v2| function absDiff(uint256 v1, uint256 v2) internal constant returns(uint256) { return v1 > v2 ? v1 - v2 : v2 - v1; } // divide v by d, round up if remainder is 0.5 or more function divRound(uint256 v, uint256 d) internal constant returns(uint256) { return add(v, d/2) / d; } // computes decimal decimalFraction 'frac' of 'amount' with maximum precision (multiplication first) // both amount and decimalFraction must have 18 decimals precision, frac 10**18 represents a whole (100% of) amount // mind loss of precision as decimal fractions do not have finite binary expansion // do not use instead of division function decimalFraction(uint256 amount, uint256 frac) internal constant returns(uint256) { // it's like 1 ether is 100% proportion return proportion(amount, frac, 10**18); } // computes part/total of amount with maximum precision (multiplication first) // part and total must have the same units function proportion(uint256 amount, uint256 part, uint256 total) internal constant returns(uint256) { return divRound(mul(amount, part), total); } // // Open Zeppelin Math library below // function mul(uint256 a, uint256 b) internal constant returns (uint256) { uint256 c = a * b; assert(a == 0 || c / a == b); return c; } function sub(uint256 a, uint256 b) internal constant returns (uint256) { assert(b <= a); return a - b; } function add(uint256 a, uint256 b) internal constant returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } function min(uint256 a, uint256 b) internal constant returns (uint256) { return a < b ? a : b; } function max(uint256 a, uint256 b) internal constant returns (uint256) { return a > b ? a : b; } } /** * @title Basic token * @dev Basic version of StandardToken, with no allowances. */ contract BasicToken is IBasicToken, Math { //////////////////////// // Mutable state //////////////////////// mapping(address => uint256) internal _balances; uint256 internal _totalSupply; //////////////////////// // Public functions //////////////////////// /** * @dev transfer token for a specified address * @param to The address to transfer to. * @param amount The amount to be transferred. */ function transfer(address to, uint256 amount) public returns (bool) { transferInternal(msg.sender, to, amount); return true; } /// @dev This function makes it easy to get the total number of tokens /// @return The total number of tokens function totalSupply() public constant returns (uint256) { return _totalSupply; } /** * @dev Gets the balance of the specified address. * @param owner The address to query the the balance of. * @return An uint256 representing the amount owned by the passed address. */ function balanceOf(address owner) public constant returns (uint256 balance) { return _balances[owner]; } //////////////////////// // Internal functions //////////////////////// // actual transfer function called by all public variants function transferInternal(address from, address to, uint256 amount) internal { require(to != address(0)); _balances[from] = sub(_balances[from], amount); _balances[to] = add(_balances[to], amount); Transfer(from, to, amount); } } /** * @title Standard ERC20 token * * @dev Implementation of the standard token. * @dev https://github.com/ethereum/EIPs/issues/20 * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol */ contract StandardToken is IERC20Token, BasicToken, IERC677Token { //////////////////////// // Mutable state //////////////////////// mapping (address => mapping (address => uint256)) private _allowed; //////////////////////// // Public functions //////////////////////// // // Implements ERC20 // /** * @dev Transfer tokens from one address to another * @param from address The address which you want to send tokens from * @param to address The address which you want to transfer to * @param amount uint256 the amount of tokens to be transferred */ function transferFrom(address from, address to, uint256 amount) public returns (bool) { // check and reset allowance var allowance = _allowed[from][msg.sender]; _allowed[from][msg.sender] = sub(allowance, amount); // do the transfer transferInternal(from, to, amount); return true; } /** * @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender. * @param spender The address which will spend the funds. * @param amount The amount of tokens to be spent. */ function approve(address spender, uint256 amount) public returns (bool) { // To change the approve amount you first have to reduce the addresses` // allowance to zero by calling `approve(_spender, 0)` if it is not // already 0 to mitigate the race condition described here: // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 require((amount == 0) || (_allowed[msg.sender][spender] == 0)); _allowed[msg.sender][spender] = amount; Approval(msg.sender, spender, amount); return true; } /** * @dev Function to check the amount of tokens that an owner allowed to a spender. * @param owner address The address which owns the funds. * @param spender address The address which will spend the funds. * @return A uint256 specifing the amount of tokens still avaible for the spender. */ function allowance(address owner, address spender) public constant returns (uint256 remaining) { return _allowed[owner][spender]; } // // Implements IERC677Token // function approveAndCall( address spender, uint256 amount, bytes extraData ) public returns (bool) { require(approve(spender, amount)); // in case of re-entry 1. approval is done 2. msg.sender is different bool success = IERC677Callback(spender).receiveApproval( msg.sender, amount, this, extraData ); require(success); return true; } } contract EtherToken is IsContract, AccessControlled, StandardToken, TokenMetadata, Reclaimable { //////////////////////// // Constants //////////////////////// string private constant NAME = "Ether Token"; string private constant SYMBOL = "ETH-T"; uint8 private constant DECIMALS = 18; //////////////////////// // Events //////////////////////// event LogDeposit( address indexed to, uint256 amount ); event LogWithdrawal( address indexed from, uint256 amount ); //////////////////////// // Constructor //////////////////////// function EtherToken(IAccessPolicy accessPolicy) AccessControlled(accessPolicy) StandardToken() TokenMetadata(NAME, DECIMALS, SYMBOL, "") Reclaimable() public { } //////////////////////// // Public functions //////////////////////// /// deposit msg.value of Ether to msg.sender balance function deposit() payable public { _balances[msg.sender] = add(_balances[msg.sender], msg.value); _totalSupply = add(_totalSupply, msg.value); LogDeposit(msg.sender, msg.value); Transfer(address(0), msg.sender, msg.value); } /// withdraws and sends 'amount' of ether to msg.sender function withdraw(uint256 amount) public { require(_balances[msg.sender] >= amount); _balances[msg.sender] = sub(_balances[msg.sender], amount); _totalSupply = sub(_totalSupply, amount); msg.sender.transfer(amount); LogWithdrawal(msg.sender, amount); Transfer(msg.sender, address(0), amount); } // // Implements IERC223Token // function transfer(address to, uint256 amount, bytes data) public returns (bool) { transferInternal(msg.sender, to, amount); // Notify the receiving contract. if (isContract(to)) { // in case of re-entry (1) transfer is done (2) msg.sender is different IERC223Callback(to).onTokenTransfer(msg.sender, amount, data); } return true; } // // Overrides Reclaimable // /// @notice allows EtherToken to reclaim tokens wrongly sent to its address /// @dev as EtherToken by design has balance of Ether (native Ethereum token) /// such reclamation is not allowed function reclaim(IBasicToken token) public { // forbid reclaiming ETH hold in this contract. require(token != RECLAIM_ETHER); Reclaimable.reclaim(token); } } /// @notice implemented in the contract that is the target of state migration /// @dev implementation must provide actual function that will be called by source to migrate state contract IMigrationTarget { //////////////////////// // Public functions //////////////////////// // should return migration source address function currentMigrationSource() public constant returns (address); } /// @notice mixin that enables contract to receive migration /// @dev when derived from contract MigrationTarget is IMigrationTarget { //////////////////////// // Modifiers //////////////////////// // intended to be applied on migration receiving function modifier onlyMigrationSource() { require(msg.sender == currentMigrationSource()); _; } } contract EuroTokenMigrationTarget is MigrationTarget { //////////////////////// // Public functions //////////////////////// /// @notice accepts migration of single eur-t token holder /// @dev allowed to be called only from migration source, do not forget to add accessor modifier in implementation function migrateEuroTokenOwner(address owner, uint256 amount) public onlyMigrationSource(); } /// @notice implemented in the contract that stores state to be migrated /// @notice contract is called migration source /// @dev migration target implements IMigrationTarget interface, when it is passed in 'enableMigration' function /// @dev 'migrate' function may be called to migrate part of state owned by msg.sender /// @dev in legal terms this corresponds to amending/changing agreement terms by co-signature of parties contract IMigrationSource { //////////////////////// // Events //////////////////////// event LogMigrationEnabled( address target ); //////////////////////// // Public functions //////////////////////// /// @notice should migrate state owned by msg.sender /// @dev intended flow is to: read source state, clear source state, call migrate function on target, log success event function migrate() public; /// @notice should enable migration to migration target /// @dev should limit access to specific role in implementation function enableMigration(IMigrationTarget migration) public; /// @notice returns current migration target function currentMigrationTarget() public constant returns (IMigrationTarget); } /// @notice mixin that enables migration pattern for a contract /// @dev when derived from contract MigrationSource is IMigrationSource, AccessControlled { //////////////////////// // Immutable state //////////////////////// /// stores role hash that can enable migration bytes32 private MIGRATION_ADMIN; //////////////////////// // Mutable state //////////////////////// // migration target contract IMigrationTarget internal _migration; //////////////////////// // Modifiers //////////////////////// /// @notice add to enableMigration function to prevent changing of migration /// target once set modifier onlyMigrationEnabledOnce() { require(address(_migration) == 0); _; } modifier onlyMigrationEnabled() { require(address(_migration) != 0); _; } //////////////////////// // Constructor //////////////////////// function MigrationSource( IAccessPolicy policy, bytes32 migrationAdminRole ) AccessControlled(policy) internal { MIGRATION_ADMIN = migrationAdminRole; } //////////////////////// // Public functions //////////////////////// /// @notice should migrate state that belongs to msg.sender /// @dev do not forget to add accessor modifier in implementation function migrate() onlyMigrationEnabled() public; /// @notice should enable migration to migration target /// @dev do not forget to add accessor modifier in override function enableMigration(IMigrationTarget migration) public onlyMigrationEnabledOnce() only(MIGRATION_ADMIN) { // this must be the source require(migration.currentMigrationSource() == address(this)); _migration = migration; LogMigrationEnabled(_migration); } /// @notice returns current migration target function currentMigrationTarget() public constant returns (IMigrationTarget) { return _migration; } } /// Simple implementation of EuroToken which is pegged 1:1 to certain off-chain /// pool of Euro. Balances of this token are intended to be migrated to final /// implementation that will be available later contract EuroToken is IERC677Token, AccessControlled, StandardToken, TokenMetadata, MigrationSource, Reclaimable { //////////////////////// // Constants //////////////////////// string private constant NAME = "Euro Token"; string private constant SYMBOL = "EUR-T"; uint8 private constant DECIMALS = 18; //////////////////////// // Mutable state //////////////////////// // a list of addresses that are allowed to receive EUR-T mapping(address => bool) private _allowedTransferTo; // a list of of addresses that are allowed to send EUR-T mapping(address => bool) private _allowedTransferFrom; //////////////////////// // Events //////////////////////// event LogDeposit( address indexed to, uint256 amount ); event LogWithdrawal( address indexed from, uint256 amount ); event LogAllowedFromAddress( address indexed from, bool allowed ); event LogAllowedToAddress( address indexed to, bool allowed ); /// @notice migration was successful event LogEuroTokenOwnerMigrated( address indexed owner, uint256 amount ); //////////////////////// // Modifiers //////////////////////// modifier onlyAllowedTransferFrom(address from) { require(_allowedTransferFrom[from]); _; } modifier onlyAllowedTransferTo(address to) { require(_allowedTransferTo[to]); _; } //////////////////////// // Constructor //////////////////////// function EuroToken(IAccessPolicy accessPolicy) AccessControlled(accessPolicy) StandardToken() TokenMetadata(NAME, DECIMALS, SYMBOL, "") MigrationSource(accessPolicy, ROLE_EURT_DEPOSIT_MANAGER) Reclaimable() public { } //////////////////////// // Public functions //////////////////////// /// @notice deposit 'amount' of EUR-T to address 'to' /// @dev address 'to' is whitelisted as recipient of future transfers /// @dev deposit may happen only in case of succesful KYC of recipient and validation of banking data /// @dev which in this implementation is an off-chain responsibility of EURT_DEPOSIT_MANAGER function deposit(address to, uint256 amount) public only(ROLE_EURT_DEPOSIT_MANAGER) returns (bool) { require(to != address(0)); _balances[to] = add(_balances[to], amount); _totalSupply = add(_totalSupply, amount); setAllowedTransferTo(to, true); LogDeposit(to, amount); Transfer(address(0), to, amount); return true; } /// @notice withdraws 'amount' of EUR-T by burning required amount and providing a proof of whithdrawal /// @dev proof is provided in form of log entry on which EURT_DEPOSIT_MANAGER /// @dev will act off-chain to return required Euro amount to EUR-T holder function withdraw(uint256 amount) public { require(_balances[msg.sender] >= amount); _balances[msg.sender] = sub(_balances[msg.sender], amount); _totalSupply = sub(_totalSupply, amount); LogWithdrawal(msg.sender, amount); Transfer(msg.sender, address(0), amount); } /// @notice enables or disables address to be receipient of EUR-T function setAllowedTransferTo(address to, bool allowed) public only(ROLE_EURT_DEPOSIT_MANAGER) { _allowedTransferTo[to] = allowed; LogAllowedToAddress(to, allowed); } /// @notice enables or disables address to be sender of EUR-T function setAllowedTransferFrom(address from, bool allowed) public only(ROLE_EURT_DEPOSIT_MANAGER) { _allowedTransferFrom[from] = allowed; LogAllowedFromAddress(from, allowed); } function allowedTransferTo(address to) public constant returns (bool) { return _allowedTransferTo[to]; } function allowedTransferFrom(address from) public constant returns (bool) { return _allowedTransferFrom[from]; } // // Overrides ERC20 Interface to allow transfer from/to allowed addresses // function transfer(address to, uint256 amount) public onlyAllowedTransferFrom(msg.sender) onlyAllowedTransferTo(to) returns (bool success) { return BasicToken.transfer(to, amount); } /// @dev broker acts in the name of 'from' address so broker needs to have permission to transfer from /// this way we may give permissions to brokering smart contracts while investors do not have permissions /// to transfer. 'to' address requires standard transfer to permission function transferFrom(address from, address to, uint256 amount) public onlyAllowedTransferFrom(msg.sender) onlyAllowedTransferTo(to) returns (bool success) { return StandardToken.transferFrom(from, to, amount); } // // Overrides migration source // function migrate() public onlyMigrationEnabled() onlyAllowedTransferTo(msg.sender) { // burn deposit uint256 amount = _balances[msg.sender]; if (amount > 0) { _balances[msg.sender] = 0; _totalSupply = sub(_totalSupply, amount); } // remove all transfer permissions _allowedTransferTo[msg.sender] = false; _allowedTransferFrom[msg.sender] = false; // migrate to EuroTokenMigrationTarget(_migration).migrateEuroTokenOwner(msg.sender, amount); // set event LogEuroTokenOwnerMigrated(msg.sender, amount); } } /// @notice implemented in the contract that is the target of LockedAccount migration /// migration process is removing investors balance from source LockedAccount fully /// target should re-create investor with the same balance, totalLockedAmount and totalInvestors are invariant during migration contract LockedAccountMigration is MigrationTarget { //////////////////////// // Public functions //////////////////////// // implemented in migration target, yes modifiers are inherited from base class function migrateInvestor( address investor, uint256 balance, uint256 neumarksDue, uint256 unlockDate ) public onlyMigrationSource(); } contract NeumarkIssuanceCurve { //////////////////////// // Constants //////////////////////// // maximum number of neumarks that may be created uint256 private constant NEUMARK_CAP = 1500000000000000000000000000; // initial neumark reward fraction (controls curve steepness) uint256 private constant INITIAL_REWARD_FRACTION = 6500000000000000000; // stop issuing new Neumarks above this Euro value (as it goes quickly to zero) uint256 private constant ISSUANCE_LIMIT_EUR_ULPS = 8300000000000000000000000000; // approximate curve linearly above this Euro value uint256 private constant LINEAR_APPROX_LIMIT_EUR_ULPS = 2100000000000000000000000000; uint256 private constant NEUMARKS_AT_LINEAR_LIMIT_ULPS = 1499832501287264827896539871; uint256 private constant TOT_LINEAR_NEUMARKS_ULPS = NEUMARK_CAP - NEUMARKS_AT_LINEAR_LIMIT_ULPS; uint256 private constant TOT_LINEAR_EUR_ULPS = ISSUANCE_LIMIT_EUR_ULPS - LINEAR_APPROX_LIMIT_EUR_ULPS; //////////////////////// // Public functions //////////////////////// /// @notice returns additional amount of neumarks issued for euroUlps at totalEuroUlps /// @param totalEuroUlps actual curve position from which neumarks will be issued /// @param euroUlps amount against which neumarks will be issued function incremental(uint256 totalEuroUlps, uint256 euroUlps) public constant returns (uint256 neumarkUlps) { require(totalEuroUlps + euroUlps >= totalEuroUlps); uint256 from = cumulative(totalEuroUlps); uint256 to = cumulative(totalEuroUlps + euroUlps); // as expansion is not monotonic for large totalEuroUlps, assert below may fail // example: totalEuroUlps=1.999999999999999999999000000e+27 and euroUlps=50 assert(to >= from); return to - from; } /// @notice returns amount of euro corresponding to burned neumarks /// @param totalEuroUlps actual curve position from which neumarks will be burned /// @param burnNeumarkUlps amount of neumarks to burn function incrementalInverse(uint256 totalEuroUlps, uint256 burnNeumarkUlps) public constant returns (uint256 euroUlps) { uint256 totalNeumarkUlps = cumulative(totalEuroUlps); require(totalNeumarkUlps >= burnNeumarkUlps); uint256 fromNmk = totalNeumarkUlps - burnNeumarkUlps; uint newTotalEuroUlps = cumulativeInverse(fromNmk, 0, totalEuroUlps); // yes, this may overflow due to non monotonic inverse function assert(totalEuroUlps >= newTotalEuroUlps); return totalEuroUlps - newTotalEuroUlps; } /// @notice returns amount of euro corresponding to burned neumarks /// @param totalEuroUlps actual curve position from which neumarks will be burned /// @param burnNeumarkUlps amount of neumarks to burn /// @param minEurUlps euro amount to start inverse search from, inclusive /// @param maxEurUlps euro amount to end inverse search to, inclusive function incrementalInverse(uint256 totalEuroUlps, uint256 burnNeumarkUlps, uint256 minEurUlps, uint256 maxEurUlps) public constant returns (uint256 euroUlps) { uint256 totalNeumarkUlps = cumulative(totalEuroUlps); require(totalNeumarkUlps >= burnNeumarkUlps); uint256 fromNmk = totalNeumarkUlps - burnNeumarkUlps; uint newTotalEuroUlps = cumulativeInverse(fromNmk, minEurUlps, maxEurUlps); // yes, this may overflow due to non monotonic inverse function assert(totalEuroUlps >= newTotalEuroUlps); return totalEuroUlps - newTotalEuroUlps; } /// @notice finds total amount of neumarks issued for given amount of Euro /// @dev binomial expansion does not guarantee monotonicity on uint256 precision for large euroUlps /// function below is not monotonic function cumulative(uint256 euroUlps) public constant returns(uint256 neumarkUlps) { // Return the cap if euroUlps is above the limit. if (euroUlps >= ISSUANCE_LIMIT_EUR_ULPS) { return NEUMARK_CAP; } // use linear approximation above limit below // binomial expansion does not guarantee monotonicity on uint256 precision for large euroUlps if (euroUlps >= LINEAR_APPROX_LIMIT_EUR_ULPS) { // (euroUlps - LINEAR_APPROX_LIMIT_EUR_ULPS) is small so expression does not overflow return NEUMARKS_AT_LINEAR_LIMIT_ULPS + (TOT_LINEAR_NEUMARKS_ULPS * (euroUlps - LINEAR_APPROX_LIMIT_EUR_ULPS)) / TOT_LINEAR_EUR_ULPS; } // Approximate cap-cap·(1-1/D)^n using the Binomial expansion // http://galileo.phys.virginia.edu/classes/152.mf1i.spring02/Exponential_Function.htm // Function[imax, -CAP*Sum[(-IR*EUR/CAP)^i/Factorial[i], {i, imax}]] // which may be simplified to // Function[imax, -CAP*Sum[(EUR)^i/(Factorial[i]*(-d)^i), {i, 1, imax}]] // where d = cap/initial_reward uint256 d = 230769230769230769230769231; // NEUMARK_CAP / INITIAL_REWARD_FRACTION uint256 term = NEUMARK_CAP; uint256 sum = 0; uint256 denom = d; do assembly { // We use assembler primarily to avoid the expensive // divide-by-zero check solc inserts for the / operator. term := div(mul(term, euroUlps), denom) sum := add(sum, term) denom := add(denom, d) // sub next term as we have power of negative value in the binomial expansion term := div(mul(term, euroUlps), denom) sum := sub(sum, term) denom := add(denom, d) } while (term != 0); return sum; } /// @notice find issuance curve inverse by binary search /// @param neumarkUlps neumark amount to compute inverse for /// @param minEurUlps minimum search range for the inverse, inclusive /// @param maxEurUlps maxium search range for the inverse, inclusive /// @dev in case of approximate search (no exact inverse) upper element of minimal search range is returned /// @dev in case of many possible inverses, the lowest one will be used (if range permits) /// @dev corresponds to a linear search that returns first euroUlp value that has cumulative() equal or greater than neumarkUlps function cumulativeInverse(uint256 neumarkUlps, uint256 minEurUlps, uint256 maxEurUlps) public constant returns (uint256 euroUlps) { require(maxEurUlps >= minEurUlps); require(cumulative(minEurUlps) <= neumarkUlps); require(cumulative(maxEurUlps) >= neumarkUlps); uint256 min = minEurUlps; uint256 max = maxEurUlps; // Binary search while (max > min) { uint256 mid = (max + min) / 2; uint256 val = cumulative(mid); // exact solution should not be used, a late points of the curve when many euroUlps are needed to // increase by one nmkUlp this will lead to "indeterministic" inverse values that depend on the initial min and max // and further binary division -> you can land at any of the euro value that is mapped to the same nmk value // with condition below removed, binary search will point to the lowest eur value possible which is good because it cannot be exploited even with 0 gas costs /* if (val == neumarkUlps) { return mid; }*/ // NOTE: approximate search (no inverse) must return upper element of the final range // last step of approximate search is always (min, min+1) so new mid is (2*min+1)/2 => min // so new min = mid + 1 = max which was upper range. and that ends the search // NOTE: when there are multiple inverses for the same neumarkUlps, the `max` will be dragged down // by `max = mid` expression to the lowest eur value of inverse. works only for ranges that cover all points of multiple inverse if (val < neumarkUlps) { min = mid + 1; } else { max = mid; } } // NOTE: It is possible that there is no inverse // for example curve(0) = 0 and curve(1) = 6, so // there is no value y such that curve(y) = 5. // When there is no inverse, we must return upper element of last search range. // This has the effect of reversing the curve less when // burning Neumarks. This ensures that Neumarks can always // be burned. It also ensure that the total supply of Neumarks // remains below the cap. return max; } function neumarkCap() public constant returns (uint256) { return NEUMARK_CAP; } function initialRewardFraction() public constant returns (uint256) { return INITIAL_REWARD_FRACTION; } } /// @title advances snapshot id on demand /// @dev see Snapshot folder for implementation examples ie. DailyAndSnapshotable contract contract ISnapshotable { //////////////////////// // Events //////////////////////// /// @dev should log each new snapshot id created, including snapshots created automatically via MSnapshotPolicy event LogSnapshotCreated(uint256 snapshotId); //////////////////////// // Public functions //////////////////////// /// always creates new snapshot id which gets returned /// however, there is no guarantee that any snapshot will be created with this id, this depends on the implementation of MSnaphotPolicy function createSnapshot() public returns (uint256); /// upper bound of series snapshotIds for which there's a value function currentSnapshotId() public constant returns (uint256); } /// @title Abstracts snapshot id creation logics /// @dev Mixin (internal interface) of the snapshot policy which abstracts snapshot id creation logics from Snapshot contract /// @dev to be implemented and such implementation should be mixed with Snapshot-derived contract, see EveryBlock for simplest example of implementation and StandardSnapshotToken contract MSnapshotPolicy { //////////////////////// // Internal functions //////////////////////// // The snapshot Ids need to be strictly increasing. // Whenever the snaspshot id changes, a new snapshot will be created. // As long as the same snapshot id is being returned, last snapshot will be updated as this indicates that snapshot id didn't change // // Values passed to `hasValueAt` and `valuteAt` are required // to be less or equal to `mCurrentSnapshotId()`. function mCurrentSnapshotId() internal returns (uint256); } /// @title creates snapshot id on each day boundary and allows to create additional snapshots within a given day /// @dev snapshots are encoded in single uint256, where high 128 bits represents a day number (from unix epoch) and low 128 bits represents additional snapshots within given day create via ISnapshotable contract DailyAndSnapshotable is MSnapshotPolicy, ISnapshotable { //////////////////////// // Constants //////////////////////// // Floor[2**128 / 1 days] uint256 private MAX_TIMESTAMP = 3938453320844195178974243141571391; //////////////////////// // Mutable state //////////////////////// uint256 private _currentSnapshotId; //////////////////////// // Constructor //////////////////////// /// @param start snapshotId from which to start generating values /// @dev start must be for the same day or 0, required for token cloning function DailyAndSnapshotable(uint256 start) internal { // 0 is invalid value as we are past unix epoch if (start > 0) { uint256 dayBase = snapshotAt(block.timestamp); require(start >= dayBase); // dayBase + 2**128 will not overflow as it is based on block.timestamp require(start < dayBase + 2**128); _currentSnapshotId = start; } } //////////////////////// // Public functions //////////////////////// function snapshotAt(uint256 timestamp) public constant returns (uint256) { require(timestamp < MAX_TIMESTAMP); uint256 dayBase = 2**128 * (timestamp / 1 days); return dayBase; } // // Implements ISnapshotable // function createSnapshot() public returns (uint256) { uint256 dayBase = 2**128 * (block.timestamp / 1 days); if (dayBase > _currentSnapshotId) { // New day has started, create snapshot for midnight _currentSnapshotId = dayBase; } else { // within single day, increase counter (assume 2**128 will not be crossed) _currentSnapshotId += 1; } // Log and return LogSnapshotCreated(_currentSnapshotId); return _currentSnapshotId; } function currentSnapshotId() public constant returns (uint256) { return mCurrentSnapshotId(); } //////////////////////// // Internal functions //////////////////////// // // Implements MSnapshotPolicy // function mCurrentSnapshotId() internal returns (uint256) { uint256 dayBase = 2**128 * (block.timestamp / 1 days); // New day has started if (dayBase > _currentSnapshotId) { _currentSnapshotId = dayBase; LogSnapshotCreated(dayBase); } return _currentSnapshotId; } } /// @title controls spending approvals /// @dev TokenAllowance observes this interface, Neumark contract implements it contract MTokenAllowanceController { //////////////////////// // Internal functions //////////////////////// /// @notice Notifies the controller about an approval allowing the /// controller to react if desired /// @param owner The address that calls `approve()` /// @param spender The spender in the `approve()` call /// @param amount The amount in the `approve()` call /// @return False if the controller does not authorize the approval function mOnApprove( address owner, address spender, uint256 amount ) internal returns (bool allow); } /// @title controls token transfers /// @dev BasicSnapshotToken observes this interface, Neumark contract implements it contract MTokenTransferController { //////////////////////// // Internal functions //////////////////////// /// @notice Notifies the controller about a token transfer allowing the /// controller to react if desired /// @param from The origin of the transfer /// @param to The destination of the transfer /// @param amount The amount of the transfer /// @return False if the controller does not authorize the transfer function mOnTransfer( address from, address to, uint256 amount ) internal returns (bool allow); } /// @title controls approvals and transfers /// @dev The token controller contract must implement these functions, see Neumark as example /// @dev please note that controller may be a separate contract that is called from mOnTransfer and mOnApprove functions contract MTokenController is MTokenTransferController, MTokenAllowanceController { } /// @title internal token transfer function /// @dev see BasicSnapshotToken for implementation contract MTokenTransfer { //////////////////////// // Internal functions //////////////////////// /// @dev This is the actual transfer function in the token contract, it can /// only be called by other functions in this contract. /// @param from The address holding the tokens being transferred /// @param to The address of the recipient /// @param amount The amount of tokens to be transferred /// @dev reverts if transfer was not successful function mTransfer( address from, address to, uint256 amount ) internal; } /// @title token spending approval and transfer /// @dev implements token approval and transfers and exposes relevant part of ERC20 and ERC677 approveAndCall /// may be mixed in with any basic token (implementing mTransfer) like BasicSnapshotToken or MintableSnapshotToken to add approval mechanism /// observes MTokenAllowanceController interface /// observes MTokenTransfer contract TokenAllowance is MTokenTransfer, MTokenAllowanceController, IERC20Allowance, IERC677Token { //////////////////////// // Mutable state //////////////////////// // `allowed` tracks rights to spends others tokens as per ERC20 mapping (address => mapping (address => uint256)) private _allowed; //////////////////////// // Constructor //////////////////////// function TokenAllowance() internal { } //////////////////////// // Public functions //////////////////////// // // Implements IERC20Token // /// @dev This function makes it easy to read the `allowed[]` map /// @param owner The address of the account that owns the token /// @param spender The address of the account able to transfer the tokens /// @return Amount of remaining tokens of _owner that _spender is allowed /// to spend function allowance(address owner, address spender) public constant returns (uint256 remaining) { return _allowed[owner][spender]; } /// @notice `msg.sender` approves `_spender` to spend `_amount` tokens on /// its behalf. This is a modified version of the ERC20 approve function /// where allowance per spender must be 0 to allow change of such allowance /// @param spender The address of the account able to transfer the tokens /// @param amount The amount of tokens to be approved for transfer /// @return True or reverts, False is never returned function approve(address spender, uint256 amount) public returns (bool success) { // Alerts the token controller of the approve function call require(mOnApprove(msg.sender, spender, amount)); // To change the approve amount you first have to reduce the addresses` // allowance to zero by calling `approve(_spender,0)` if it is not // already 0 to mitigate the race condition described here: // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 require((amount == 0) || (_allowed[msg.sender][spender] == 0)); _allowed[msg.sender][spender] = amount; Approval(msg.sender, spender, amount); return true; } /// @notice Send `_amount` tokens to `_to` from `_from` on the condition it /// is approved by `_from` /// @param from The address holding the tokens being transferred /// @param to The address of the recipient /// @param amount The amount of tokens to be transferred /// @return True if the transfer was successful, reverts in any other case function transferFrom(address from, address to, uint256 amount) public returns (bool success) { // The standard ERC 20 transferFrom functionality bool amountApproved = _allowed[from][msg.sender] >= amount; require(amountApproved); _allowed[from][msg.sender] -= amount; mTransfer(from, to, amount); return true; } // // Implements IERC677Token // /// @notice `msg.sender` approves `_spender` to send `_amount` tokens on /// its behalf, and then a function is triggered in the contract that is /// being approved, `_spender`. This allows users to use their tokens to /// interact with contracts in one function call instead of two /// @param spender The address of the contract able to transfer the tokens /// @param amount The amount of tokens to be approved for transfer /// @return True or reverts, False is never returned function approveAndCall( address spender, uint256 amount, bytes extraData ) public returns (bool success) { require(approve(spender, amount)); success = IERC677Callback(spender).receiveApproval( msg.sender, amount, this, extraData ); require(success); return true; } } /// @title Reads and writes snapshots /// @dev Manages reading and writing a series of values, where each value has assigned a snapshot id for access to historical data /// @dev may be added to any contract to provide snapshotting mechanism. should be mixed in with any of MSnapshotPolicy implementations to customize snapshot creation mechanics /// observes MSnapshotPolicy /// based on MiniMe token contract Snapshot is MSnapshotPolicy { //////////////////////// // Types //////////////////////// /// @dev `Values` is the structure that attaches a snapshot id to a /// given value, the snapshot id attached is the one that last changed the /// value struct Values { // `snapshotId` is the snapshot id that the value was generated at uint256 snapshotId; // `value` at a specific snapshot id uint256 value; } //////////////////////// // Internal functions //////////////////////// function hasValue( Values[] storage values ) internal constant returns (bool) { return values.length > 0; } /// @dev makes sure that 'snapshotId' between current snapshot id (mCurrentSnapshotId) and first snapshot id. this guarantees that getValueAt returns value from one of the snapshots. function hasValueAt( Values[] storage values, uint256 snapshotId ) internal constant returns (bool) { require(snapshotId <= mCurrentSnapshotId()); return values.length > 0 && values[0].snapshotId <= snapshotId; } /// gets last value in the series function getValue( Values[] storage values, uint256 defaultValue ) internal constant returns (uint256) { if (values.length == 0) { return defaultValue; } else { uint256 last = values.length - 1; return values[last].value; } } /// @dev `getValueAt` retrieves value at a given snapshot id /// @param values The series of values being queried /// @param snapshotId Snapshot id to retrieve the value at /// @return Value in series being queried function getValueAt( Values[] storage values, uint256 snapshotId, uint256 defaultValue ) internal constant returns (uint256) { require(snapshotId <= mCurrentSnapshotId()); // Empty value if (values.length == 0) { return defaultValue; } // Shortcut for the out of bounds snapshots uint256 last = values.length - 1; uint256 lastSnapshot = values[last].snapshotId; if (snapshotId >= lastSnapshot) { return values[last].value; } uint256 firstSnapshot = values[0].snapshotId; if (snapshotId < firstSnapshot) { return defaultValue; } // Binary search of the value in the array uint256 min = 0; uint256 max = last; while (max > min) { uint256 mid = (max + min + 1) / 2; // must always return lower indice for approximate searches if (values[mid].snapshotId <= snapshotId) { min = mid; } else { max = mid - 1; } } return values[min].value; } /// @dev `setValue` used to update sequence at next snapshot /// @param values The sequence being updated /// @param value The new last value of sequence function setValue( Values[] storage values, uint256 value ) internal { // TODO: simplify or break into smaller functions uint256 currentSnapshotId = mCurrentSnapshotId(); // Always create a new entry if there currently is no value bool empty = values.length == 0; if (empty) { // Create a new entry values.push( Values({ snapshotId: currentSnapshotId, value: value }) ); return; } uint256 last = values.length - 1; bool hasNewSnapshot = values[last].snapshotId < currentSnapshotId; if (hasNewSnapshot) { // Do nothing if the value was not modified bool unmodified = values[last].value == value; if (unmodified) { return; } // Create new entry values.push( Values({ snapshotId: currentSnapshotId, value: value }) ); } else { // We are updating the currentSnapshotId bool previousUnmodified = last > 0 && values[last - 1].value == value; if (previousUnmodified) { // Remove current snapshot if current value was set to previous value delete values[last]; values.length--; return; } // Overwrite next snapshot entry values[last].value = value; } } } /// @title access to snapshots of a token /// @notice allows to implement complex token holder rights like revenue disbursal or voting /// @notice snapshots are series of values with assigned ids. ids increase strictly. particular id mechanism is not assumed contract ITokenSnapshots { //////////////////////// // Public functions //////////////////////// /// @notice Total amount of tokens at a specific `snapshotId`. /// @param snapshotId of snapshot at which totalSupply is queried /// @return The total amount of tokens at `snapshotId` /// @dev reverts on snapshotIds greater than currentSnapshotId() /// @dev returns 0 for snapshotIds less than snapshotId of first value function totalSupplyAt(uint256 snapshotId) public constant returns(uint256); /// @dev Queries the balance of `owner` at a specific `snapshotId` /// @param owner The address from which the balance will be retrieved /// @param snapshotId of snapshot at which the balance is queried /// @return The balance at `snapshotId` function balanceOfAt(address owner, uint256 snapshotId) public constant returns (uint256); /// @notice upper bound of series of snapshotIds for which there's a value in series /// @return snapshotId function currentSnapshotId() public constant returns (uint256); } /// @title represents link between cloned and parent token /// @dev when token is clone from other token, initial balances of the cloned token /// correspond to balances of parent token at the moment of parent snapshot id specified /// @notice please note that other tokens beside snapshot token may be cloned contract IClonedTokenParent is ITokenSnapshots { //////////////////////// // Public functions //////////////////////// /// @return address of parent token, address(0) if root /// @dev parent token does not need to clonable, nor snapshottable, just a normal token function parentToken() public constant returns(IClonedTokenParent parent); /// @return snapshot at wchich initial token distribution was taken function parentSnapshotId() public constant returns(uint256 snapshotId); } /// @title token with snapshots and transfer functionality /// @dev observes MTokenTransferController interface /// observes ISnapshotToken interface /// implementes MTokenTransfer interface contract BasicSnapshotToken is MTokenTransfer, MTokenTransferController, IBasicToken, IClonedTokenParent, Snapshot { //////////////////////// // Immutable state //////////////////////// // `PARENT_TOKEN` is the Token address that was cloned to produce this token; // it will be 0x0 for a token that was not cloned IClonedTokenParent private PARENT_TOKEN; // `PARENT_SNAPSHOT_ID` is the snapshot id from the Parent Token that was // used to determine the initial distribution of the cloned token uint256 private PARENT_SNAPSHOT_ID; //////////////////////// // Mutable state //////////////////////// // `balances` is the map that tracks the balance of each address, in this // contract when the balance changes the snapshot id that the change // occurred is also included in the map mapping (address => Values[]) internal _balances; // Tracks the history of the `totalSupply` of the token Values[] internal _totalSupplyValues; //////////////////////// // Constructor //////////////////////// /// @notice Constructor to create snapshot token /// @param parentToken Address of the parent token, set to 0x0 if it is a /// new token /// @param parentSnapshotId at which snapshot id clone was created, set to 0 to clone at upper bound /// @dev please not that as long as cloned token does not overwrite value at current snapshot id, it will refer /// to parent token at which this snapshot still may change until snapshot id increases. for that time tokens are coupled /// this is prevented by parentSnapshotId value of parentToken.currentSnapshotId() - 1 being the maxiumum /// see SnapshotToken.js test to learn consequences coupling has. function BasicSnapshotToken( IClonedTokenParent parentToken, uint256 parentSnapshotId ) Snapshot() internal { PARENT_TOKEN = parentToken; if (parentToken == address(0)) { require(parentSnapshotId == 0); } else { if (parentSnapshotId == 0) { require(parentToken.currentSnapshotId() > 0); PARENT_SNAPSHOT_ID = parentToken.currentSnapshotId() - 1; } else { PARENT_SNAPSHOT_ID = parentSnapshotId; } } } //////////////////////// // Public functions //////////////////////// // // Implements IBasicToken // /// @dev This function makes it easy to get the total number of tokens /// @return The total number of tokens function totalSupply() public constant returns (uint256) { return totalSupplyAtInternal(mCurrentSnapshotId()); } /// @param owner The address that's balance is being requested /// @return The balance of `owner` at the current block function balanceOf(address owner) public constant returns (uint256 balance) { return balanceOfAtInternal(owner, mCurrentSnapshotId()); } /// @notice Send `amount` tokens to `to` from `msg.sender` /// @param to The address of the recipient /// @param amount The amount of tokens to be transferred /// @return True if the transfer was successful, reverts in any other case function transfer(address to, uint256 amount) public returns (bool success) { mTransfer(msg.sender, to, amount); return true; } // // Implements ITokenSnapshots // function totalSupplyAt(uint256 snapshotId) public constant returns(uint256) { return totalSupplyAtInternal(snapshotId); } function balanceOfAt(address owner, uint256 snapshotId) public constant returns (uint256) { return balanceOfAtInternal(owner, snapshotId); } function currentSnapshotId() public constant returns (uint256) { return mCurrentSnapshotId(); } // // Implements IClonedTokenParent // function parentToken() public constant returns(IClonedTokenParent parent) { return PARENT_TOKEN; } /// @return snapshot at wchich initial token distribution was taken function parentSnapshotId() public constant returns(uint256 snapshotId) { return PARENT_SNAPSHOT_ID; } // // Other public functions // /// @notice gets all token balances of 'owner' /// @dev intended to be called via eth_call where gas limit is not an issue function allBalancesOf(address owner) external constant returns (uint256[2][]) { /* very nice and working implementation below, // copy to memory Values[] memory values = _balances[owner]; do assembly { // in memory structs have simple layout where every item occupies uint256 balances := values } while (false);*/ Values[] storage values = _balances[owner]; uint256[2][] memory balances = new uint256[2][](values.length); for(uint256 ii = 0; ii < values.length; ++ii) { balances[ii] = [values[ii].snapshotId, values[ii].value]; } return balances; } //////////////////////// // Internal functions //////////////////////// function totalSupplyAtInternal(uint256 snapshotId) public constant returns(uint256) { Values[] storage values = _totalSupplyValues; // If there is a value, return it, reverts if value is in the future if (hasValueAt(values, snapshotId)) { return getValueAt(values, snapshotId, 0); } // Try parent contract at or before the fork if (address(PARENT_TOKEN) != 0) { uint256 earlierSnapshotId = PARENT_SNAPSHOT_ID > snapshotId ? snapshotId : PARENT_SNAPSHOT_ID; return PARENT_TOKEN.totalSupplyAt(earlierSnapshotId); } // Default to an empty balance return 0; } // get balance at snapshot if with continuation in parent token function balanceOfAtInternal(address owner, uint256 snapshotId) internal constant returns (uint256) { Values[] storage values = _balances[owner]; // If there is a value, return it, reverts if value is in the future if (hasValueAt(values, snapshotId)) { return getValueAt(values, snapshotId, 0); } // Try parent contract at or before the fork if (PARENT_TOKEN != address(0)) { uint256 earlierSnapshotId = PARENT_SNAPSHOT_ID > snapshotId ? snapshotId : PARENT_SNAPSHOT_ID; return PARENT_TOKEN.balanceOfAt(owner, earlierSnapshotId); } // Default to an empty balance return 0; } // // Implements MTokenTransfer // /// @dev This is the actual transfer function in the token contract, it can /// only be called by other functions in this contract. /// @param from The address holding the tokens being transferred /// @param to The address of the recipient /// @param amount The amount of tokens to be transferred /// @return True if the transfer was successful, reverts in any other case function mTransfer( address from, address to, uint256 amount ) internal { // never send to address 0 require(to != address(0)); // block transfers in clone that points to future/current snapshots of patent token require(parentToken() == address(0) || parentSnapshotId() < parentToken().currentSnapshotId()); // Alerts the token controller of the transfer require(mOnTransfer(from, to, amount)); // If the amount being transfered is more than the balance of the // account the transfer reverts var previousBalanceFrom = balanceOf(from); require(previousBalanceFrom >= amount); // First update the balance array with the new value for the address // sending the tokens uint256 newBalanceFrom = previousBalanceFrom - amount; setValue(_balances[from], newBalanceFrom); // Then update the balance array with the new value for the address // receiving the tokens uint256 previousBalanceTo = balanceOf(to); uint256 newBalanceTo = previousBalanceTo + amount; assert(newBalanceTo >= previousBalanceTo); // Check for overflow setValue(_balances[to], newBalanceTo); // An event to make the transfer easy to find on the blockchain Transfer(from, to, amount); } } /// @title token generation and destruction /// @dev internal interface providing token generation and destruction, see MintableSnapshotToken for implementation contract MTokenMint { //////////////////////// // Internal functions //////////////////////// /// @notice Generates `amount` tokens that are assigned to `owner` /// @param owner The address that will be assigned the new tokens /// @param amount The quantity of tokens generated /// @dev reverts if tokens could not be generated function mGenerateTokens(address owner, uint256 amount) internal; /// @notice Burns `amount` tokens from `owner` /// @param owner The address that will lose the tokens /// @param amount The quantity of tokens to burn /// @dev reverts if tokens could not be destroyed function mDestroyTokens(address owner, uint256 amount) internal; } /// @title basic snapshot token with facitilites to generate and destroy tokens /// @dev implementes MTokenMint, does not expose any public functions that create/destroy tokens contract MintableSnapshotToken is BasicSnapshotToken, MTokenMint { //////////////////////// // Constructor //////////////////////// /// @notice Constructor to create a MintableSnapshotToken /// @param parentToken Address of the parent token, set to 0x0 if it is a /// new token function MintableSnapshotToken( IClonedTokenParent parentToken, uint256 parentSnapshotId ) BasicSnapshotToken(parentToken, parentSnapshotId) internal {} /// @notice Generates `amount` tokens that are assigned to `owner` /// @param owner The address that will be assigned the new tokens /// @param amount The quantity of tokens generated function mGenerateTokens(address owner, uint256 amount) internal { // never create for address 0 require(owner != address(0)); // block changes in clone that points to future/current snapshots of patent token require(parentToken() == address(0) || parentSnapshotId() < parentToken().currentSnapshotId()); uint256 curTotalSupply = totalSupply(); uint256 newTotalSupply = curTotalSupply + amount; require(newTotalSupply >= curTotalSupply); // Check for overflow uint256 previousBalanceTo = balanceOf(owner); uint256 newBalanceTo = previousBalanceTo + amount; assert(newBalanceTo >= previousBalanceTo); // Check for overflow setValue(_totalSupplyValues, newTotalSupply); setValue(_balances[owner], newBalanceTo); Transfer(0, owner, amount); } /// @notice Burns `amount` tokens from `owner` /// @param owner The address that will lose the tokens /// @param amount The quantity of tokens to burn function mDestroyTokens(address owner, uint256 amount) internal { // block changes in clone that points to future/current snapshots of patent token require(parentToken() == address(0) || parentSnapshotId() < parentToken().currentSnapshotId()); uint256 curTotalSupply = totalSupply(); require(curTotalSupply >= amount); uint256 previousBalanceFrom = balanceOf(owner); require(previousBalanceFrom >= amount); uint256 newTotalSupply = curTotalSupply - amount; uint256 newBalanceFrom = previousBalanceFrom - amount; setValue(_totalSupplyValues, newTotalSupply); setValue(_balances[owner], newBalanceFrom); Transfer(owner, 0, amount); } } /* Copyright 2016, Jordi Baylina Copyright 2017, Remco Bloemen, Marcin Rudolf This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /// @title StandardSnapshotToken Contract /// @author Jordi Baylina, Remco Bloemen, Marcin Rudolf /// @dev This token contract's goal is to make it easy for anyone to clone this /// token using the token distribution at a given block, this will allow DAO's /// and DApps to upgrade their features in a decentralized manner without /// affecting the original token /// @dev It is ERC20 compliant, but still needs to under go further testing. /// @dev Various contracts are composed to provide required functionality of this token, different compositions are possible /// MintableSnapshotToken provides transfer, miniting and snapshotting functions /// TokenAllowance provides approve/transferFrom functions /// TokenMetadata adds name, symbol and other token metadata /// @dev This token is still abstract, Snapshot, BasicSnapshotToken and TokenAllowance observe interfaces that must be implemented /// MSnapshotPolicy - particular snapshot id creation mechanism /// MTokenController - controlls approvals and transfers /// see Neumark as an example /// @dev implements ERC223 token transfer contract StandardSnapshotToken is IERC20Token, MintableSnapshotToken, TokenAllowance, IERC223Token, IsContract { //////////////////////// // Constructor //////////////////////// /// @notice Constructor to create a MiniMeToken /// is a new token /// param tokenName Name of the new token /// param decimalUnits Number of decimals of the new token /// param tokenSymbol Token Symbol for the new token function StandardSnapshotToken( IClonedTokenParent parentToken, uint256 parentSnapshotId ) MintableSnapshotToken(parentToken, parentSnapshotId) TokenAllowance() internal {} //////////////////////// // Public functions //////////////////////// // // Implements IERC223Token // function transfer(address to, uint256 amount, bytes data) public returns (bool) { // it is necessary to point out implementation to be called BasicSnapshotToken.mTransfer(msg.sender, to, amount); // Notify the receiving contract. if (isContract(to)) { IERC223Callback(to).onTokenTransfer(msg.sender, amount, data); } return true; } } contract Neumark is AccessControlled, AccessRoles, Agreement, DailyAndSnapshotable, StandardSnapshotToken, TokenMetadata, NeumarkIssuanceCurve, Reclaimable { //////////////////////// // Constants //////////////////////// string private constant TOKEN_NAME = "Neumark"; uint8 private constant TOKEN_DECIMALS = 18; string private constant TOKEN_SYMBOL = "NEU"; string private constant VERSION = "NMK_1.0"; //////////////////////// // Mutable state //////////////////////// // disable transfers when Neumark is created bool private _transferEnabled = false; // at which point on curve new Neumarks will be created, see NeumarkIssuanceCurve contract // do not use to get total invested funds. see burn(). this is just a cache for expensive inverse function uint256 private _totalEurUlps; //////////////////////// // Events //////////////////////// event LogNeumarksIssued( address indexed owner, uint256 euroUlps, uint256 neumarkUlps ); event LogNeumarksBurned( address indexed owner, uint256 euroUlps, uint256 neumarkUlps ); //////////////////////// // Constructor //////////////////////// function Neumark( IAccessPolicy accessPolicy, IEthereumForkArbiter forkArbiter ) AccessControlled(accessPolicy) AccessRoles() Agreement(accessPolicy, forkArbiter) StandardSnapshotToken( IClonedTokenParent(0x0), 0 ) TokenMetadata( TOKEN_NAME, TOKEN_DECIMALS, TOKEN_SYMBOL, VERSION ) DailyAndSnapshotable(0) NeumarkIssuanceCurve() Reclaimable() public {} //////////////////////// // Public functions //////////////////////// /// @notice issues new Neumarks to msg.sender with reward at current curve position /// moves curve position by euroUlps /// callable only by ROLE_NEUMARK_ISSUER function issueForEuro(uint256 euroUlps) public only(ROLE_NEUMARK_ISSUER) acceptAgreement(msg.sender) returns (uint256) { require(_totalEurUlps + euroUlps >= _totalEurUlps); uint256 neumarkUlps = incremental(_totalEurUlps, euroUlps); _totalEurUlps += euroUlps; mGenerateTokens(msg.sender, neumarkUlps); LogNeumarksIssued(msg.sender, euroUlps, neumarkUlps); return neumarkUlps; } /// @notice used by ROLE_NEUMARK_ISSUER to transer newly issued neumarks /// typically to the investor and platform operator function distribute(address to, uint256 neumarkUlps) public only(ROLE_NEUMARK_ISSUER) acceptAgreement(to) { mTransfer(msg.sender, to, neumarkUlps); } /// @notice msg.sender can burn their Neumarks, curve is rolled back using inverse /// curve. as a result cost of Neumark gets lower (reward is higher) function burn(uint256 neumarkUlps) public only(ROLE_NEUMARK_BURNER) { burnPrivate(neumarkUlps, 0, _totalEurUlps); } /// @notice executes as function above but allows to provide search range for low gas burning function burn(uint256 neumarkUlps, uint256 minEurUlps, uint256 maxEurUlps) public only(ROLE_NEUMARK_BURNER) { burnPrivate(neumarkUlps, minEurUlps, maxEurUlps); } function enableTransfer(bool enabled) public only(ROLE_TRANSFER_ADMIN) { _transferEnabled = enabled; } function createSnapshot() public only(ROLE_SNAPSHOT_CREATOR) returns (uint256) { return DailyAndSnapshotable.createSnapshot(); } function transferEnabled() public constant returns (bool) { return _transferEnabled; } function totalEuroUlps() public constant returns (uint256) { return _totalEurUlps; } function incremental(uint256 euroUlps) public constant returns (uint256 neumarkUlps) { return incremental(_totalEurUlps, euroUlps); } //////////////////////// // Internal functions //////////////////////// // // Implements MTokenController // function mOnTransfer( address from, address, // to uint256 // amount ) internal acceptAgreement(from) returns (bool allow) { // must have transfer enabled or msg.sender is Neumark issuer return _transferEnabled || accessPolicy().allowed(msg.sender, ROLE_NEUMARK_ISSUER, this, msg.sig); } function mOnApprove( address owner, address, // spender, uint256 // amount ) internal acceptAgreement(owner) returns (bool allow) { return true; } //////////////////////// // Private functions //////////////////////// function burnPrivate(uint256 burnNeumarkUlps, uint256 minEurUlps, uint256 maxEurUlps) private { uint256 prevEuroUlps = _totalEurUlps; // burn first in the token to make sure balance/totalSupply is not crossed mDestroyTokens(msg.sender, burnNeumarkUlps); _totalEurUlps = cumulativeInverse(totalSupply(), minEurUlps, maxEurUlps); // actually may overflow on non-monotonic inverse assert(prevEuroUlps >= _totalEurUlps); uint256 euroUlps = prevEuroUlps - _totalEurUlps; LogNeumarksBurned(msg.sender, euroUlps, burnNeumarkUlps); } } contract TimeSource { //////////////////////// // Public functions //////////////////////// function currentTime() internal constant returns (uint256) { return block.timestamp; } } contract LockedAccount is AccessControlled, AccessRoles, TimeSource, Math, IsContract, MigrationSource, IERC677Callback, Reclaimable { //////////////////////// // Type declarations //////////////////////// // state space of LockedAccount enum LockState { // controller is not yet set Uncontrolled, // new funds lockd are accepted from investors AcceptingLocks, // funds may be unlocked by investors, final state AcceptingUnlocks, // funds may be unlocked by investors, without any constraints, final state ReleaseAll } // represents locked account of the investor struct Account { // funds locked in the account uint256 balance; // neumark amount that must be returned to unlock uint256 neumarksDue; // date with which unlock may happen without penalty uint256 unlockDate; } //////////////////////// // Immutable state //////////////////////// // a token controlled by LockedAccount, read ERC20 + extensions to read what // token is it (ETH/EUR etc.) IERC677Token private ASSET_TOKEN; Neumark private NEUMARK; // longstop period in seconds uint256 private LOCK_PERIOD; // penalty: decimalFraction of stored amount on escape hatch uint256 private PENALTY_FRACTION; //////////////////////// // Mutable state //////////////////////// // total amount of tokens locked uint256 private _totalLockedAmount; // total number of locked investors uint256 internal _totalInvestors; // current state of the locking contract LockState private _lockState; // controlling contract that may lock money or unlock all account if fails address private _controller; // fee distribution pool address private _penaltyDisbursalAddress; // LockedAccountMigration private migration; mapping(address => Account) internal _accounts; //////////////////////// // Events //////////////////////// /// @notice logged when funds are locked by investor /// @param investor address of investor locking funds /// @param amount amount of newly locked funds /// @param amount of neumarks that must be returned to unlock funds event LogFundsLocked( address indexed investor, uint256 amount, uint256 neumarks ); /// @notice logged when investor unlocks funds /// @param investor address of investor unlocking funds /// @param amount amount of unlocked funds /// @param neumarks amount of Neumarks that was burned event LogFundsUnlocked( address indexed investor, uint256 amount, uint256 neumarks ); /// @notice logged when unlock penalty is disbursed to Neumark holders /// @param disbursalPoolAddress address of disbursal pool receiving penalty /// @param amount penalty amount /// @param assetToken address of token contract penalty was paid with /// @param investor addres of investor paying penalty /// @dev assetToken and investor parameters are added for quick tallying penalty payouts event LogPenaltyDisbursed( address indexed disbursalPoolAddress, uint256 amount, address assetToken, address investor ); /// @notice logs Locked Account state transitions event LogLockStateTransition( LockState oldState, LockState newState ); event LogInvestorMigrated( address indexed investor, uint256 amount, uint256 neumarks, uint256 unlockDate ); //////////////////////// // Modifiers //////////////////////// modifier onlyController() { require(msg.sender == address(_controller)); _; } modifier onlyState(LockState state) { require(_lockState == state); _; } modifier onlyStates(LockState state1, LockState state2) { require(_lockState == state1 || _lockState == state2); _; } //////////////////////// // Constructor //////////////////////// /// @notice creates new LockedAccount instance /// @param policy governs execution permissions to admin functions /// @param assetToken token contract representing funds locked /// @param neumark Neumark token contract /// @param penaltyDisbursalAddress address of disbursal contract for penalty fees /// @param lockPeriod period for which funds are locked, in seconds /// @param penaltyFraction decimal fraction of unlocked amount paid as penalty, /// if unlocked before lockPeriod is over /// @dev this implementation does not allow spending funds on ICOs but provides /// a migration mechanism to final LockedAccount with such functionality function LockedAccount( IAccessPolicy policy, IERC677Token assetToken, Neumark neumark, address penaltyDisbursalAddress, uint256 lockPeriod, uint256 penaltyFraction ) AccessControlled(policy) MigrationSource(policy, ROLE_LOCKED_ACCOUNT_ADMIN) Reclaimable() public { ASSET_TOKEN = assetToken; NEUMARK = neumark; LOCK_PERIOD = lockPeriod; PENALTY_FRACTION = penaltyFraction; _penaltyDisbursalAddress = penaltyDisbursalAddress; } //////////////////////// // Public functions //////////////////////// /// @notice locks funds of investors for a period of time /// @param investor funds owner /// @param amount amount of funds locked /// @param neumarks amount of neumarks that needs to be returned by investor to unlock funds /// @dev callable only from controller (Commitment) contract function lock(address investor, uint256 amount, uint256 neumarks) public onlyState(LockState.AcceptingLocks) onlyController() { require(amount > 0); // transfer to itself from Commitment contract allowance assert(ASSET_TOKEN.transferFrom(msg.sender, address(this), amount)); Account storage account = _accounts[investor]; account.balance = addBalance(account.balance, amount); account.neumarksDue = add(account.neumarksDue, neumarks); if (account.unlockDate == 0) { // this is new account - unlockDate always > 0 _totalInvestors += 1; account.unlockDate = currentTime() + LOCK_PERIOD; } LogFundsLocked(investor, amount, neumarks); } /// @notice unlocks investors funds, see unlockInvestor for details /// @dev function requires that proper allowance on Neumark is made to LockedAccount by msg.sender /// except in ReleaseAll state which does not burn Neumark function unlock() public onlyStates(LockState.AcceptingUnlocks, LockState.ReleaseAll) { unlockInvestor(msg.sender); } /// @notice unlocks investors funds, see unlockInvestor for details /// @dev this ERC667 callback by Neumark contract after successful approve /// allows to unlock and allow neumarks to be burned in one transaction function receiveApproval( address from, uint256, // _amount, address _token, bytes _data ) public onlyState(LockState.AcceptingUnlocks) returns (bool) { require(msg.sender == _token); require(_data.length == 0); // only from neumarks require(_token == address(NEUMARK)); // this will check if allowance was made and if _amount is enough to // unlock, reverts on any error condition unlockInvestor(from); // we assume external call so return value will be lost to clients // that's why we throw above return true; } /// allows to anyone to release all funds without burning Neumarks and any /// other penalties function controllerFailed() public onlyState(LockState.AcceptingLocks) onlyController() { changeState(LockState.ReleaseAll); } /// allows anyone to use escape hatch function controllerSucceeded() public onlyState(LockState.AcceptingLocks) onlyController() { changeState(LockState.AcceptingUnlocks); } function setController(address controller) public only(ROLE_LOCKED_ACCOUNT_ADMIN) onlyState(LockState.Uncontrolled) { _controller = controller; changeState(LockState.AcceptingLocks); } /// sets address to which tokens from unlock penalty are sent /// both simple addresses and contracts are allowed /// contract needs to implement ApproveAndCallCallback interface function setPenaltyDisbursal(address penaltyDisbursalAddress) public only(ROLE_LOCKED_ACCOUNT_ADMIN) { require(penaltyDisbursalAddress != address(0)); // can be changed at any moment by admin _penaltyDisbursalAddress = penaltyDisbursalAddress; } function assetToken() public constant returns (IERC677Token) { return ASSET_TOKEN; } function neumark() public constant returns (Neumark) { return NEUMARK; } function lockPeriod() public constant returns (uint256) { return LOCK_PERIOD; } function penaltyFraction() public constant returns (uint256) { return PENALTY_FRACTION; } function balanceOf(address investor) public constant returns (uint256, uint256, uint256) { Account storage account = _accounts[investor]; return (account.balance, account.neumarksDue, account.unlockDate); } function controller() public constant returns (address) { return _controller; } function lockState() public constant returns (LockState) { return _lockState; } function totalLockedAmount() public constant returns (uint256) { return _totalLockedAmount; } function totalInvestors() public constant returns (uint256) { return _totalInvestors; } function penaltyDisbursalAddress() public constant returns (address) { return _penaltyDisbursalAddress; } // // Overrides migration source // /// enables migration to new LockedAccount instance /// it can be set only once to prevent setting temporary migrations that let /// just one investor out /// may be set in AcceptingLocks state (in unlikely event that controller /// fails we let investors out) /// and AcceptingUnlocks - which is normal operational mode function enableMigration(IMigrationTarget migration) public onlyStates(LockState.AcceptingLocks, LockState.AcceptingUnlocks) { // will enforce other access controls MigrationSource.enableMigration(migration); } /// migrates single investor function migrate() public onlyMigrationEnabled() { // migrates Account memory account = _accounts[msg.sender]; // return on non existing accounts silently if (account.balance == 0) { return; } // this will clear investor storage removeInvestor(msg.sender, account.balance); // let migration target to own asset balance that belongs to investor assert(ASSET_TOKEN.approve(address(_migration), account.balance)); LockedAccountMigration(_migration).migrateInvestor( msg.sender, account.balance, account.neumarksDue, account.unlockDate ); LogInvestorMigrated(msg.sender, account.balance, account.neumarksDue, account.unlockDate); } // // Overrides Reclaimable // /// @notice allows LockedAccount to reclaim tokens wrongly sent to its address /// @dev as LockedAccount by design has balance of assetToken (in the name of investors) /// such reclamation is not allowed function reclaim(IBasicToken token) public { // forbid reclaiming locked tokens require(token != ASSET_TOKEN); Reclaimable.reclaim(token); } //////////////////////// // Internal functions //////////////////////// function addBalance(uint256 balance, uint256 amount) internal returns (uint256) { _totalLockedAmount = add(_totalLockedAmount, amount); uint256 newBalance = balance + amount; return newBalance; } //////////////////////// // Private functions //////////////////////// function subBalance(uint256 balance, uint256 amount) private returns (uint256) { _totalLockedAmount -= amount; return balance - amount; } function removeInvestor(address investor, uint256 balance) private { subBalance(balance, balance); _totalInvestors -= 1; delete _accounts[investor]; } function changeState(LockState newState) private { assert(newState != _lockState); LogLockStateTransition(_lockState, newState); _lockState = newState; } /// @notice unlocks 'investor' tokens by making them withdrawable from assetToken /// @dev expects number of neumarks that is due on investor's account to be approved for LockedAccount for transfer /// @dev there are 3 unlock modes depending on contract and investor state /// in 'AcceptingUnlocks' state Neumarks due will be burned and funds transferred to investors address in assetToken, /// before unlockDate, penalty is deduced and distributed /// in 'ReleaseAll' neumarks are not burned and unlockDate is not observed, funds are unlocked unconditionally function unlockInvestor(address investor) private { // use memory storage to obtain copy and be able to erase storage Account memory accountInMem = _accounts[investor]; // silently return on non-existing accounts if (accountInMem.balance == 0) { return; } // remove investor account before external calls removeInvestor(investor, accountInMem.balance); // Neumark burning and penalty processing only in AcceptingUnlocks state if (_lockState == LockState.AcceptingUnlocks) { // transfer Neumarks to be burned to itself via allowance mechanism // not enough allowance results in revert which is acceptable state so 'require' is used require(NEUMARK.transferFrom(investor, address(this), accountInMem.neumarksDue)); // burn neumarks corresponding to unspent funds NEUMARK.burn(accountInMem.neumarksDue); // take the penalty if before unlockDate if (currentTime() < accountInMem.unlockDate) { require(_penaltyDisbursalAddress != address(0)); uint256 penalty = decimalFraction(accountInMem.balance, PENALTY_FRACTION); // distribute penalty if (isContract(_penaltyDisbursalAddress)) { require( ASSET_TOKEN.approveAndCall(_penaltyDisbursalAddress,penalty, "") ); } else { // transfer to simple address assert(ASSET_TOKEN.transfer(_penaltyDisbursalAddress, penalty)); } LogPenaltyDisbursed(_penaltyDisbursalAddress, penalty, ASSET_TOKEN, investor); accountInMem.balance -= penalty; } } if (_lockState == LockState.ReleaseAll) { accountInMem.neumarksDue = 0; } // transfer amount back to investor - now it can withdraw assert(ASSET_TOKEN.transfer(investor, accountInMem.balance)); LogFundsUnlocked(investor, accountInMem.balance, accountInMem.neumarksDue); } } /// @title state machine for Commitment contract /// @notice implements following state progression Before --> Whitelist --> Public --> Finished /// @dev state switching via 'transitionTo' function /// @dev inherited contract must implement mAfterTransition which will be called just after state transition happened contract StateMachine { //////////////////////// // Types //////////////////////// enum State { Before, Whitelist, Public, Finished } //////////////////////// // Mutable state //////////////////////// // current state State private _state = State.Before; //////////////////////// // Events //////////////////////// event LogStateTransition( State oldState, State newState ); //////////////////////// // Modifiers //////////////////////// modifier onlyState(State state) { require(_state == state); _; } modifier onlyStates(State state0, State state1) { require(_state == state0 || _state == state1); _; } /// @dev Multiple states can be handled by adding more modifiers. /* modifier notInState(State state) { require(_state != state); _; }*/ //////////////////////// // Constructor //////////////////////// function StateMachine() internal { } //////////////////////// // Public functions //////////////////////// function state() public constant returns (State) { return _state; } //////////////////////// // Internal functions //////////////////////// // @dev Transitioning to the same state is silently ignored, no log events // or handlers are called. function transitionTo(State newState) internal { State oldState = _state; require(validTransition(oldState, newState)); _state = newState; LogStateTransition(oldState, newState); // should not change state and it is required here. mAfterTransition(oldState, newState); require(_state == newState); } function validTransition(State oldState, State newState) private constant returns (bool valid) { return ( oldState == State.Before && newState == State.Whitelist) || ( oldState == State.Whitelist && newState == State.Public) || ( oldState == State.Public && newState == State.Finished ); } /// @notice gets called after every state transition. /// @dev may not change state, transitionTo will revert on that condition function mAfterTransition(State oldState, State newState) internal; } /// @title time induced state machine /// @notice ------ time -----> /// /// +--------+-----------+--------+------------ /// | Before | Whitelist | Public | Finished … /// +--------+-----------+--------+------------ /// @dev intended usage via 'withTimedTransitions' modifier which makes sure that state machine transitions into /// correct state before executing function body. note that this is contract state changing modifier so use with care /// @dev state change request is publicly accessible via 'handleTimedTransitions' /// @dev time is based on block.timestamp contract TimedStateMachine is StateMachine { //////////////////////// // Constants //////////////////////// // duration of Whitelist state int256 private constant WHITELIST_DURATION = 5 days; // duration of Public state int256 private constant PUBLIC_DURATION = 30 days; //////////////////////// // Immutable state //////////////////////// // timestamp at which Whitelist phase of Commitment starts // @dev set in TimedStateMachine constructor, it is an exclusive reference point // to all time induced state changes in this contract int256 internal WHITELIST_START; //////////////////////// // Modifiers //////////////////////// // @dev This modifier needs to be applied to all external non-constant // functions. // @dev This modifier goes _before_ other state modifiers like `onlyState`. modifier withTimedTransitions() { handleTimedTransitions(); _; } //////////////////////// // Constructor //////////////////////// function TimedStateMachine(int256 whitelistStart) internal { WHITELIST_START = whitelistStart; } //////////////////////// // Public functions //////////////////////// // @notice This function is public so that it can be called independently. function handleTimedTransitions() public { int256 t = int256(block.timestamp); // Time induced state transitions. // @dev Don't use `else if` and keep sorted by time and call `state()` // or else multiple transitions won't cascade properly. if (state() == State.Before && t >= startOf(State.Whitelist)) { transitionTo(State.Whitelist); } if (state() == State.Whitelist && t >= startOf(State.Public)) { transitionTo(State.Public); } if (state() == State.Public && t >= startOf(State.Finished)) { transitionTo(State.Finished); } } function startOf(State state) public constant returns (int256) { if (state == State.Before) { return 0; } if (state == State.Whitelist) { return WHITELIST_START; } if (state == State.Public) { return WHITELIST_START + WHITELIST_DURATION; } if (state == State.Finished) { return WHITELIST_START + WHITELIST_DURATION + PUBLIC_DURATION; } } } /// @title processes capital commitments into Neufund ecosystem contract Commitment is AccessControlled, Agreement, TimedStateMachine, Reclaimable, Math { //////////////////////// // Types //////////////////////// // The two tokens accepted in a pre-allocated ticket. enum Token { None, Ether, Euro } // Pre-allocated tickets with a pre-allocated neumark reward. struct WhitelistTicket { // The currency the investor wants and is allowed to committed. Token token; // The amount the investor committed. The investor can invest more or // less than this amount. In units of least precision of the token. uint256 amountEurUlps; // The amount of Neumark reward for this commitment (computed by // contract). Investor can still invest more, but that would be at // spot price. uint256 rewardNmk; } //////////////////////// // Constants //////////////////////// // share of Neumark reward platform operator gets // actually this is a divisor that splits Neumark reward in two parts // the results of division belongs to platform operator, the remaining reward part belongs to investor uint256 private constant PLATFORM_NEUMARK_SHARE = 2; // 50:50 division //////////////////////// // Immutable state //////////////////////// // wallet that keeps Platform Operator share of neumarks address private PLATFORM_WALLET; Neumark private NEUMARK; EtherToken private ETHER_TOKEN; EuroToken private EURO_TOKEN; LockedAccount private ETHER_LOCK; LockedAccount private EURO_LOCK; // maximum amount of EuroToken that can be committed to generate Neumark reward // indirectly this is cap for Neumark amount generated as it is checked against NEUMARK.totalEuroUlps() uint256 private CAP_EUR_ULPS; // minimum amount of EuroToken that may be committed uint256 private MIN_TICKET_EUR_ULPS; // fixed ETH/EUR price during the commitment, used to convert ETH into EUR, see convertToEur uint256 private ETH_EUR_FRACTION; //////////////////////// // Mutable state //////////////////////// // Mapping of investor to pre-allocated tickets. mapping (address => WhitelistTicket) private _whitelist; // List of pre-allocated ticket investors. // NOTE: The order of of the investors matters when computing the reward. address[] private _whitelistInvestors; // amount of Neumarks reserved for Ether whitelist investors uint256 private _whitelistEtherNmk = 0; // amount of Neumarks reserved for Euro whitelist investors uint256 private _whitelistEuroNmk = 0; //////////////////////// // Events //////////////////////// /// on every commitment transaction /// `investor` committed `amount` in `paymentToken` currency which was /// converted to `eurEquivalent` that generates `grantedAmount` of /// `ofToken`. event LogFundsCommitted( address indexed investor, address indexed paymentToken, uint256 amount, uint256 eurEquivalent, uint256 grantedAmount, address ofToken ); //////////////////////// // Constructor //////////////////////// /// @param accessPolicy access policy instance controlling access to admin public functions /// @param forkArbiter indicates supported fork /// @param startDate timestamp of Whitelist state beginning, see TimedStateMachine constructor /// @param platformWallet address of wallet storing platform operator's Neumarks /// @param neumark Neumark token contract /// @param etherToken ether-encapsulating token contract /// @param euroToken euro pegged stable coin /// @param etherLock manages locking mechanism for ether investors /// @param euroLock manages locking mechanism for euro token investors /// @param capEurUlps maxium amount of euro tokens committed /// @param minTicketEurUlps minimum ticket size /// @param ethEurFraction Ether to Euro rate, fixed during commitment function Commitment( IAccessPolicy accessPolicy, IEthereumForkArbiter forkArbiter, int256 startDate, address platformWallet, Neumark neumark, EtherToken etherToken, EuroToken euroToken, LockedAccount etherLock, LockedAccount euroLock, uint256 capEurUlps, uint256 minTicketEurUlps, uint256 ethEurFraction ) AccessControlled(accessPolicy) Agreement(accessPolicy, forkArbiter) TimedStateMachine(startDate) public { require(platformWallet != 0x0); require(address(neumark) != 0x0); require(address(etherToken) != 0x0); require(address(euroToken) != 0x0); require(address(etherLock) != 0x0); require(etherLock.assetToken() == etherToken); require(address(euroLock) != 0x0); require(euroLock.assetToken() == euroToken); // Euro is represented internally with 18 decimals require(capEurUlps >= 10**18*10**6); // 1 M€ require(capEurUlps <= 10**18*10**9); // 1 G€ require(minTicketEurUlps >= 10**18*10**2); // 100 € require(minTicketEurUlps <= 10**18*10**5); // 100 k€ require(ethEurFraction >= 10**18*10**2); // 100 € / ETH require(ethEurFraction <= 10**18*10**4); // 10 k€ / ETH PLATFORM_WALLET = platformWallet; NEUMARK = neumark; ETHER_TOKEN = etherToken; EURO_TOKEN = euroToken; ETHER_LOCK = etherLock; EURO_LOCK = euroLock; CAP_EUR_ULPS = capEurUlps; MIN_TICKET_EUR_ULPS = minTicketEurUlps; ETH_EUR_FRACTION = ethEurFraction; } //////////////////////// // External functions //////////////////////// function addWhitelisted( address[] investors, Token[] tokens, uint256[] amounts ) external withTimedTransitions() onlyState(State.Before) only(ROLE_WHITELIST_ADMIN) { require(investors.length == tokens.length); require(investors.length == amounts.length); for (uint256 i = 0; i < investors.length; ++i) { addWhitelistInvestorPrivate(investors[i], tokens[i], amounts[i]); } // We don't go over the cap require(NEUMARK.totalEuroUlps() <= CAP_EUR_ULPS); } /// @notice used by WHITELIST_ADMIN to kill commitment process before it starts /// @dev by selfdestruct we make all LockContracts controlled by this contract dysfunctional function abort() external withTimedTransitions() onlyState(State.Before) only(ROLE_WHITELIST_ADMIN) { // Return all Neumarks that may have been reserved. NEUMARK.burn(NEUMARK.balanceOf(this)); // At this point we can kill the contract, it can not have aquired any // other value. selfdestruct(msg.sender); } function commit() external payable withTimedTransitions() onlyStates(State.Whitelist, State.Public) acceptAgreement(msg.sender) // agreement accepted by act of reserving funds in this function { // Take with EtherToken allowance (if any) uint256 allowedAmount = ETHER_TOKEN.allowance(msg.sender, this); uint256 committedAmount = add(allowedAmount, msg.value); uint256 committedEurUlps = convertToEur(committedAmount); // check against minimum ticket before proceeding require(committedEurUlps >= MIN_TICKET_EUR_ULPS); if (allowedAmount > 0) { assert(ETHER_TOKEN.transferFrom(msg.sender, this, allowedAmount)); } if (msg.value > 0) { ETHER_TOKEN.deposit.value(msg.value)(); } // calculate Neumark reward and update Whitelist ticket uint256 investorNmk = getInvestorNeumarkReward(committedEurUlps, Token.Ether); // Lock EtherToken ETHER_TOKEN.approve(ETHER_LOCK, committedAmount); ETHER_LOCK.lock(msg.sender, committedAmount, investorNmk); // Log successful commitment LogFundsCommitted( msg.sender, ETHER_TOKEN, committedAmount, committedEurUlps, investorNmk, NEUMARK ); } function commitEuro() external withTimedTransitions() onlyStates(State.Whitelist, State.Public) acceptAgreement(msg.sender) // agreement accepted by act of reserving funds in this function { // receive Euro tokens uint256 committedEurUlps = EURO_TOKEN.allowance(msg.sender, this); // check against minimum ticket before proceeding require(committedEurUlps >= MIN_TICKET_EUR_ULPS); assert(EURO_TOKEN.transferFrom(msg.sender, this, committedEurUlps)); // calculate Neumark reward and update Whitelist ticket uint256 investorNmk = getInvestorNeumarkReward(committedEurUlps, Token.Euro); // Lock EuroToken EURO_TOKEN.approve(EURO_LOCK, committedEurUlps); EURO_LOCK.lock(msg.sender, committedEurUlps, investorNmk); // Log successful commitment LogFundsCommitted( msg.sender, EURO_TOKEN, committedEurUlps, committedEurUlps, investorNmk, NEUMARK ); } function estimateNeumarkReward(uint256 amount) external constant returns (uint256) { uint256 amountEurUlps = convertToEur(amount); uint256 rewardNmk = NEUMARK.incremental(amountEurUlps); var (, investorNmk) = calculateNeumarkDistribtion(rewardNmk); return investorNmk; } //////////////////////// // Public functions //////////////////////// /// converts `amount` in wei into EUR with 18 decimals required by Curve /// Neufund public commitment uses fixed EUR rate during commitment to level playing field and /// prevent strategic behavior around ETH/EUR volatility. equity TOs will use oracles as they need spot prices /// /// @notice Considering the max possible ETH_EUR_FRACTION value (10**18*10**4 == ~2**73), the max /// amount of ETH (not wei) that is safe to be passed as the argument /// is ~10**(54 - 18) (~2**123). function convertToEur(uint256 amount) public constant returns (uint256) { require(amount < 2**123); return decimalFraction(amount, ETH_EUR_FRACTION); } function platformWalletAddress() public constant returns (address) { return PLATFORM_WALLET; } function neumark() public constant returns (Neumark) { return NEUMARK; } function etherLock() public constant returns (LockedAccount) { return ETHER_LOCK; } function euroLock() public constant returns (LockedAccount) { return EURO_LOCK; } function maxCapEur() public constant returns (uint256) { return CAP_EUR_ULPS; } function minTicketEur() public constant returns (uint256) { return MIN_TICKET_EUR_ULPS; } function ethEurFraction() public constant returns (uint256) { return ETH_EUR_FRACTION; } function platformOperatorNeumarkRewardShare() public constant returns (uint256) { return PLATFORM_NEUMARK_SHARE; } // used to enumerate investors in whitelist function whitelistInvestor(uint256 atWhitelistPosition) public constant returns (address) { return _whitelistInvestors[atWhitelistPosition]; } // ticket information for particular investors function whitelistTicket(address investor) public constant returns (Token token, uint256 ticketEurUlps, uint256 /*investorNmk*/) { WhitelistTicket storage ticket = _whitelist[investor]; // could also use ( , investorNmk) but parser has problems in solium TODO fix solium var (, investorNmk) = calculateNeumarkDistribtion(ticket.rewardNmk); return (ticket.token, ticket.amountEurUlps, investorNmk); } //////////////////////// // Internal functions //////////////////////// // // Implements StateMachine // function mAfterTransition(State /* oldState */, State newState) internal { // nothing to do after entering Whitelist if (newState == State.Whitelist) { return; } uint256 nmkToBurn = 0; if (newState == State.Public) { // mark unfufilled Ether reservations for burning nmkToBurn = _whitelistEtherNmk; _whitelistEtherNmk = 0; } if (newState == State.Finished) { // mark unfufilled Euro reservations for burning nmkToBurn = _whitelistEuroNmk; _whitelistEuroNmk = 0; // enable escape hatch and end locking funds phase ETHER_LOCK.controllerSucceeded(); EURO_LOCK.controllerSucceeded(); // enable Neumark transfers NEUMARK.enableTransfer(true); } // burn Neumarks after state change to prevent theoretical re-entry NEUMARK.burn(nmkToBurn); } //////////////////////// // Private functions //////////////////////// function addWhitelistInvestorPrivate( address investor, Token token, uint256 amount ) private { // Validate require(investor != 0x0); require(_whitelist[investor].token == Token.None); bool isEuro = token == Token.Euro; bool isEther = token == Token.Ether; require(isEuro || isEther); // Note: amount can be zero, indicating no pre-allocated NMK, // but still the ability to commit before the public. uint256 amountEurUlps = isEuro ? amount : convertToEur(amount); require(amount == 0 || amountEurUlps >= MIN_TICKET_EUR_ULPS); // Register the investor on the list of investors to keep them // in order. _whitelistInvestors.push(investor); // Create a ticket without NEUMARK reward information and add it to // the pre-allocated tickets. _whitelist[investor] = WhitelistTicket({ token: token, amountEurUlps: amountEurUlps, rewardNmk: 0 }); if (amountEurUlps > 0) { // Allocate Neumarks (will be issued to `this`). // Because `_whitelist[investor].token == Token.None` does not not hold // any more, this function is protected against reentrancy attack // conducted from NEUMARK.issueForEuro(). uint256 rewardNmk = NEUMARK.issueForEuro(amountEurUlps); // Record the number of Neumarks for investor. _whitelist[investor].rewardNmk = rewardNmk; // Add to totals if (isEuro) { _whitelistEuroNmk = add(_whitelistEuroNmk, rewardNmk); } else { _whitelistEtherNmk = add(_whitelistEtherNmk, rewardNmk); } } } /// @dev Token.None should not be passed to 'tokenType' parameter function getInvestorNeumarkReward(uint256 committedEurUlps, Token tokenType) private returns (uint256) { // We don't go over the cap require(add(NEUMARK.totalEuroUlps(), committedEurUlps) <= CAP_EUR_ULPS); // Compute committed funds uint256 remainingEurUlps = committedEurUlps; uint256 rewardNmk = 0; uint256 ticketNmk = 0; // Whitelist part WhitelistTicket storage ticket = _whitelist[msg.sender]; bool whitelisted = ticket.token == tokenType; require(whitelisted || state() == State.Public); bool whitelistActiveForToken = tokenType == Token.Euro || state() == State.Whitelist; if (whitelisted && whitelistActiveForToken && ticket.amountEurUlps > 0 ) { uint256 ticketEurUlps = min(remainingEurUlps, ticket.amountEurUlps); ticketNmk = proportion( ticket.rewardNmk, ticketEurUlps, ticket.amountEurUlps ); // update investor ticket ticket.amountEurUlps = sub(ticket.amountEurUlps, ticketEurUlps); ticket.rewardNmk = sub(ticket.rewardNmk, ticketNmk); // mark ticketEurUlps as spent remainingEurUlps = sub(remainingEurUlps, ticketEurUlps); // set neumark reward rewardNmk = ticketNmk; // decrease reserved Neumark pool accordingly if (tokenType == Token.Euro) { _whitelistEuroNmk = sub(_whitelistEuroNmk, ticketNmk); } else { _whitelistEtherNmk = sub(_whitelistEtherNmk, ticketNmk); } } // issue Neumarks against curve for amount left after pre-defined ticket was realized if (remainingEurUlps > 0) { rewardNmk = add(rewardNmk, NEUMARK.issueForEuro(remainingEurUlps)); remainingEurUlps = 0; // not used later but we should keep variable semantics } // Split the Neumarks var (platformNmk, investorNmk) = calculateNeumarkDistribtion(rewardNmk); // Issue Neumarks and distribute NEUMARK.distribute(msg.sender, investorNmk); NEUMARK.distribute(PLATFORM_WALLET, platformNmk); return investorNmk; } // calculates investor's and platform operator's neumarks from total reward function calculateNeumarkDistribtion(uint256 rewardNmk) private returns (uint256 platformNmk, uint256 investorNmk) { // round down - platform may get 1 wei less than investor platformNmk = rewardNmk / PLATFORM_NEUMARK_SHARE; // rewardNmk > platformNmk always return (platformNmk, rewardNmk - platformNmk); } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"constant":true,"inputs":[{"name":"investor","type":"address"}],"name":"whitelistTicket","outputs":[{"name":"token","type":"uint8"},{"name":"ticketEurUlps","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"minTicketEur","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"platformOperatorNeumarkRewardShare","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"abort","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"handleTimedTransitions","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"commit","outputs":[],"payable":true,"type":"function"},{"constant":true,"inputs":[{"name":"atWhitelistPosition","type":"uint256"}],"name":"whitelistInvestor","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"newPolicy","type":"address"},{"name":"newAccessController","type":"address"}],"name":"setAccessPolicy","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"amount","type":"uint256"}],"name":"estimateNeumarkReward","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"ethEurFraction","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"signatory","type":"address"}],"name":"agreementSignedAtBlock","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"amount","type":"uint256"}],"name":"convertToEur","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"euroLock","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"amendmentIndex","type":"uint256"}],"name":"pastAgreement","outputs":[{"name":"platformOperatorRepresentative","type":"address"},{"name":"signedBlockTimestamp","type":"uint256"},{"name":"agreementUri","type":"string"},{"name":"index","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"investors","type":"address[]"},{"name":"tokens","type":"uint8[]"},{"name":"amounts","type":"uint256[]"}],"name":"addWhitelisted","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"platformWalletAddress","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"state","type":"uint8"}],"name":"startOf","outputs":[{"name":"","type":"int256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"state","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"maxCapEur","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"currentAgreement","outputs":[{"name":"platformOperatorRepresentative","type":"address"},{"name":"signedBlockTimestamp","type":"uint256"},{"name":"agreementUri","type":"string"},{"name":"index","type":"uint256"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"etherLock","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[],"name":"commitEuro","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"neumark","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"ethereumForkArbiter","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"agreementUri","type":"string"}],"name":"amendAgreement","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"accessPolicy","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"}],"name":"reclaim","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"accessPolicy","type":"address"},{"name":"forkArbiter","type":"address"},{"name":"startDate","type":"int256"},{"name":"platformWallet","type":"address"},{"name":"neumark","type":"address"},{"name":"etherToken","type":"address"},{"name":"euroToken","type":"address"},{"name":"etherLock","type":"address"},{"name":"euroLock","type":"address"},{"name":"capEurUlps","type":"uint256"},{"name":"minTicketEurUlps","type":"uint256"},{"name":"ethEurFraction","type":"uint256"}],"payable":false,"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"investor","type":"address"},{"indexed":true,"name":"paymentToken","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"eurEquivalent","type":"uint256"},{"indexed":false,"name":"grantedAmount","type":"uint256"},{"indexed":false,"name":"ofToken","type":"address"}],"name":"LogFundsCommitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldState","type":"uint8"},{"indexed":false,"name":"newState","type":"uint8"}],"name":"LogStateTransition","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"accepter","type":"address"}],"name":"LogAgreementAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"platformOperatorRepresentative","type":"address"},{"indexed":false,"name":"agreementUri","type":"string"}],"name":"LogAgreementAmended","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"controller","type":"address"},{"indexed":false,"name":"oldPolicy","type":"address"},{"indexed":false,"name":"newPolicy","type":"address"}],"name":"LogAccessPolicyChanged","type":"event"}]
Contract Creation Code
6060604052600480546000919060ff19166001835b02179055506000601155600060125534156200002f57600080fd5b6040516101808062002d6f8339810160405280805191906020018051919060200180519190602001805191906020018051919060200180519190602001805191906020018051919060200180519190602001805191906020018051919060200180519150505b895b5b8c8c5b8e5b600160a060020a0381161515620000b357600080fd5b60008054600160a060020a031916600160a060020a0383161790555b50600160a060020a0381161515620000e657600080fd5b60018054600160a060020a031916600160a060020a0383161790555b50505b60058190555b50600160a060020a03891615156200012257600080fd5b600160a060020a03881615156200013857600080fd5b600160a060020a03871615156200014e57600080fd5b600160a060020a03861615156200016457600080fd5b600160a060020a03851615156200017a57600080fd5b86600160a060020a031685600160a060020a0316631083f7616000604051602001526040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b1515620001e457600080fd5b6102c65a03f11515620001f657600080fd5b50505060405180519050600160a060020a03161415156200021657600080fd5b600160a060020a03841615156200022c57600080fd5b85600160a060020a031684600160a060020a0316631083f7616000604051602001526040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15156200029657600080fd5b6102c65a03f11515620002a857600080fd5b50505060405180519050600160a060020a0316141515620002c857600080fd5b69d3c21bcecceda1000000831015620002e057600080fd5b6b033b2e3c9fd0803ce8000000831115620002fa57600080fd5b68056bc75e2d631000008210156200031157600080fd5b69152d02c7e14af68000008211156200032957600080fd5b68056bc75e2d631000008110156200034057600080fd5b69021e19e0c9bab24000008111156200035857600080fd5b60068054600160a060020a0319908116600160a060020a038c8116919091179092556007805482168b84161790556008805482168a8416179055600980548216898416179055600a80548216888416179055600b8054909116918616919091179055600c839055600d829055600e8190555b5050505050505050505050505b61298880620003e76000396000f300606060405236156101435763ffffffff60e060020a6000350416630143279c81146101485780632b2fbd681461019957806332139a0e146101be57806335a063b4146101e35780633767aec6146101f85780633c7a3aff1461020d57806356fbdca31461021757806357875631146102495780635861356814610270578063664c7bf7146102985780636fa00f07146102bd57806379e1fa2e146102ee57806387055008146103165780638fb29d6c146103455780639ad0a5ac146103f35780639f2a63741461042b578063a0f8be8c1461045a578063c19d93fb14610485578063c3dd2395146104bc578063c90f793e146104e1578063e0f20a651461058c578063e4002b30146105bb578063e97b97fe146105d0578063ea490b84146105ff578063eb4e64d61461062e578063f5d60a5114610681578063fc772c8b146106b0575b600080fd5b341561015357600080fd5b610167600160a060020a03600435166106d1565b6040518084600281111561017757fe5b60ff168152602001838152602001828152602001935050505060405180910390f35b34156101a457600080fd5b6101ac61071d565b60405190815260200160405180910390f35b34156101c957600080fd5b6101ac610724565b60405190815260200160405180910390f35b34156101ee57600080fd5b6101f661072a565b005b341561020357600080fd5b6101f66108fe565b005b6101f66109b7565b005b341561022257600080fd5b61022d600435610da3565b604051600160a060020a03909116815260200160405180910390f35b341561025457600080fd5b6101f6600160a060020a0360043581169060243516610ddc565b005b341561027b57600080fd5b6101ac600435611004565b60405190815260200160405180910390f35b34156102a357600080fd5b6101ac61109c565b60405190815260200160405180910390f35b34156102c857600080fd5b6101ac600160a060020a03600435166110a3565b60405190815260200160405180910390f35b34156102f957600080fd5b6101ac6004356110c2565b60405190815260200160405180910390f35b341561032157600080fd5b61022d6110f4565b604051600160a060020a03909116815260200160405180910390f35b341561035057600080fd5b61035b600435611104565b604051600160a060020a0385168152602081018490526060810182905260806040820181815290820184818151815260200191508051906020019080838360005b838110156103b55780820151818401525b60200161039c565b50505050905090810190601f1680156103e25780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b34156103fe57600080fd5b6101f66024600480358281019290820135918135808301929082013591604435918201910135611204565b005b341561043657600080fd5b61022d611410565b604051600160a060020a03909116815260200160405180910390f35b341561046557600080fd5b6101ac60ff60043516611420565b60405190815260200160405180910390f35b341561049057600080fd5b6104986114a8565b604051808260038111156104a857fe5b60ff16815260200191505060405180910390f35b34156104c757600080fd5b6101ac6114b2565b60405190815260200160405180910390f35b34156104ec57600080fd5b61035b6114b9565b604051600160a060020a0385168152602081018490526060810182905260806040820181815290820184818151815260200191508051906020019080838360005b838110156103b55780820151818401525b60200161039c565b50505050905090810190601f1680156103e25780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b341561059757600080fd5b61022d6115d2565b604051600160a060020a03909116815260200160405180910390f35b34156105c657600080fd5b6101f66115e2565b005b34156105db57600080fd5b61022d61194a565b604051600160a060020a03909116815260200160405180910390f35b341561060a57600080fd5b61022d61195a565b604051600160a060020a03909116815260200160405180910390f35b341561063957600080fd5b6101f660046024813581810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965061196a95505050505050565b005b341561068c57600080fd5b61022d611b9a565b604051600160a060020a03909116815260200160405180910390f35b34156106bb57600080fd5b6101f6600160a060020a0360043516611baa565b005b600160a060020a0381166000908152600f6020526040812060028101548291829182906106fd90611dcf565b8354600185015460ff909116975095509350839150505b50509193909250565b600d545b90565b60025b90565b6107326108fe565b6000805b60045460ff16600381111561074757fe5b1461075157600080fd5b600080547faef456e7c864418e1d2a40d996ca4febf3a7e317fe3af5a7ea4dda59033bbe5c91600160a060020a0390911690639085b77f90339084903090600160e060020a0319813516906040516020015260405160e060020a63ffffffff8716028152600160a060020a039485166004820152602481019390935292166044820152600160e060020a03199091166064820152608401602060405180830381600087803b151561080157600080fd5b6102c65a03f1151561081257600080fd5b50505060405180519050151561082757600080fd5b600754600160a060020a03166342966c68816370a082313060006040516020015260405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401602060405180830381600087803b151561088657600080fd5b6102c65a03f1151561089757600080fd5b5050506040518051905060405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b15156108d757600080fd5b6102c65a03f115156108e857600080fd5b50505033600160a060020a0316ff5b5b505b505b565b4260005b61090a6114a8565b600381111561091557fe5b14801561092b57506109276001611420565b8112155b1561093a5761093a6001611de3565b5b60015b6109466114a8565b600381111561095157fe5b14801561096757506109636002611420565b8112155b15610976576109766002611de3565b5b60025b6109826114a8565b600381111561098d57fe5b1480156109a3575061099f6003611420565b8112155b156108fa576108fa6003611de3565b5b5b50565b6000806000806109c56108fe565b60016002815b60045460ff1660038111156109dc57fe5b1480610a0257508060038111156109ef57fe5b60045460ff166003811115610a0057fe5b145b1515610a0d57600080fd5b33600160a060020a0381166000908152600360205260409020541515610a865760025460009011610a3d57600080fd5b600160a060020a038116600081815260036020526040908190204390557f8c41d101e4d957423a65fda82dcc88bc6b3e756166d2331f663c10166658ebb8905160405180910390a25b600854600160a060020a031663dd62ed3e333060006040516020015260405160e060020a63ffffffff8516028152600160a060020a03928316600482015291166024820152604401602060405180830381600087803b1515610ae757600080fd5b6102c65a03f11515610af857600080fd5b505050604051805190509650610b0e8734611ead565b9550610b19866110c2565b600d54909550851015610b2b57600080fd5b6000871115610bc357600854600160a060020a03166323b872dd33308a60006040516020015260405160e060020a63ffffffff8616028152600160a060020a0393841660048201529190921660248201526044810191909152606401602060405180830381600087803b1515610ba057600080fd5b6102c65a03f11515610bb157600080fd5b505050604051805190501515610bc357fe5b5b6000341115610c2257600854600160a060020a031663d0e30db0346040518263ffffffff1660e060020a0281526004016000604051808303818588803b1515610c0c57600080fd5b6125ee5a03f11515610c1d57600080fd5b505050505b610c2d856001611ec7565b600854600a54919550600160a060020a039081169163095ea7b391168860006040516020015260405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401602060405180830381600087803b1515610c9657600080fd5b6102c65a03f11515610ca757600080fd5b50505060405180515050600a54600160a060020a031663e2ab691d33888760405160e060020a63ffffffff8616028152600160a060020a03909316600484015260248301919091526044820152606401600060405180830381600087803b1515610d1057600080fd5b6102c65a03f11515610d2157600080fd5b5050600854600754600160a060020a039182169250338216917f3b52147fc495650947cc4178511501c7c8900970ae9ce3fa4c096f7ede84f7d5918a918a918a91166040519384526020840192909252604080840191909152600160a060020a0390911660608301526080909101905180910390a35b5b505b50505b50505050565b6000601082815481101515610db457fe5b906000526020600020900160005b9054906101000a9004600160a060020a031690505b919050565b600080547fac42f8beb17975ed062dcb80c63e6d203ef1c2c335ced149dc5664cc671cb7da90600160a060020a0316639085b77f338330600160e060020a0319873516876040516020015260405160e060020a63ffffffff8716028152600160a060020a039485166004820152602481019390935292166044820152600160e060020a03199091166064820152608401602060405180830381600087803b1515610e8557600080fd5b6102c65a03f11515610e9657600080fd5b505050604051805190501515610eab57600080fd5b83600160a060020a0316639085b77f847fac42f8beb17975ed062dcb80c63e6d203ef1c2c335ced149dc5664cc671cb7da60010230600035600160e060020a03191660006040516020015260405160e060020a63ffffffff8716028152600160a060020a039485166004820152602481019390935292166044820152600160e060020a03199091166064820152608401602060405180830381600087803b1515610f5457600080fd5b6102c65a03f11515610f6557600080fd5b505050604051805190501515610f7a57600080fd5b60008054600160a060020a0386811673ffffffffffffffffffffffffffffffffffffffff198316179092551691507f7d475c32583df95fccc34a6e12df24c1fc9943092cc129b6512013aecba0f136338386604051600160a060020a03938416815291831660208301529091166040808301919091526060909101905180910390a15b5b50505050565b600080600080611013856110c2565b600754909350600160a060020a031663a8c17ec28460006040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561106757600080fd5b6102c65a03f1151561107857600080fd5b50505060405180519050915061108d82611dcf565b9150508093505b505050919050565b600e545b90565b600160a060020a0381166000908152600360205260409020545b919050565b60006f0800000000000000000000000000000082106110e057600080fd5b6110ec82600e54612238565b90505b919050565b600b54600160a060020a03165b90565b60008061110f61277f565b60008060028681548110151561112157fe5b906000526020600020906003020160005b5090508060000160009054906101000a9004600160a060020a031681600101548260020188818054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156111ec5780601f106111c1576101008083540402835291602001916111ec565b820191906000526020600020905b8154815290600101906020018083116111cf57829003601f168201915b5050505050915094509450945094505b509193509193565b600061120e6108fe565b6000805b60045460ff16600381111561122357fe5b1461122d57600080fd5b600080547faef456e7c864418e1d2a40d996ca4febf3a7e317fe3af5a7ea4dda59033bbe5c91600160a060020a0390911690639085b77f90339084903090600160e060020a0319813516906040516020015260405160e060020a63ffffffff8716028152600160a060020a039485166004820152602481019390935292166044820152600160e060020a03199091166064820152608401602060405180830381600087803b15156112dd57600080fd5b6102c65a03f115156112ee57600080fd5b50505060405180519050151561130357600080fd5b87861461130f57600080fd5b87841461131b57600080fd5b600092505b8783101561138e5761138289898581811061133757fe5b90506020020135600160a060020a0316888886818110151561135557fe5b90506020020135600281111561136a57600080fd5b87878781811061137657fe5b90506020020135612256565b5b826001019250611320565b600c54600754600160a060020a0316631a3d069b6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156113d957600080fd5b6102c65a03f115156113ea57600080fd5b505050604051805190501115151561140157600080fd5b5b5b505b505b50505050505050565b600654600160a060020a03165b90565b6000805b82600381111561143057fe5b141561143e57506000610dd7565b60015b82600381111561144d57fe5b141561145c5750600554610dd7565b60025b82600381111561146b57fe5b141561147f57506005546206978001610dd7565b60035b82600381111561148e57fe5b1415610dd75750600554622e248001610dd7565b5b919050565b60045460ff165b90565b600c545b90565b6000806114c461277f565b600254600090819081908190116114da57600080fd5b6002805460001981019350839081106114ef57fe5b906000526020600020906003020160005b5090508060000160009054906101000a9004600160a060020a031681600101548260020184818054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156115ba5780601f1061158f576101008083540402835291602001916115ba565b820191906000526020600020905b81548152906001019060200180831161159d57829003601f168201915b5050505050915095509550955095505b505090919293565b600a54600160a060020a03165b90565b6000806115ed6108fe565b60016002815b60045460ff16600381111561160457fe5b148061162a575080600381111561161757fe5b60045460ff16600381111561162857fe5b145b151561163557600080fd5b33600160a060020a03811660009081526003602052604090205415156116ae576002546000901161166557600080fd5b600160a060020a038116600081815260036020526040908190204390557f8c41d101e4d957423a65fda82dcc88bc6b3e756166d2331f663c10166658ebb8905160405180910390a25b600954600160a060020a031663dd62ed3e333060006040516020015260405160e060020a63ffffffff8516028152600160a060020a03928316600482015291166024820152604401602060405180830381600087803b151561170f57600080fd5b6102c65a03f1151561172057600080fd5b5050506040518051600d54909650861015905061173c57600080fd5b600954600160a060020a03166323b872dd33308860006040516020015260405160e060020a63ffffffff8616028152600160a060020a0393841660048201529190921660248201526044810191909152606401602060405180830381600087803b15156117a857600080fd5b6102c65a03f115156117b957600080fd5b5050506040518051905015156117cb57fe5b6117d6856002611ec7565b600954600b54919550600160a060020a039081169163095ea7b391168760006040516020015260405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401602060405180830381600087803b151561183f57600080fd5b6102c65a03f1151561185057600080fd5b50505060405180515050600b54600160a060020a031663e2ab691d33878760405160e060020a63ffffffff8616028152600160a060020a03909316600484015260248301919091526044820152606401600060405180830381600087803b15156118b957600080fd5b6102c65a03f115156118ca57600080fd5b5050600954600754600160a060020a039182169250338216917f3b52147fc495650947cc4178511501c7c8900970ae9ce3fa4c096f7ede84f7d591899182918a91166040519384526020840192909252604080840191909152600160a060020a0390911660608301526080909101905180910390a35b5b505b50505b5050565b600754600160a060020a03165b90565b600154600160a060020a03165b90565b611972612791565b600080547fb2b321377653f655206f71514ff9f150d0822d062a5abcf220d549e1da7999f091600160a060020a0390911690639085b77f90339084903090600160e060020a0319813516906040516020015260405160e060020a63ffffffff8716028152600160a060020a039485166004820152602481019390935292166044820152600160e060020a03199091166064820152608401602060405180830381600087803b1515611a2257600080fd5b6102c65a03f11515611a3357600080fd5b505050604051805190501515611a4857600080fd5b60606040519081016040528033600160a060020a0316815260200142815260200184815250915060028054806001018281611a8391906127b7565b916000526020600020906003020160005b5083908151815473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a039190911617815560208201518160010155604082015181600201908051611ae59291602001906127e9565b505050507fe9835ee18f0f0b190604da3474d67a8f29aba2c92e90eee2bdaeca67d40d5a6b3384604051600160a060020a038316815260406020820181815290820183818151815260200191508051906020019080838360005b83811015611b585780820151818401525b602001611b3f565b50505050905090810190601f168015611b855780820380516001836020036101000a031916815260200191505b50935050505060405180910390a15b5b505050565b600054600160a060020a03165b90565b6000805481907f0542bbd0c672578966dcc525b30aa16723bb042675554ac5b0362f86b6e97dc590600160a060020a0316639085b77f338330600160e060020a0319873516876040516020015260405160e060020a63ffffffff8716028152600160a060020a039485166004820152602481019390935292166044820152600160e060020a03199091166064820152608401602060405180830381600087803b1515611c5557600080fd5b6102c65a03f11515611c6657600080fd5b505050604051805190501515611c7b57600080fd5b339250600160a060020a0384161515611cd05782600160a060020a03166108fc30600160a060020a0316319081150290604051600060405180830381858888f193505050501515611ccb57600080fd5b610d9d565b83600160a060020a03166370a082313060006040516020015260405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401602060405180830381600087803b1515611d2757600080fd5b6102c65a03f11515611d3857600080fd5b5050506040518051925050600160a060020a03841663a9059cbb848460006040516020015260405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401602060405180830381600087803b1515611da057600080fd5b6102c65a03f11515611db157600080fd5b505050604051805190501515610d9d57600080fd5b5b5b5b50505050565b6000806002835b049150508082035b915091565b60045460ff16611df38183612486565b1515611dfe57600080fd5b6004805483919060ff19166001836003811115611e1757fe5b02179055507f06772bbd55fda8cf7fdb2fe4ce48d2fb499ee1bb6a9af59c5f095344700931d0818360405180836003811115611e4f57fe5b60ff168152602001826003811115611e6357fe5b60ff1681526020019250505060405180910390a1611e818183612516565b816003811115611e8d57fe5b60045460ff166003811115611e9e57fe5b146108f757600080fd5b5b5050565b600082820183811015611ebc57fe5b8091505b5092915050565b600080600080600080600080600080600c54611f58600760009054906101000a9004600160a060020a0316600160a060020a0316631a3d069b6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515611f3757600080fd5b6102c65a03f11515611f4857600080fd5b505050604051805190508e611ead565b1115611f6357600080fd5b600160a060020a0333166000908152600f602052604081208d9a5090985088975095508a6002811115611f9257fe5b865460ff166002811115611fa257fe5b1494508480611fc4575060025b611fb76114a8565b6003811115611fc257fe5b145b1515611fcf57600080fd5b60025b8b6002811115611fde57fe5b1480611ffd575060015b611ff06114a8565b6003811115611ffb57fe5b145b93508480156120095750835b8015612019575060008660010154115b156120b45761202c8987600101546126dc565b925061204186600201548488600101546126f6565b9650612051866001015484612715565b600187015560028601546120659088612715565b60028701556120748984612715565b9850959650869560025b8b600281111561208a57fe5b14156120a45761209c60125488612715565b6012556120b4565b6120b060115488612715565b6011555b5b600089111561213c57600754612135908990600160a060020a0316639260faf88c60006040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561211557600080fd5b6102c65a03f1151561212657600080fd5b50505060405180519050611ead565b9750600098505b61214588611dcf565b6007549193509150600160a060020a031663fb932108338360405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b15156121a057600080fd5b6102c65a03f115156121b157600080fd5b5050600754600654600160a060020a03918216925063fb93210891168460405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b151561221157600080fd5b6102c65a03f1151561222257600080fd5b5050508099505b50505050505050505092915050565b600061224d8383670de0b6b3a76400006126f6565b90505b92915050565b6000808080600160a060020a038716151561227057600080fd5b60005b600160a060020a0388166000908152600f602052604090205460ff16600281111561229a57fe5b146122a457600080fd5b60025b8660028111156122b357fe5b14935060015b8660028111156122c557fe5b14925083806122d15750825b15156122dc57600080fd5b836122ef576122ea856110c2565b6122f1565b845b91508415806123025750600d548210155b151561230d57600080fd5b601080546001810161231f8382612868565b916000526020600020900160005b8154600160a060020a03808c166101009390930a928302920219161790555060606040519081016040528087600281111561236457fe5b8152602080820185905260006040928301819052600160a060020a038b168152600f9091522081518154829060ff191660018360028111156123a257fe5b0217905550602082015181600101556040820151600290910155506000821115610d9757600754600160a060020a0316639260faf88360006040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561241757600080fd5b6102c65a03f1151561242857600080fd5b5050506040518051600160a060020a0389166000908152600f60205260409020600201819055915050831561246b5761246360125482611ead565b601255610d97565b61247760115482611ead565b6011555b5b5b50505050505050565b6000805b83600381111561249657fe5b1480156124af575060015b8260038111156124ad57fe5b145b806124dd575060015b8360038111156124c457fe5b1480156124dd575060025b8260038111156124db57fe5b145b5b8061224d575060025b8360038111156124f357fe5b14801561224d575060035b82600381111561250a57fe5b145b5b90505b92915050565b600060015b82600381111561252757fe5b141561253257611b94565b50600060025b82600381111561254457fe5b141561255557506011805460009091555b60035b82600381111561256457fe5b141561267a5750601280546000909155600a54600160a060020a0316636cff82446040518163ffffffff1660e060020a028152600401600060405180830381600087803b15156125b357600080fd5b6102c65a03f115156125c457600080fd5b5050600b54600160a060020a03169050636cff82446040518163ffffffff1660e060020a028152600401600060405180830381600087803b151561260757600080fd5b6102c65a03f1151561261857600080fd5b5050600754600160a060020a0316905063ef7ac0e5600160405160e060020a63ffffffff84160281529015156004820152602401600060405180830381600087803b151561266557600080fd5b6102c65a03f1151561267657600080fd5b5050505b600754600160a060020a03166342966c688260405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b15156126c257600080fd5b6102c65a03f11515610d9a57600080fd5b5050505b505050565b60008183106126eb578161224d565b825b90505b92915050565b600061270b612705858561272c565b8361275b565b90505b9392505050565b60008282111561272157fe5b508082035b92915050565b6000828202831580612748575082848281151561274557fe5b04145b1515611ebc57fe5b8091505b5092915050565b60008161276c846002835b04611ead565b81151561277557fe5b0490505b92915050565b60206040519081016040526000815290565b606060405190810160409081526000808352602083015281016127b261277f565b905290565b815481835581811511611b9457600302816003028360005260206000209182019101611b9491906128a4565b5b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061282a57805160ff1916838001178555612857565b82800160010185558215612857579182015b8281111561285757825182559160200191906001019061283c565b5b506128649291506128f3565b5090565b815481835581811511611b9457600083815260209020611b949181019083016128f3565b5b505050565b60206040519081016040526000815290565b61072191905b8082111561286457805473ffffffffffffffffffffffffffffffffffffffff191681556000600182018190556128e36002830182612914565b506003016128aa565b5090565b90565b61072191905b8082111561286457600081556001016128f9565b5090565b90565b50805460018160011615610100020316600290046000825580601f1061293a57506108fa565b601f0160209004906000526020600020908101906108fa91906128f3565b5b505600a165627a7a72305820d8f8bdc3551e75848b45618527ebdc0e56dfcc13fe8788293d3b70d0ab088a010029000000000000000000000000ae38c27e646959735ec70d77ed4ecc03a3eff4900000000000000000000000004c688949578b4fd0cb8f8993a0ec608659e05cf1000000000000000000000000000000000000000000000000000000005a0829b0000000000000000000000000a826813d0eb5d629e959c02b8f7a3d0f53066ce4000000000000000000000000a823e6722006afe99e91c30ff5295052fe6b8e32000000000000000000000000b59a226a2b8a2f2b0512baa35cc348b6b213b6710000000000000000000000002682762d9830ace22218c7bd51d19e5757ec3f03000000000000000000000000b1e4675f0dbe360ba90447a7e58c62c762ad62d40000000000000000000000005cb5d4a198a253e21212c72c27b51558d19e3a57000000000000000000000000000000000000000000a56fa5b99019a5c800000000000000000000000000000000000000000000000000000fb88ef7839f48000000000000000000000000000000000000000000000000000fb88ef7839f480000
Deployed Bytecode
0x606060405236156101435763ffffffff60e060020a6000350416630143279c81146101485780632b2fbd681461019957806332139a0e146101be57806335a063b4146101e35780633767aec6146101f85780633c7a3aff1461020d57806356fbdca31461021757806357875631146102495780635861356814610270578063664c7bf7146102985780636fa00f07146102bd57806379e1fa2e146102ee57806387055008146103165780638fb29d6c146103455780639ad0a5ac146103f35780639f2a63741461042b578063a0f8be8c1461045a578063c19d93fb14610485578063c3dd2395146104bc578063c90f793e146104e1578063e0f20a651461058c578063e4002b30146105bb578063e97b97fe146105d0578063ea490b84146105ff578063eb4e64d61461062e578063f5d60a5114610681578063fc772c8b146106b0575b600080fd5b341561015357600080fd5b610167600160a060020a03600435166106d1565b6040518084600281111561017757fe5b60ff168152602001838152602001828152602001935050505060405180910390f35b34156101a457600080fd5b6101ac61071d565b60405190815260200160405180910390f35b34156101c957600080fd5b6101ac610724565b60405190815260200160405180910390f35b34156101ee57600080fd5b6101f661072a565b005b341561020357600080fd5b6101f66108fe565b005b6101f66109b7565b005b341561022257600080fd5b61022d600435610da3565b604051600160a060020a03909116815260200160405180910390f35b341561025457600080fd5b6101f6600160a060020a0360043581169060243516610ddc565b005b341561027b57600080fd5b6101ac600435611004565b60405190815260200160405180910390f35b34156102a357600080fd5b6101ac61109c565b60405190815260200160405180910390f35b34156102c857600080fd5b6101ac600160a060020a03600435166110a3565b60405190815260200160405180910390f35b34156102f957600080fd5b6101ac6004356110c2565b60405190815260200160405180910390f35b341561032157600080fd5b61022d6110f4565b604051600160a060020a03909116815260200160405180910390f35b341561035057600080fd5b61035b600435611104565b604051600160a060020a0385168152602081018490526060810182905260806040820181815290820184818151815260200191508051906020019080838360005b838110156103b55780820151818401525b60200161039c565b50505050905090810190601f1680156103e25780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b34156103fe57600080fd5b6101f66024600480358281019290820135918135808301929082013591604435918201910135611204565b005b341561043657600080fd5b61022d611410565b604051600160a060020a03909116815260200160405180910390f35b341561046557600080fd5b6101ac60ff60043516611420565b60405190815260200160405180910390f35b341561049057600080fd5b6104986114a8565b604051808260038111156104a857fe5b60ff16815260200191505060405180910390f35b34156104c757600080fd5b6101ac6114b2565b60405190815260200160405180910390f35b34156104ec57600080fd5b61035b6114b9565b604051600160a060020a0385168152602081018490526060810182905260806040820181815290820184818151815260200191508051906020019080838360005b838110156103b55780820151818401525b60200161039c565b50505050905090810190601f1680156103e25780820380516001836020036101000a031916815260200191505b509550505050505060405180910390f35b341561059757600080fd5b61022d6115d2565b604051600160a060020a03909116815260200160405180910390f35b34156105c657600080fd5b6101f66115e2565b005b34156105db57600080fd5b61022d61194a565b604051600160a060020a03909116815260200160405180910390f35b341561060a57600080fd5b61022d61195a565b604051600160a060020a03909116815260200160405180910390f35b341561063957600080fd5b6101f660046024813581810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284375094965061196a95505050505050565b005b341561068c57600080fd5b61022d611b9a565b604051600160a060020a03909116815260200160405180910390f35b34156106bb57600080fd5b6101f6600160a060020a0360043516611baa565b005b600160a060020a0381166000908152600f6020526040812060028101548291829182906106fd90611dcf565b8354600185015460ff909116975095509350839150505b50509193909250565b600d545b90565b60025b90565b6107326108fe565b6000805b60045460ff16600381111561074757fe5b1461075157600080fd5b600080547faef456e7c864418e1d2a40d996ca4febf3a7e317fe3af5a7ea4dda59033bbe5c91600160a060020a0390911690639085b77f90339084903090600160e060020a0319813516906040516020015260405160e060020a63ffffffff8716028152600160a060020a039485166004820152602481019390935292166044820152600160e060020a03199091166064820152608401602060405180830381600087803b151561080157600080fd5b6102c65a03f1151561081257600080fd5b50505060405180519050151561082757600080fd5b600754600160a060020a03166342966c68816370a082313060006040516020015260405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401602060405180830381600087803b151561088657600080fd5b6102c65a03f1151561089757600080fd5b5050506040518051905060405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b15156108d757600080fd5b6102c65a03f115156108e857600080fd5b50505033600160a060020a0316ff5b5b505b505b565b4260005b61090a6114a8565b600381111561091557fe5b14801561092b57506109276001611420565b8112155b1561093a5761093a6001611de3565b5b60015b6109466114a8565b600381111561095157fe5b14801561096757506109636002611420565b8112155b15610976576109766002611de3565b5b60025b6109826114a8565b600381111561098d57fe5b1480156109a3575061099f6003611420565b8112155b156108fa576108fa6003611de3565b5b5b50565b6000806000806109c56108fe565b60016002815b60045460ff1660038111156109dc57fe5b1480610a0257508060038111156109ef57fe5b60045460ff166003811115610a0057fe5b145b1515610a0d57600080fd5b33600160a060020a0381166000908152600360205260409020541515610a865760025460009011610a3d57600080fd5b600160a060020a038116600081815260036020526040908190204390557f8c41d101e4d957423a65fda82dcc88bc6b3e756166d2331f663c10166658ebb8905160405180910390a25b600854600160a060020a031663dd62ed3e333060006040516020015260405160e060020a63ffffffff8516028152600160a060020a03928316600482015291166024820152604401602060405180830381600087803b1515610ae757600080fd5b6102c65a03f11515610af857600080fd5b505050604051805190509650610b0e8734611ead565b9550610b19866110c2565b600d54909550851015610b2b57600080fd5b6000871115610bc357600854600160a060020a03166323b872dd33308a60006040516020015260405160e060020a63ffffffff8616028152600160a060020a0393841660048201529190921660248201526044810191909152606401602060405180830381600087803b1515610ba057600080fd5b6102c65a03f11515610bb157600080fd5b505050604051805190501515610bc357fe5b5b6000341115610c2257600854600160a060020a031663d0e30db0346040518263ffffffff1660e060020a0281526004016000604051808303818588803b1515610c0c57600080fd5b6125ee5a03f11515610c1d57600080fd5b505050505b610c2d856001611ec7565b600854600a54919550600160a060020a039081169163095ea7b391168860006040516020015260405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401602060405180830381600087803b1515610c9657600080fd5b6102c65a03f11515610ca757600080fd5b50505060405180515050600a54600160a060020a031663e2ab691d33888760405160e060020a63ffffffff8616028152600160a060020a03909316600484015260248301919091526044820152606401600060405180830381600087803b1515610d1057600080fd5b6102c65a03f11515610d2157600080fd5b5050600854600754600160a060020a039182169250338216917f3b52147fc495650947cc4178511501c7c8900970ae9ce3fa4c096f7ede84f7d5918a918a918a91166040519384526020840192909252604080840191909152600160a060020a0390911660608301526080909101905180910390a35b5b505b50505b50505050565b6000601082815481101515610db457fe5b906000526020600020900160005b9054906101000a9004600160a060020a031690505b919050565b600080547fac42f8beb17975ed062dcb80c63e6d203ef1c2c335ced149dc5664cc671cb7da90600160a060020a0316639085b77f338330600160e060020a0319873516876040516020015260405160e060020a63ffffffff8716028152600160a060020a039485166004820152602481019390935292166044820152600160e060020a03199091166064820152608401602060405180830381600087803b1515610e8557600080fd5b6102c65a03f11515610e9657600080fd5b505050604051805190501515610eab57600080fd5b83600160a060020a0316639085b77f847fac42f8beb17975ed062dcb80c63e6d203ef1c2c335ced149dc5664cc671cb7da60010230600035600160e060020a03191660006040516020015260405160e060020a63ffffffff8716028152600160a060020a039485166004820152602481019390935292166044820152600160e060020a03199091166064820152608401602060405180830381600087803b1515610f5457600080fd5b6102c65a03f11515610f6557600080fd5b505050604051805190501515610f7a57600080fd5b60008054600160a060020a0386811673ffffffffffffffffffffffffffffffffffffffff198316179092551691507f7d475c32583df95fccc34a6e12df24c1fc9943092cc129b6512013aecba0f136338386604051600160a060020a03938416815291831660208301529091166040808301919091526060909101905180910390a15b5b50505050565b600080600080611013856110c2565b600754909350600160a060020a031663a8c17ec28460006040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561106757600080fd5b6102c65a03f1151561107857600080fd5b50505060405180519050915061108d82611dcf565b9150508093505b505050919050565b600e545b90565b600160a060020a0381166000908152600360205260409020545b919050565b60006f0800000000000000000000000000000082106110e057600080fd5b6110ec82600e54612238565b90505b919050565b600b54600160a060020a03165b90565b60008061110f61277f565b60008060028681548110151561112157fe5b906000526020600020906003020160005b5090508060000160009054906101000a9004600160a060020a031681600101548260020188818054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156111ec5780601f106111c1576101008083540402835291602001916111ec565b820191906000526020600020905b8154815290600101906020018083116111cf57829003601f168201915b5050505050915094509450945094505b509193509193565b600061120e6108fe565b6000805b60045460ff16600381111561122357fe5b1461122d57600080fd5b600080547faef456e7c864418e1d2a40d996ca4febf3a7e317fe3af5a7ea4dda59033bbe5c91600160a060020a0390911690639085b77f90339084903090600160e060020a0319813516906040516020015260405160e060020a63ffffffff8716028152600160a060020a039485166004820152602481019390935292166044820152600160e060020a03199091166064820152608401602060405180830381600087803b15156112dd57600080fd5b6102c65a03f115156112ee57600080fd5b50505060405180519050151561130357600080fd5b87861461130f57600080fd5b87841461131b57600080fd5b600092505b8783101561138e5761138289898581811061133757fe5b90506020020135600160a060020a0316888886818110151561135557fe5b90506020020135600281111561136a57600080fd5b87878781811061137657fe5b90506020020135612256565b5b826001019250611320565b600c54600754600160a060020a0316631a3d069b6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b15156113d957600080fd5b6102c65a03f115156113ea57600080fd5b505050604051805190501115151561140157600080fd5b5b5b505b505b50505050505050565b600654600160a060020a03165b90565b6000805b82600381111561143057fe5b141561143e57506000610dd7565b60015b82600381111561144d57fe5b141561145c5750600554610dd7565b60025b82600381111561146b57fe5b141561147f57506005546206978001610dd7565b60035b82600381111561148e57fe5b1415610dd75750600554622e248001610dd7565b5b919050565b60045460ff165b90565b600c545b90565b6000806114c461277f565b600254600090819081908190116114da57600080fd5b6002805460001981019350839081106114ef57fe5b906000526020600020906003020160005b5090508060000160009054906101000a9004600160a060020a031681600101548260020184818054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156115ba5780601f1061158f576101008083540402835291602001916115ba565b820191906000526020600020905b81548152906001019060200180831161159d57829003601f168201915b5050505050915095509550955095505b505090919293565b600a54600160a060020a03165b90565b6000806115ed6108fe565b60016002815b60045460ff16600381111561160457fe5b148061162a575080600381111561161757fe5b60045460ff16600381111561162857fe5b145b151561163557600080fd5b33600160a060020a03811660009081526003602052604090205415156116ae576002546000901161166557600080fd5b600160a060020a038116600081815260036020526040908190204390557f8c41d101e4d957423a65fda82dcc88bc6b3e756166d2331f663c10166658ebb8905160405180910390a25b600954600160a060020a031663dd62ed3e333060006040516020015260405160e060020a63ffffffff8516028152600160a060020a03928316600482015291166024820152604401602060405180830381600087803b151561170f57600080fd5b6102c65a03f1151561172057600080fd5b5050506040518051600d54909650861015905061173c57600080fd5b600954600160a060020a03166323b872dd33308860006040516020015260405160e060020a63ffffffff8616028152600160a060020a0393841660048201529190921660248201526044810191909152606401602060405180830381600087803b15156117a857600080fd5b6102c65a03f115156117b957600080fd5b5050506040518051905015156117cb57fe5b6117d6856002611ec7565b600954600b54919550600160a060020a039081169163095ea7b391168760006040516020015260405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401602060405180830381600087803b151561183f57600080fd5b6102c65a03f1151561185057600080fd5b50505060405180515050600b54600160a060020a031663e2ab691d33878760405160e060020a63ffffffff8616028152600160a060020a03909316600484015260248301919091526044820152606401600060405180830381600087803b15156118b957600080fd5b6102c65a03f115156118ca57600080fd5b5050600954600754600160a060020a039182169250338216917f3b52147fc495650947cc4178511501c7c8900970ae9ce3fa4c096f7ede84f7d591899182918a91166040519384526020840192909252604080840191909152600160a060020a0390911660608301526080909101905180910390a35b5b505b50505b5050565b600754600160a060020a03165b90565b600154600160a060020a03165b90565b611972612791565b600080547fb2b321377653f655206f71514ff9f150d0822d062a5abcf220d549e1da7999f091600160a060020a0390911690639085b77f90339084903090600160e060020a0319813516906040516020015260405160e060020a63ffffffff8716028152600160a060020a039485166004820152602481019390935292166044820152600160e060020a03199091166064820152608401602060405180830381600087803b1515611a2257600080fd5b6102c65a03f11515611a3357600080fd5b505050604051805190501515611a4857600080fd5b60606040519081016040528033600160a060020a0316815260200142815260200184815250915060028054806001018281611a8391906127b7565b916000526020600020906003020160005b5083908151815473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a039190911617815560208201518160010155604082015181600201908051611ae59291602001906127e9565b505050507fe9835ee18f0f0b190604da3474d67a8f29aba2c92e90eee2bdaeca67d40d5a6b3384604051600160a060020a038316815260406020820181815290820183818151815260200191508051906020019080838360005b83811015611b585780820151818401525b602001611b3f565b50505050905090810190601f168015611b855780820380516001836020036101000a031916815260200191505b50935050505060405180910390a15b5b505050565b600054600160a060020a03165b90565b6000805481907f0542bbd0c672578966dcc525b30aa16723bb042675554ac5b0362f86b6e97dc590600160a060020a0316639085b77f338330600160e060020a0319873516876040516020015260405160e060020a63ffffffff8716028152600160a060020a039485166004820152602481019390935292166044820152600160e060020a03199091166064820152608401602060405180830381600087803b1515611c5557600080fd5b6102c65a03f11515611c6657600080fd5b505050604051805190501515611c7b57600080fd5b339250600160a060020a0384161515611cd05782600160a060020a03166108fc30600160a060020a0316319081150290604051600060405180830381858888f193505050501515611ccb57600080fd5b610d9d565b83600160a060020a03166370a082313060006040516020015260405160e060020a63ffffffff8416028152600160a060020a039091166004820152602401602060405180830381600087803b1515611d2757600080fd5b6102c65a03f11515611d3857600080fd5b5050506040518051925050600160a060020a03841663a9059cbb848460006040516020015260405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401602060405180830381600087803b1515611da057600080fd5b6102c65a03f11515611db157600080fd5b505050604051805190501515610d9d57600080fd5b5b5b5b50505050565b6000806002835b049150508082035b915091565b60045460ff16611df38183612486565b1515611dfe57600080fd5b6004805483919060ff19166001836003811115611e1757fe5b02179055507f06772bbd55fda8cf7fdb2fe4ce48d2fb499ee1bb6a9af59c5f095344700931d0818360405180836003811115611e4f57fe5b60ff168152602001826003811115611e6357fe5b60ff1681526020019250505060405180910390a1611e818183612516565b816003811115611e8d57fe5b60045460ff166003811115611e9e57fe5b146108f757600080fd5b5b5050565b600082820183811015611ebc57fe5b8091505b5092915050565b600080600080600080600080600080600c54611f58600760009054906101000a9004600160a060020a0316600160a060020a0316631a3d069b6000604051602001526040518163ffffffff1660e060020a028152600401602060405180830381600087803b1515611f3757600080fd5b6102c65a03f11515611f4857600080fd5b505050604051805190508e611ead565b1115611f6357600080fd5b600160a060020a0333166000908152600f602052604081208d9a5090985088975095508a6002811115611f9257fe5b865460ff166002811115611fa257fe5b1494508480611fc4575060025b611fb76114a8565b6003811115611fc257fe5b145b1515611fcf57600080fd5b60025b8b6002811115611fde57fe5b1480611ffd575060015b611ff06114a8565b6003811115611ffb57fe5b145b93508480156120095750835b8015612019575060008660010154115b156120b45761202c8987600101546126dc565b925061204186600201548488600101546126f6565b9650612051866001015484612715565b600187015560028601546120659088612715565b60028701556120748984612715565b9850959650869560025b8b600281111561208a57fe5b14156120a45761209c60125488612715565b6012556120b4565b6120b060115488612715565b6011555b5b600089111561213c57600754612135908990600160a060020a0316639260faf88c60006040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561211557600080fd5b6102c65a03f1151561212657600080fd5b50505060405180519050611ead565b9750600098505b61214588611dcf565b6007549193509150600160a060020a031663fb932108338360405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b15156121a057600080fd5b6102c65a03f115156121b157600080fd5b5050600754600654600160a060020a03918216925063fb93210891168460405160e060020a63ffffffff8516028152600160a060020a0390921660048301526024820152604401600060405180830381600087803b151561221157600080fd5b6102c65a03f1151561222257600080fd5b5050508099505b50505050505050505092915050565b600061224d8383670de0b6b3a76400006126f6565b90505b92915050565b6000808080600160a060020a038716151561227057600080fd5b60005b600160a060020a0388166000908152600f602052604090205460ff16600281111561229a57fe5b146122a457600080fd5b60025b8660028111156122b357fe5b14935060015b8660028111156122c557fe5b14925083806122d15750825b15156122dc57600080fd5b836122ef576122ea856110c2565b6122f1565b845b91508415806123025750600d548210155b151561230d57600080fd5b601080546001810161231f8382612868565b916000526020600020900160005b8154600160a060020a03808c166101009390930a928302920219161790555060606040519081016040528087600281111561236457fe5b8152602080820185905260006040928301819052600160a060020a038b168152600f9091522081518154829060ff191660018360028111156123a257fe5b0217905550602082015181600101556040820151600290910155506000821115610d9757600754600160a060020a0316639260faf88360006040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561241757600080fd5b6102c65a03f1151561242857600080fd5b5050506040518051600160a060020a0389166000908152600f60205260409020600201819055915050831561246b5761246360125482611ead565b601255610d97565b61247760115482611ead565b6011555b5b5b50505050505050565b6000805b83600381111561249657fe5b1480156124af575060015b8260038111156124ad57fe5b145b806124dd575060015b8360038111156124c457fe5b1480156124dd575060025b8260038111156124db57fe5b145b5b8061224d575060025b8360038111156124f357fe5b14801561224d575060035b82600381111561250a57fe5b145b5b90505b92915050565b600060015b82600381111561252757fe5b141561253257611b94565b50600060025b82600381111561254457fe5b141561255557506011805460009091555b60035b82600381111561256457fe5b141561267a5750601280546000909155600a54600160a060020a0316636cff82446040518163ffffffff1660e060020a028152600401600060405180830381600087803b15156125b357600080fd5b6102c65a03f115156125c457600080fd5b5050600b54600160a060020a03169050636cff82446040518163ffffffff1660e060020a028152600401600060405180830381600087803b151561260757600080fd5b6102c65a03f1151561261857600080fd5b5050600754600160a060020a0316905063ef7ac0e5600160405160e060020a63ffffffff84160281529015156004820152602401600060405180830381600087803b151561266557600080fd5b6102c65a03f1151561267657600080fd5b5050505b600754600160a060020a03166342966c688260405160e060020a63ffffffff84160281526004810191909152602401600060405180830381600087803b15156126c257600080fd5b6102c65a03f11515610d9a57600080fd5b5050505b505050565b60008183106126eb578161224d565b825b90505b92915050565b600061270b612705858561272c565b8361275b565b90505b9392505050565b60008282111561272157fe5b508082035b92915050565b6000828202831580612748575082848281151561274557fe5b04145b1515611ebc57fe5b8091505b5092915050565b60008161276c846002835b04611ead565b81151561277557fe5b0490505b92915050565b60206040519081016040526000815290565b606060405190810160409081526000808352602083015281016127b261277f565b905290565b815481835581811511611b9457600302816003028360005260206000209182019101611b9491906128a4565b5b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061282a57805160ff1916838001178555612857565b82800160010185558215612857579182015b8281111561285757825182559160200191906001019061283c565b5b506128649291506128f3565b5090565b815481835581811511611b9457600083815260209020611b949181019083016128f3565b5b505050565b60206040519081016040526000815290565b61072191905b8082111561286457805473ffffffffffffffffffffffffffffffffffffffff191681556000600182018190556128e36002830182612914565b506003016128aa565b5090565b90565b61072191905b8082111561286457600081556001016128f9565b5090565b90565b50805460018160011615610100020316600290046000825580601f1061293a57506108fa565b601f0160209004906000526020600020908101906108fa91906128f3565b5b505600a165627a7a72305820d8f8bdc3551e75848b45618527ebdc0e56dfcc13fe8788293d3b70d0ab088a010029
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000ae38c27e646959735ec70d77ed4ecc03a3eff4900000000000000000000000004c688949578b4fd0cb8f8993a0ec608659e05cf1000000000000000000000000000000000000000000000000000000005a0829b0000000000000000000000000a826813d0eb5d629e959c02b8f7a3d0f53066ce4000000000000000000000000a823e6722006afe99e91c30ff5295052fe6b8e32000000000000000000000000b59a226a2b8a2f2b0512baa35cc348b6b213b6710000000000000000000000002682762d9830ace22218c7bd51d19e5757ec3f03000000000000000000000000b1e4675f0dbe360ba90447a7e58c62c762ad62d40000000000000000000000005cb5d4a198a253e21212c72c27b51558d19e3a57000000000000000000000000000000000000000000a56fa5b99019a5c800000000000000000000000000000000000000000000000000000fb88ef7839f48000000000000000000000000000000000000000000000000000fb88ef7839f480000
-----Decoded View---------------
Arg [0] : accessPolicy (address): 0xaE38c27E646959735ec70d77ED4eCc03A3EFf490
Arg [1] : forkArbiter (address): 0x4C688949578B4fD0CB8F8993a0Ec608659e05cf1
Arg [2] : startDate (int256): 1510484400
Arg [3] : platformWallet (address): 0xA826813D0eb5D629E959c02b8f7a3d0f53066Ce4
Arg [4] : neumark (address): 0xA823E6722006afe99E91c30FF5295052fe6b8E32
Arg [5] : etherToken (address): 0xB59A226a2b8A2F2b0512bAA35CC348b6b213b671
Arg [6] : euroToken (address): 0x2682762d9830ACE22218C7bd51D19e5757Ec3f03
Arg [7] : etherLock (address): 0xb1E4675f0dBE360bA90447A7e58c62C762Ad62D4
Arg [8] : euroLock (address): 0x5cB5d4A198a253e21212c72C27B51558d19e3a57
Arg [9] : capEurUlps (uint256): 200000000000000000000000000
Arg [10] : minTicketEurUlps (uint256): 290000000000000000000
Arg [11] : ethEurFraction (uint256): 290000000000000000000
-----Encoded View---------------
12 Constructor Arguments found :
Arg [0] : 000000000000000000000000ae38c27e646959735ec70d77ed4ecc03a3eff490
Arg [1] : 0000000000000000000000004c688949578b4fd0cb8f8993a0ec608659e05cf1
Arg [2] : 000000000000000000000000000000000000000000000000000000005a0829b0
Arg [3] : 000000000000000000000000a826813d0eb5d629e959c02b8f7a3d0f53066ce4
Arg [4] : 000000000000000000000000a823e6722006afe99e91c30ff5295052fe6b8e32
Arg [5] : 000000000000000000000000b59a226a2b8a2f2b0512baa35cc348b6b213b671
Arg [6] : 0000000000000000000000002682762d9830ace22218c7bd51d19e5757ec3f03
Arg [7] : 000000000000000000000000b1e4675f0dbe360ba90447a7e58c62c762ad62d4
Arg [8] : 0000000000000000000000005cb5d4a198a253e21212c72c27b51558d19e3a57
Arg [9] : 000000000000000000000000000000000000000000a56fa5b99019a5c8000000
Arg [10] : 00000000000000000000000000000000000000000000000fb88ef7839f480000
Arg [11] : 00000000000000000000000000000000000000000000000fb88ef7839f480000
Swarm Source
bzzr://d8f8bdc3551e75848b45618527ebdc0e56dfcc13fe8788293d3b70d0ab088a01
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.